diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
index fd4f8c5e9..290a3e021 100644
--- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
+++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
@@ -3045,6 +3045,63 @@ describeWithDOM('mount', () => {
expect(mount().debug()).to.equal('');
});
+
+ describe('child components', () => {
+ class Child extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { state: 'a' };
+ }
+
+ render() {
+ const { prop } = this.props;
+ const { state } = this.state;
+ return (
+
+ {prop} - {state}
+
+ );
+ }
+ }
+
+ class Parent extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { childProp: 1 };
+ }
+
+ render() {
+ const { childProp } = this.state;
+ return ;
+ }
+ }
+
+ it('sets the state of the parent', () => {
+ const wrapper = mount();
+
+ expect(wrapper.text().trim()).to.eql('1 - a');
+
+ return new Promise((resolve) => {
+ wrapper.setState({ childProp: 2 }, () => {
+ expect(wrapper.text().trim()).to.eql('2 - a');
+ resolve();
+ });
+ });
+ });
+
+ it('sets the state of the child', () => {
+ const wrapper = mount();
+
+ 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();
+ });
+ });
+ });
+ });
});
describe('.is(selector)', () => {
@@ -3597,6 +3654,50 @@ describeWithDOM('mount', () => {
expect(() => wrapper.state()).to.throw(Error, 'ReactWrapper::state() can only be called on class components');
});
});
+
+ describe('child components', () => {
+ class Child extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { a: 'a' };
+ }
+
+ render() {
+ const { _ } = this.props;
+ const { a } = this.state;
+ return (
+
+ {_}
+ {a}
+
+ );
+ }
+ }
+
+ class Parent extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { _: 1 };
+ }
+
+ render() {
+ const { _ } = this.state;
+ return ;
+ }
+ }
+
+ it('gets the state of the parent', () => {
+ const wrapper = mount();
+
+ expect(wrapper.state()).to.eql({ _: 1 });
+ });
+
+ it('gets the state of the child', () => {
+ const wrapper = mount();
+
+ expect(wrapper.find(Child).state()).to.eql({ a: 'a' });
+ });
+ });
});
describe('.children([selector])', () => {
diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
index 13ce6c5b1..238ec3357 100644
--- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
+++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
@@ -2738,6 +2738,59 @@ describe('shallow', () => {
expect(shallow().debug()).to.equal('');
});
+
+ describe('child components', () => {
+ class Child extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { a: 'a' };
+ }
+
+ render() {
+ const { _ } = this.props;
+ const { a } = this.state;
+ return (
+
+ {_}
+ {a}
+
+ );
+ }
+ }
+
+ class Parent extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { _: 1 };
+ }
+
+ render() {
+ const { _ } = this.state;
+ return ;
+ }
+ }
+
+ it('sets the state of the parent', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.debug()).to.eql('');
+
+ return new Promise((resolve) => {
+ wrapper.setState({ _: 2 }, () => {
+ expect(wrapper.debug()).to.eql('');
+ resolve();
+ });
+ });
+ });
+
+ it('can not set the state of the child', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.debug()).to.eql('');
+
+ expect(() => wrapper.find(Child).setState({ a: 'b' })).to.throw(Error, 'ShallowWrapper::setState() can only be called on the root');
+ });
+ });
});
describe('.is(selector)', () => {
@@ -3291,6 +3344,49 @@ describe('shallow', () => {
expect(() => wrapper.state()).to.throw(Error, 'ShallowWrapper::state() can only be called on class components');
});
});
+
+ describe('child components', () => {
+ class Child extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { state: 'a' };
+ }
+
+ render() {
+ const { prop } = this.props;
+ const { state } = this.state;
+ return (
+
+ {prop} - {state}
+
+ );
+ }
+ }
+
+ class Parent extends React.Component {
+ constructor(...args) {
+ super(...args);
+ this.state = { childProp: 1 };
+ }
+
+ render() {
+ const { childProp } = this.state;
+ return ;
+ }
+ }
+
+ it('gets the state of the parent', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.state()).to.eql({ childProp: 1 });
+ });
+
+ it('can not get the state of the child', () => {
+ const wrapper = shallow();
+
+ expect(() => wrapper.find(Child).state()).to.throw(Error, 'ShallowWrapper::state() can only be called on the root');
+ });
+ });
});
describe('.children([selector])', () => {
diff --git a/packages/enzyme/src/ReactWrapper.js b/packages/enzyme/src/ReactWrapper.js
index e3d577334..cc517fdd9 100644
--- a/packages/enzyme/src/ReactWrapper.js
+++ b/packages/enzyme/src/ReactWrapper.js
@@ -217,13 +217,13 @@ class ReactWrapper {
* Forces a re-render. Useful to run before checking the render output if something external
* may be updating the state of the component somewhere.
*
- * NOTE: can only be called on a wrapper instance that is also the root instance.
+ * NOTE: no matter what instance this is called on, it will always update the root.
*
* @returns {ReactWrapper}
*/
update() {
if (this[ROOT] !== this) {
- throw new Error('ReactWrapper::update() can only be called on the root');
+ return this[ROOT].update();
}
privateSetNodes(this, this[RENDERER].getNode());
return this;
@@ -307,9 +307,6 @@ class ReactWrapper {
* @returns {ReactWrapper}
*/
setState(state, callback = undefined) {
- if (this[ROOT] !== this) {
- throw new Error('ReactWrapper::setState() can only be called on the root');
- }
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
throw new Error('ReactWrapper::setState() can only be called on class components');
}
@@ -668,9 +665,6 @@ class ReactWrapper {
* @returns {*}
*/
state(name) {
- if (this[ROOT] !== this) {
- throw new Error('ReactWrapper::state() can only be called on the root');
- }
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
throw new Error('ReactWrapper::state() can only be called on class components');
}