Skip to content

Commit

Permalink
[Fix] mount: setState: allow setting state on a class child of an…
Browse files Browse the repository at this point in the history
… SFC root

[enzyme-adapter-utils] [fix] stop checking `rootNode`

Fixes #1961.
  • Loading branch information
ljharb committed Jan 3, 2019
1 parent e8fd525 commit 30ece7d
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 9 deletions.
7 changes: 1 addition & 6 deletions packages/enzyme-adapter-utils/src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,11 @@ function getComponentStack(
export function simulateError(
error,
catchingInstance,
rootNode,
rootNode, // TODO: remove `rootNode` next semver-major
hierarchy,
getNodeType = nodeTypeFromType,
getDisplayName = displayNameOfNode,
) {
const nodeType = getNodeType(rootNode.type);
if (nodeType !== 'class') {
throw new TypeError('simulateError() can only be called on class components with an instance');
}

const { componentDidCatch } = catchingInstance || {};
if (!componentDidCatch) {
throw error;
Expand Down
60 changes: 59 additions & 1 deletion packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3163,6 +3163,23 @@ describeWithDOM('mount', () => {
});
});
});

itIf(is('> 0.13'), 'sets the state of a class child with a root SFC', () => {
function SFC(props) {
return <Parent {...props} />;
}

const wrapper = mount(<SFC />);

expect(wrapper.text().trim()).to.eql('1 - a');

return new Promise((resolve) => {
wrapper.find(Child).setState({ state: 'b' }, () => {
expect(wrapper.text().trim()).to.eql('1 - b');
resolve();
});
});
});
});
});

Expand Down Expand Up @@ -5447,6 +5464,10 @@ describeWithDOM('mount', () => {
}
}

function ErrorSFC(props) {
return <ErrorBoundary {...props} />;
}

describe('Thrower', () => {
it('does not throw when `throws` is `false`', () => {
expect(() => mount(<Thrower throws={false} />)).not.to.throw();
Expand Down Expand Up @@ -5499,6 +5520,18 @@ describeWithDOM('mount', () => {
expect(wrapper.find({ children: 'HasNotThrown' })).to.have.lengthOf(0);
});

it('rerenders on a simulated error with an SFC root', () => {
const wrapper = mount(<ErrorSFC spy={sinon.stub()} />);

expect(wrapper.find({ children: 'HasThrown' })).to.have.lengthOf(0);
expect(wrapper.find({ children: 'HasNotThrown' })).to.have.lengthOf(1);

expect(() => wrapper.find(Thrower).simulateError(errorToThrow)).not.to.throw();

expect(wrapper.find({ children: 'HasThrown' })).to.have.lengthOf(1);
expect(wrapper.find({ children: 'HasNotThrown' })).to.have.lengthOf(0);
});

it('catches errors during render', () => {
const spy = sinon.spy();
const wrapper = mount(<ErrorBoundary spy={spy} />);
Expand All @@ -5519,6 +5552,31 @@ describeWithDOM('mount', () => {
in main (created by ErrorBoundary)`}
in div (created by ErrorBoundary)
in ErrorBoundary (created by WrapperComponent)
in WrapperComponent`,
});
});

it('works when the root is an SFC', () => {
const spy = sinon.spy();
const wrapper = mount(<ErrorSFC spy={spy} />);

expect(spy).to.have.property('callCount', 0);

wrapper.find(ErrorBoundary).setState({ throws: true });

expect(spy).to.have.property('callCount', 1);

expect(spy.args).to.be.an('array').and.have.lengthOf(1);
const [[actualError, info]] = spy.args;
expect(actualError).to.satisfy(properErrorMessage);
expect(info).to.deep.equal({
componentStack: `
in Thrower (created by ErrorBoundary)
in span (created by ErrorBoundary)${hasFragments ? '' : `
in main (created by ErrorBoundary)`}
in div (created by ErrorBoundary)
in ErrorBoundary (created by ErrorSFC)
in ErrorSFC (created by WrapperComponent)
in WrapperComponent`,
});
});
Expand Down Expand Up @@ -7717,7 +7775,7 @@ describeWithDOM('mount', () => {
const mappedChildren = [];
React.Children.forEach(children, (child, i) => {
const clonedChild = React.cloneElement(child, {
key: i,
key: i, // eslint-disable-line react/no-array-index-key
onClick() {
return child.props.name;
},
Expand Down
2 changes: 1 addition & 1 deletion packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7639,7 +7639,7 @@ describe('shallow', () => {
const mappedChildren = [];
React.Children.forEach(children, (child, i) => {
const clonedChild = React.cloneElement(child, {
key: i,
key: i, // eslint-disable-line react/no-array-index-key
onClick() {
return child.props.name;
},
Expand Down
2 changes: 1 addition & 1 deletion packages/enzyme/src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ class ReactWrapper {
* @returns {ReactWrapper}
*/
setState(state, callback = undefined) {
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
if (this.instance() === null || this.getNodeInternal().nodeType !== 'class') {
throw new Error('ReactWrapper::setState() can only be called on class components');
}
if (arguments.length > 1 && typeof callback !== 'function') {
Expand Down

0 comments on commit 30ece7d

Please sign in to comment.