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 dd275fe commit 65657ff
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 8 deletions.
102 changes: 102 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2965,6 +2965,64 @@ describeWithDOM('mount', () => {

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

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 (
<div>
{_}
{a}
</div>
);
}
}

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

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

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

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

return new Promise((resolve) => {
wrapper.setState({ _: 2 }, () => {
expect(wrapper.text().trim()).to.eql('2a');
resolve();
});
});
});

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

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

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

describe('.is(selector)', () => {
Expand Down Expand Up @@ -3517,6 +3575,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 (
<div>
{_}
{a}
</div>
);
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

describe('.is(selector)', () => {
Expand Down Expand Up @@ -3210,6 +3263,50 @@ 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 = { a: 'a' };
}

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

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

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

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

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

it('gets 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
10 changes: 2 additions & 8 deletions packages/enzyme/src/ReactWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,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;
Expand Down Expand Up @@ -303,9 +303,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 @@ -635,9 +632,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 65657ff

Please sign in to comment.