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] shallow: .parents: ensure that one .find call does not affect another #1781

Merged
merged 5 commits into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3893,6 +3893,53 @@ describeWithDOM('mount', () => {
const formUp = input.parents('form');
expect(formUp).to.have.lengthOf(1);
});

it('works when called sequentially on two sibling nodes', () => {
class Test extends React.Component {
render() {
return (
<div>
<div className="a">
<div>A child</div>
</div>
<div className="b">
<div>B child</div>
</div>
</div>
);
}
}

const wrapper = mount(<Test />);

const aChild = wrapper.find({ children: 'A child' });
expect(aChild.debug()).to.equal(`<div>
A child
</div>`);
expect(aChild).to.have.lengthOf(1);

const bChild = wrapper.find({ children: 'B child' });
expect(bChild.debug()).to.equal(`<div>
B child
</div>`);
expect(bChild).to.have.lengthOf(1);

const bChildParents = bChild.parents('.b');
expect(bChildParents.debug()).to.equal(`<div className="b">
<div>
B child
</div>
</div>`);
expect(bChildParents).to.have.lengthOf(1);

const aChildParents = aChild.parents('.a');
expect(aChildParents.debug()).to.equal(`<div className="a">
<div>
A child
</div>
</div>`);
expect(aChildParents).to.have.lengthOf(1);
});
});

describe('.parent()', () => {
Expand Down
47 changes: 47 additions & 0 deletions packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3556,6 +3556,53 @@ describe('shallow', () => {
expect(parents.at(0).hasClass('foo')).to.equal(true);
expect(parents.at(1).hasClass('bax')).to.equal(true);
});

it('works when called sequentially on two sibling nodes', () => {
class Test extends React.Component {
render() {
return (
<div>
<div className="a">
<div>A child</div>
</div>
<div className="b">
<div>B child</div>
</div>
</div>
);
}
}

const wrapper = shallow(<Test />);

const aChild = wrapper.find({ children: 'A child' });
expect(aChild.debug()).to.equal(`<div>
A child
</div>`);
expect(aChild).to.have.lengthOf(1);

const bChild = wrapper.find({ children: 'B child' });
expect(bChild.debug()).to.equal(`<div>
B child
</div>`);
expect(bChild).to.have.lengthOf(1);

const bChildParents = bChild.parents('.b');
expect(bChildParents.debug()).to.equal(`<div className="b">
<div>
B child
</div>
</div>`);
expect(bChildParents).to.have.lengthOf(1);

const aChildParents = aChild.parents('.a');
expect(aChildParents.debug()).to.equal(`<div className="a">
<div>
A child
</div>
</div>`);
expect(aChildParents).to.have.lengthOf(1);
});
});

describe('.parent()', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/enzyme/src/RSTTraversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function pathToNode(node, root) {
}

export function parentsOfNode(node, root) {
return pathToNode(node, root).reverse();
return (pathToNode(node, root) || []).reverse();
}

export function nodeHasId(node, id) {
Expand Down
22 changes: 18 additions & 4 deletions packages/enzyme/src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const RENDERER = sym('__renderer__');
const UNRENDERED = sym('__unrendered__');
const ROOT = sym('__root__');
const OPTIONS = sym('__options__');
const ROOT_NODES = sym('__rootNodes__');

/**
* Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
Expand All @@ -57,8 +58,18 @@ function filterWhereUnwrapped(wrapper, predicate) {
return wrapper.wrap(wrapper.getNodesInternal().filter(predicate).filter(Boolean));
}

function getRootNodeInternal(wrapper) {
if (wrapper[ROOT].length !== 1) {
throw new Error('getRootNodeInternal(wrapper) can only be called when wrapper wraps one node');
}
if (wrapper[ROOT] !== wrapper) {
return wrapper[ROOT_NODES][0];
}
return wrapper[ROOT][NODE];
}

function nodeParents(wrapper, node) {
return parentsOfNode(node, wrapper[ROOT].getNodeInternal());
return parentsOfNode(node, getRootNodeInternal(wrapper));
}

function privateSetNodes(wrapper, nodes) {
Expand Down Expand Up @@ -102,6 +113,7 @@ class ReactWrapper {
privateSet(this, RENDERER, root[RENDERER]);
privateSet(this, ROOT, root);
privateSetNodes(this, nodes);
privateSet(this, ROOT_NODES, root[NODES]);
}
privateSet(this, OPTIONS, root ? root[OPTIONS] : options);
}
Expand Down Expand Up @@ -639,7 +651,7 @@ class ReactWrapper {
throw new TypeError('your adapter does not support `simulateError`. Try upgrading it!');
}

const rootNode = this[ROOT].getNodeInternal();
const rootNode = getRootNodeInternal(this);
const nodeHierarchy = [thisNode].concat(nodeParents(this, thisNode));
renderer.simulateError(nodeHierarchy, rootNode, error);

Expand Down Expand Up @@ -737,8 +749,10 @@ class ReactWrapper {
* @returns {ReactWrapper}
*/
parents(selector) {
const allParents = this.wrap(this.single('parents', n => nodeParents(this, n)));
return selector ? allParents.filter(selector) : allParents;
return this.single('parents', (n) => {
const allParents = this.wrap(nodeParents(this, n));
return selector ? allParents.filter(selector) : allParents;
});
}

/**
Expand Down
15 changes: 13 additions & 2 deletions packages/enzyme/src/ShallowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const UNRENDERED = sym('__unrendered__');
const ROOT = sym('__root__');
const OPTIONS = sym('__options__');
const SET_STATE = sym('__setState__');
const ROOT_NODES = sym('__rootNodes__');

/**
* Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
* function.
Expand Down Expand Up @@ -144,6 +146,12 @@ function getRootNode(node) {
}

function getRootNodeInternal(wrapper) {
if (wrapper[ROOT].length !== 1) {
throw new Error('getRootNodeInternal(wrapper) can only be called when wrapper wraps one node');
}
if (wrapper[ROOT] !== wrapper) {
return wrapper[ROOT_NODES][0];
}
return wrapper[ROOT][NODE];
}

Expand Down Expand Up @@ -219,6 +227,7 @@ class ShallowWrapper {
privateSet(this, RENDERER, root[RENDERER]);
privateSetNodes(this, nodes);
privateSet(this, OPTIONS, root[OPTIONS]);
privateSet(this, ROOT_NODES, root[NODES]);
}
}

Expand Down Expand Up @@ -972,8 +981,10 @@ class ShallowWrapper {
* @returns {ShallowWrapper}
*/
parents(selector) {
const allParents = this.wrap(this.single('parents', n => nodeParents(this, n)));
return selector ? allParents.filter(selector) : allParents;
return this.single('parents', (n) => {
const allParents = this.wrap(nodeParents(this, n));
return selector ? allParents.filter(selector) : allParents;
});
}

/**
Expand Down