Skip to content

Commit d7f63b9

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

File tree

3 files changed

+199
-8
lines changed

3 files changed

+199
-8
lines changed

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

Lines changed: 101 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,50 @@ 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 = { a: 'a' };
3663+
}
3664+
3665+
render() {
3666+
const { _ } = this.props;
3667+
const { a } = this.state;
3668+
return (
3669+
<div>
3670+
{_}
3671+
{a}
3672+
</div>
3673+
);
3674+
}
3675+
}
3676+
3677+
class Parent extends React.Component {
3678+
constructor(...args) {
3679+
super(...args);
3680+
this.state = { _: 1 };
3681+
}
3682+
3683+
render() {
3684+
const { _ } = this.state;
3685+
return <Child _={_} />;
3686+
}
3687+
}
3688+
3689+
it('gets the state of the parent', () => {
3690+
const wrapper = mount(<Parent />);
3691+
3692+
expect(wrapper.state()).to.eql({ _: 1 });
3693+
});
3694+
3695+
it('gets the state of the child', () => {
3696+
const wrapper = mount(<Parent />);
3697+
3698+
expect(wrapper.find(Child).state()).to.eql({ a: 'a' });
3699+
});
3700+
});
36003701
});
36013702

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

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,59 @@ 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 = { a: 'a' };
2747+
}
2748+
2749+
render() {
2750+
const { _ } = this.props;
2751+
const { a } = this.state;
2752+
return (
2753+
<div>
2754+
{_}
2755+
{a}
2756+
</div>
2757+
);
2758+
}
2759+
}
2760+
2761+
class Parent extends React.Component {
2762+
constructor(...args) {
2763+
super(...args);
2764+
this.state = { _: 1 };
2765+
}
2766+
2767+
render() {
2768+
const { _ } = this.state;
2769+
return <Child _={_} />;
2770+
}
2771+
}
2772+
2773+
it('sets the state of the parent', () => {
2774+
const wrapper = shallow(<Parent />);
2775+
2776+
expect(wrapper.debug()).to.eql('<Child _={1} />');
2777+
2778+
return new Promise((resolve) => {
2779+
wrapper.setState({ _: 2 }, () => {
2780+
expect(wrapper.debug()).to.eql('<Child _={2} />');
2781+
resolve();
2782+
});
2783+
});
2784+
});
2785+
2786+
it('can not set the state of the child', () => {
2787+
const wrapper = shallow(<Parent />);
2788+
2789+
expect(wrapper.debug()).to.eql('<Child _={1} />');
2790+
2791+
expect(() => wrapper.find(Child).setState({ a: 'b' })).to.throw(Error, 'ShallowWrapper::setState() can only be called on the root');
2792+
});
2793+
});
27412794
});
27422795

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

32963392
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)