Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): stop mutating Context's input #3076

Merged
merged 12 commits into from
Jul 15, 2021
4 changes: 3 additions & 1 deletion lib/core/base/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
select,
isNodeInContext,
nodeSorter,
respondable
respondable,
clone
} from '../utils';

/**
Expand Down Expand Up @@ -230,6 +231,7 @@ function getRootNode({ include, exclude }) {
* @param {Object} spec Configuration or "specification" object
*/
export default function Context(spec, flatTree) {
spec = clone(spec);
this.frames = [];
this.page = typeof spec?.page === 'boolean' ? spec.page : undefined;
this.initiator = typeof spec?.initiator === 'boolean' ? spec.initiator : true;
Expand Down
4 changes: 4 additions & 0 deletions lib/core/utils/clone.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ function clone(obj) {
var index,
length,
out = obj;
// DOM nodes cannot be cloned.
if (window?.Node && obj instanceof window.Node) {
clottman marked this conversation as resolved.
Show resolved Hide resolved
return obj;
}

if (obj !== null && typeof obj === 'object') {
if (Array.isArray(obj)) {
Expand Down
42 changes: 42 additions & 0 deletions test/core/base/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,48 @@ describe('Context', function() {
fixture.innerHTML = '';
});

it('should not mutate exclude in input', function() {
fixture.innerHTML = '<div id="foo"></div>';
var context = { exclude: [['iframe', '#foo']] };
// eslint-disable-next-line no-new
new Context(context);
assert.deepEqual(context, { exclude: [['iframe', '#foo']] });
});

it('should not mutate its include input', function() {
fixture.innerHTML = '<div id="foo"></div>';
var context = { include: [['#foo']] };
// eslint-disable-next-line no-new
new Context(context);
assert.deepEqual(context, { include: [['#foo']] });
});

it('should not share memory with complex object', function() {
fixture.innerHTML = '<div id="foo"><a href="">Click me</a></div>';
var spec = {
include: [['#foo'], ['a']],
exclude: [['iframe', '#foo2']],
size: { width: 100, height: 100 }
};
var context = new Context(spec);
assert.notStrictEqual(spec.include, context.include);
spec.include.forEach(function(_, index) {
assert.notStrictEqual(spec.include[index], context.include[index]);
});
assert.notStrictEqual(spec.exclude, context.exclude);
spec.exclude.forEach(function(_, index) {
assert.notStrictEqual(spec.exclude[index], context.exclude[index]);
});
assert.notStrictEqual(spec.size, context.size);
});

it('should not share memory with simple array', function() {
fixture.innerHTML = '<div id="foo"></div>';
var spec = ['#foo'];
var context = new Context(spec);
assert.notStrictEqual(spec, context.include);
});

describe('include', function() {
it('should accept a single selector', function() {
fixture.innerHTML = '<div id="foo"></div>';
Expand Down