Skip to content

Commit 44a9e54

Browse files
committed
[New] mount: .state()/.setState(): allow calling on children.
Fixes #635. Fixes #1289.
1 parent c6632de commit 44a9e54

File tree

3 files changed

+200
-8
lines changed

3 files changed

+200
-8
lines changed

packages/enzyme-test-suite/test/ReactWrapper-spec.jsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,6 +3045,63 @@ describeWithDOM('mount', () => {
30453045

30463046
expect(mount(<Comp />).debug()).to.equal('<Comp />');
30473047
});
3048+
3049+
describe('child components', () => {
3050+
class Child extends React.Component {
3051+
constructor(...args) {
3052+
super(...args);
3053+
this.state = { state: 'a' };
3054+
}
3055+
3056+
render() {
3057+
const { prop } = this.props;
3058+
const { state } = this.state;
3059+
return (
3060+
<div>
3061+
{prop} - {state}
3062+
</div>
3063+
);
3064+
}
3065+
}
3066+
3067+
class Parent extends React.Component {
3068+
constructor(...args) {
3069+
super(...args);
3070+
this.state = { childProp: 1 };
3071+
}
3072+
3073+
render() {
3074+
const { childProp } = this.state;
3075+
return <Child prop={childProp} />;
3076+
}
3077+
}
3078+
3079+
it('sets the state of the parent', () => {
3080+
const wrapper = mount(<Parent />);
3081+
3082+
expect(wrapper.text().trim()).to.eql('1 - a');
3083+
3084+
return new Promise((resolve) => {
3085+
wrapper.setState({ childProp: 2 }, () => {
3086+
expect(wrapper.text().trim()).to.eql('2 - a');
3087+
resolve();
3088+
});
3089+
});
3090+
});
3091+
3092+
it('sets the state of the child', () => {
3093+
const wrapper = mount(<Parent />);
3094+
3095+
expect(wrapper.text().trim()).to.eql('1 - a');
3096+
3097+
return new Promise((resolve) => {
3098+
wrapper.find(Child).setState({ state: 'b' }, () => {
3099+
expect(wrapper.text().trim()).to.eql('1 - b');
3100+
resolve();
3101+
});
3102+
});
3103+
});
3104+
});
30483105
});
30493106

30503107
describe('.is(selector)', () => {
@@ -3597,6 +3654,49 @@ describeWithDOM('mount', () => {
35973654
expect(() => wrapper.state()).to.throw(Error, 'ReactWrapper::state() can only be called on class components');
35983655
});
35993656
});
3657+
3658+
describe('child components', () => {
3659+
class Child extends React.Component {
3660+
constructor(...args) {
3661+
super(...args);
3662+
this.state = { state: 'a' };
3663+
}
3664+
3665+
render() {
3666+
const { prop } = this.props;
3667+
const { state } = this.state;
3668+
return (
3669+
<div>
3670+
{prop} - {state}
3671+
</div>
3672+
);
3673+
}
3674+
}
3675+
3676+
class Parent extends React.Component {
3677+
constructor(...args) {
3678+
super(...args);
3679+
this.state = { childProp: 1 };
3680+
}
3681+
3682+
render() {
3683+
const { childProp } = this.state;
3684+
return <Child prop={childProp} />;
3685+
}
3686+
}
3687+
3688+
it('gets the state of the parent', () => {
3689+
const wrapper = mount(<Parent />);
3690+
3691+
expect(wrapper.state()).to.eql({ childProp: 1 });
3692+
});
3693+
3694+
it('gets the state of the child', () => {
3695+
const wrapper = mount(<Parent />);
3696+
3697+
expect(wrapper.find(Child).state()).to.eql({ state: 'a' });
3698+
});
3699+
});
36003700
});
36013701

36023702
describe('.children([selector])', () => {

packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,61 @@ describe('shallow', () => {
27382738

27392739
expect(shallow(<Comp />).debug()).to.equal('');
27402740
});
2741+
2742+
describe('child components', () => {
2743+
class Child extends React.Component {
2744+
constructor(...args) {
2745+
super(...args);
2746+
this.state = { state: 'a' };
2747+
}
2748+
2749+
render() {
2750+
const { prop } = this.props;
2751+
const { state } = this.state;
2752+
return (
2753+
<div>
2754+
{prop} - {state}
2755+
</div>
2756+
);
2757+
}
2758+
}
2759+
2760+
class Parent extends React.Component {
2761+
constructor(...args) {
2762+
super(...args);
2763+
this.state = { childProp: 1 };
2764+
}
2765+
2766+
render() {
2767+
const { childProp } = this.state;
2768+
return <Child prop={childProp} />;
2769+
}
2770+
}
2771+
2772+
it('sets the state of the parent', () => {
2773+
const wrapper = shallow(<Parent />);
2774+
2775+
expect(wrapper.debug()).to.eql('<Child prop={1} />');
2776+
2777+
return new Promise((resolve) => {
2778+
wrapper.setState({ childProp: 2 }, () => {
2779+
expect(wrapper.debug()).to.eql('<Child prop={2} />');
2780+
resolve();
2781+
});
2782+
});
2783+
});
2784+
2785+
it('can not set the state of the child', () => {
2786+
const wrapper = shallow(<Parent />);
2787+
2788+
expect(wrapper.debug()).to.eql('<Child prop={1} />');
2789+
2790+
expect(() => wrapper.find(Child).setState({ state: 'b' })).to.throw(
2791+
Error,
2792+
'ShallowWrapper::setState() can only be called on the root',
2793+
);
2794+
});
2795+
});
27412796
});
27422797

27432798
describe('.is(selector)', () => {
@@ -3291,6 +3346,49 @@ describe('shallow', () => {
32913346
expect(() => wrapper.state()).to.throw(Error, 'ShallowWrapper::state() can only be called on class components');
32923347
});
32933348
});
3349+
3350+
describe('child components', () => {
3351+
class Child extends React.Component {
3352+
constructor(...args) {
3353+
super(...args);
3354+
this.state = { state: 'a' };
3355+
}
3356+
3357+
render() {
3358+
const { prop } = this.props;
3359+
const { state } = this.state;
3360+
return (
3361+
<div>
3362+
{prop} - {state}
3363+
</div>
3364+
);
3365+
}
3366+
}
3367+
3368+
class Parent extends React.Component {
3369+
constructor(...args) {
3370+
super(...args);
3371+
this.state = { childProp: 1 };
3372+
}
3373+
3374+
render() {
3375+
const { childProp } = this.state;
3376+
return <Child prop={childProp} />;
3377+
}
3378+
}
3379+
3380+
it('gets the state of the parent', () => {
3381+
const wrapper = shallow(<Parent />);
3382+
3383+
expect(wrapper.state()).to.eql({ childProp: 1 });
3384+
});
3385+
3386+
it('can not get the state of the child', () => {
3387+
const wrapper = shallow(<Parent />);
3388+
3389+
expect(() => wrapper.find(Child).state()).to.throw(Error, 'ShallowWrapper::state() can only be called on the root');
3390+
});
3391+
});
32943392
});
32953393

32963394
describe('.children([selector])', () => {

packages/enzyme/src/ReactWrapper.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,13 @@ class ReactWrapper {
217217
* Forces a re-render. Useful to run before checking the render output if something external
218218
* may be updating the state of the component somewhere.
219219
*
220-
* NOTE: can only be called on a wrapper instance that is also the root instance.
220+
* NOTE: no matter what instance this is called on, it will always update the root.
221221
*
222222
* @returns {ReactWrapper}
223223
*/
224224
update() {
225225
if (this[ROOT] !== this) {
226-
throw new Error('ReactWrapper::update() can only be called on the root');
226+
return this[ROOT].update();
227227
}
228228
privateSetNodes(this, this[RENDERER].getNode());
229229
return this;
@@ -307,9 +307,6 @@ class ReactWrapper {
307307
* @returns {ReactWrapper}
308308
*/
309309
setState(state, callback = undefined) {
310-
if (this[ROOT] !== this) {
311-
throw new Error('ReactWrapper::setState() can only be called on the root');
312-
}
313310
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
314311
throw new Error('ReactWrapper::setState() can only be called on class components');
315312
}
@@ -668,9 +665,6 @@ class ReactWrapper {
668665
* @returns {*}
669666
*/
670667
state(name) {
671-
if (this[ROOT] !== this) {
672-
throw new Error('ReactWrapper::state() can only be called on the root');
673-
}
674668
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
675669
throw new Error('ReactWrapper::state() can only be called on class components');
676670
}

0 commit comments

Comments
 (0)