Skip to content

Commit

Permalink
[New] mount: .state()/.setState(): allow calling on children.
Browse files Browse the repository at this point in the history
Fixes #635. Fixes #1289.
  • Loading branch information
ljharb committed Sep 3, 2018
1 parent 6b63db3 commit 6d1a498
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 9 deletions.
100 changes: 100 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3045,6 +3045,63 @@ describeWithDOM('mount', () => {

expect(mount(<Comp />).debug()).to.equal('<Comp />');
});

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 (
<div>
{prop} - {state}
</div>
);
}
}

class Parent extends React.Component {
constructor(...args) {
super(...args);
this.state = { childProp: 1 };
}

render() {
const { childProp } = this.state;
return <Child prop={childProp} />;
}
}

it('sets the state of the parent', () => {
const wrapper = mount(<Parent />);

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(<Parent />);

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)', () => {
Expand Down Expand Up @@ -3597,6 +3654,49 @@ 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 = { state: 'a' };
}

render() {
const { prop } = this.props;
const { state } = this.state;
return (
<div>
{prop} - {state}
</div>
);
}
}

class Parent extends React.Component {
constructor(...args) {
super(...args);
this.state = { childProp: 1 };
}

render() {
const { childProp } = this.state;
return <Child prop={childProp} />;
}
}

it('gets the state of the parent', () => {
const wrapper = mount(<Parent />);

expect(wrapper.state()).to.eql({ childProp: 1 });
});

it('gets the state of the child', () => {
const wrapper = mount(<Parent />);

expect(wrapper.find(Child).state()).to.eql({ state: 'a' });
});
});
});

describe('.children([selector])', () => {
Expand Down
98 changes: 98 additions & 0 deletions packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2738,6 +2738,61 @@ describe('shallow', () => {

expect(shallow(<Comp />).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 (
<div>
{prop} - {state}
</div>
);
}
}

class Parent extends React.Component {
constructor(...args) {
super(...args);
this.state = { childProp: 1 };
}

render() {
const { childProp } = this.state;
return <Child prop={childProp} />;
}
}

it('sets the state of the parent', () => {
const wrapper = shallow(<Parent />);

expect(wrapper.debug()).to.eql('<Child prop={1} />');

return new Promise((resolve) => {
wrapper.setState({ childProp: 2 }, () => {
expect(wrapper.debug()).to.eql('<Child prop={2} />');
resolve();
});
});
});

it('can not set the state of the child', () => {
const wrapper = shallow(<Parent />);

expect(wrapper.debug()).to.eql('<Child prop={1} />');

expect(() => wrapper.find(Child).setState({ state: 'b' })).to.throw(
Error,
'ShallowWrapper::setState() can only be called on the root',
);
});
});
});

describe('.is(selector)', () => {
Expand Down Expand Up @@ -3291,6 +3346,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 (
<div>
{prop} - {state}
</div>
);
}
}

class Parent extends React.Component {
constructor(...args) {
super(...args);
this.state = { childProp: 1 };
}

render() {
const { childProp } = this.state;
return <Child prop={childProp} />;
}
}

it('gets the state of the parent', () => {
const wrapper = shallow(<Parent />);

expect(wrapper.state()).to.eql({ childProp: 1 });
});

it('can not get the state of the child', () => {
const wrapper = shallow(<Parent />);

expect(() => wrapper.find(Child).state()).to.throw(Error, 'ShallowWrapper::state() can only be called on the root');
});
});
});

describe('.children([selector])', () => {
Expand Down
13 changes: 4 additions & 9 deletions packages/enzyme/src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,14 @@ 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');
const root = this[ROOT];
if (this !== root) {
return root.update();
}
privateSetNodes(this, this[RENDERER].getNode());
return this;
Expand Down Expand Up @@ -313,9 +314,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');
}
Expand Down Expand Up @@ -675,9 +673,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');
}
Expand Down

0 comments on commit 6d1a498

Please sign in to comment.