Skip to content

Commit 25caf93

Browse files
koba04ljharb
authored andcommitted
[Tests] shallow: add componentDidUpdate tests
Per #1452 (comment)
1 parent 696a272 commit 25caf93

File tree

1 file changed

+276
-8
lines changed

1 file changed

+276
-8
lines changed

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

Lines changed: 276 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,7 +2945,7 @@ describe('shallow', () => {
29452945
}
29462946
}
29472947
const result = shallow(<Foo />);
2948-
expect(result.state('count')).to.equal(2);
2948+
expect(result.state()).to.have.property('count', 2);
29492949
expect(spy).to.have.property('callCount', 2);
29502950
});
29512951
});
@@ -2981,19 +2981,17 @@ describe('shallow', () => {
29812981

29822982
render() {
29832983
spy('render');
2984-
return <div>{this.state.foo}</div>;
2984+
const { foo } = this.state;
2985+
return <div>{foo}</div>;
29852986
}
29862987
}
29872988
Foo.contextTypes = {
29882989
foo: PropTypes.string,
29892990
};
29902991

2991-
const wrapper = shallow(
2992-
<Foo foo="bar" />,
2993-
{
2994-
context: { foo: 'context' },
2995-
},
2996-
);
2992+
const wrapper = shallow(<Foo foo="bar" />, {
2993+
context: { foo: 'context' },
2994+
});
29972995
wrapper.setProps({ foo: 'baz' });
29982996
wrapper.setProps({ foo: 'bax' });
29992997
expect(spy.args).to.deep.equal([
@@ -3595,6 +3593,276 @@ describe('shallow', () => {
35953593
});
35963594
});
35973595

3596+
context('unmounting phase', () => {
3597+
it('should call componentWillUnmount', () => {
3598+
const spy = sinon.spy();
3599+
class Foo extends React.Component {
3600+
componentWillUnmount() {
3601+
spy();
3602+
}
3603+
render() {
3604+
return <div>foo</div>;
3605+
}
3606+
}
3607+
const wrapper = shallow(<Foo />);
3608+
wrapper.unmount();
3609+
expect(spy).to.have.property('callCount', 1);
3610+
});
3611+
});
3612+
3613+
context('component instance', () => {
3614+
it('should call `componentDidUpdate` when component’s `setState` is called', () => {
3615+
const spy = sinon.spy();
3616+
class Foo extends React.Component {
3617+
constructor(props) {
3618+
super(props);
3619+
this.state = {
3620+
foo: 'init',
3621+
};
3622+
}
3623+
componentDidUpdate() {
3624+
spy();
3625+
}
3626+
onChange() {
3627+
// enzyme can't handle the update because `this` is a ReactComponent instance,
3628+
// not a ShallowWrapper instance.
3629+
this.setState({ foo: 'onChange update' });
3630+
}
3631+
render() {
3632+
return <div>{this.state.foo}</div>;
3633+
}
3634+
}
3635+
const wrapper = shallow(<Foo />);
3636+
3637+
wrapper.setState({ foo: 'wrapper setState update' });
3638+
expect(wrapper.state('foo')).to.equal('wrapper setState update');
3639+
expect(spy).to.have.property('callCount', 1);
3640+
3641+
wrapper.instance().onChange();
3642+
expect(wrapper.state('foo')).to.equal('onChange update');
3643+
expect(spy).to.have.property('callCount', 2);
3644+
});
3645+
});
3646+
3647+
describeIf(REACT16, 'support getSnapshotBeforeUpdate', () => {
3648+
it('should call getSnapshotBeforeUpdate and pass snapshot to componentDidUpdate', () => {
3649+
const spy = sinon.spy();
3650+
class Foo extends React.Component {
3651+
constructor(props) {
3652+
super(props);
3653+
this.state = {
3654+
foo: 'bar',
3655+
};
3656+
}
3657+
componentDidUpdate(prevProps, prevState, snapshot) {
3658+
spy('componentDidUpdate', prevProps, this.props, prevState, this.state, snapshot);
3659+
}
3660+
getSnapshotBeforeUpdate(prevProps, prevState) {
3661+
spy('getSnapshotBeforeUpdate', prevProps, this.props, prevState, this.state);
3662+
return { snapshot: 'ok' };
3663+
}
3664+
render() {
3665+
spy('render');
3666+
return <div>foo</div>;
3667+
}
3668+
}
3669+
const wrapper = shallow(<Foo name="foo" />);
3670+
spy.reset();
3671+
wrapper.setProps({ name: 'bar' });
3672+
expect(spy.args).to.deep.equal([
3673+
['render'],
3674+
['getSnapshotBeforeUpdate', { name: 'foo' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'bar' }],
3675+
['componentDidUpdate', { name: 'foo' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'bar' }, { snapshot: 'ok' }],
3676+
]);
3677+
spy.reset();
3678+
wrapper.setState({ foo: 'baz' });
3679+
expect(spy.args).to.deep.equal([
3680+
['render'],
3681+
['getSnapshotBeforeUpdate', { name: 'bar' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'baz' }],
3682+
['componentDidUpdate', { name: 'bar' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'baz' }, { snapshot: 'ok' }],
3683+
]);
3684+
});
3685+
});
3686+
3687+
it('should not call when disableLifecycleMethods flag is true', () => {
3688+
const spy = sinon.spy();
3689+
class Foo extends React.Component {
3690+
componentDidMount() {
3691+
spy();
3692+
}
3693+
render() {
3694+
return <div>foo</div>;
3695+
}
3696+
}
3697+
shallow(<Foo />, { disableLifecycleMethods: true });
3698+
expect(spy).to.have.property('callCount', 0);
3699+
});
3700+
3701+
it('should call shouldComponentUpdate when disableLifecycleMethods flag is true', () => {
3702+
const spy = sinon.spy();
3703+
class Foo extends React.Component {
3704+
constructor(props) {
3705+
super(props);
3706+
this.state = {
3707+
foo: 'bar',
3708+
};
3709+
}
3710+
shouldComponentUpdate() {
3711+
spy();
3712+
return false;
3713+
}
3714+
render() {
3715+
return <div>{this.state.foo}</div>;
3716+
}
3717+
}
3718+
const wrapper = shallow(
3719+
<Foo foo="foo" />,
3720+
{
3721+
context: { foo: 'foo' },
3722+
disableLifecycleMethods: true,
3723+
},
3724+
);
3725+
expect(spy).to.have.property('callCount', 0);
3726+
wrapper.setProps({ foo: 'bar' });
3727+
expect(spy).to.have.property('callCount', 1);
3728+
wrapper.setState({ foo: 'bar' });
3729+
expect(spy).to.have.property('callCount', 2);
3730+
wrapper.setContext({ foo: 'bar' });
3731+
expect(spy).to.have.property('callCount', 3);
3732+
});
3733+
});
3734+
3735+
it('works with class components that return null', () => {
3736+
class Foo extends React.Component {
3737+
render() {
3738+
return null;
3739+
}
3740+
}
3741+
const wrapper = shallow(<Foo />);
3742+
expect(wrapper).to.have.length(1);
3743+
expect(wrapper.html()).to.equal(null);
3744+
expect(wrapper.type()).to.equal(null);
3745+
const rendered = wrapper.render();
3746+
expect(rendered.length).to.equal(0);
3747+
expect(rendered.html()).to.equal(null);
3748+
});
3749+
3750+
itIf(REACT16, 'works with class components that return arrays', () => {
3751+
class Foo extends React.Component {
3752+
render() {
3753+
return [<div />, <div />];
3754+
}
3755+
}
3756+
const wrapper = shallow(<Foo />);
3757+
expect(wrapper).to.have.lengthOf(2);
3758+
expect(wrapper.find('div')).to.have.lengthOf(2);
3759+
});
3760+
3761+
itIf(is('>=15 || ^16.0.0-alpha'), 'works with SFCs that return null', () => {
3762+
const Foo = () => null;
3763+
3764+
const wrapper = shallow(<Foo />);
3765+
expect(wrapper).to.have.length(1);
3766+
expect(wrapper.html()).to.equal(null);
3767+
expect(wrapper.type()).to.equal(null);
3768+
const rendered = wrapper.render();
3769+
expect(rendered.length).to.equal(0);
3770+
expect(rendered.html()).to.equal(null);
3771+
});
3772+
3773+
describe('.tap()', () => {
3774+
it('should call the passed function with current ShallowWrapper and returns itself', () => {
3775+
const spy = sinon.spy();
3776+
const wrapper = shallow((
3777+
<ul>
3778+
<li>xxx</li>
3779+
<li>yyy</li>
3780+
<li>zzz</li>
3781+
</ul>
3782+
)).find('li');
3783+
const result = wrapper.tap(spy);
3784+
expect(spy.calledWith(wrapper)).to.equal(true);
3785+
expect(result).to.equal(wrapper);
3786+
});
3787+
});
3788+
3789+
describe('.key()', () => {
3790+
it('should return the key of the node', () => {
3791+
const wrapper = shallow((
3792+
<ul>
3793+
{['foo', 'bar', ''].map(s => <li key={s}>{s}</li>)}
3794+
</ul>
3795+
)).find('li');
3796+
expect(wrapper.at(0).key()).to.equal('foo');
3797+
expect(wrapper.at(1).key()).to.equal('bar');
3798+
expect(wrapper.at(2).key()).to.equal('');
3799+
});
3800+
3801+
it('should return null when no key is specified', () => {
3802+
const wrapper = shallow((
3803+
<ul>
3804+
<li>foo</li>
3805+
</ul>
3806+
)).find('li');
3807+
expect(wrapper.key()).to.equal(null);
3808+
});
3809+
});
3810+
3811+
describe('.matchesElement(node)', () => {
3812+
it('should match on a root node that looks like the rendered one', () => {
3813+
const spy = sinon.spy();
3814+
const wrapper = shallow((
3815+
<div>
3816+
<div onClick={spy} style={{ fontSize: 12, color: 'red' }}>Hello World</div>
3817+
</div>
3818+
)).first();
3819+
expect(wrapper.matchesElement(<div><div>Hello World</div></div>)).to.equal(true);
3820+
expect(wrapper.matchesElement((
3821+
<div>
3822+
<div onClick={spy} style={{ fontSize: 12, color: 'red' }}>Hello World</div>
3823+
</div>
3824+
))).to.equal(true);
3825+
expect(wrapper.matchesElement((
3826+
<div>
3827+
<div onClick={spy}>Hello World</div>
3828+
</div>
3829+
))).to.equal(true);
3830+
expect(wrapper.matchesElement((
3831+
<div>
3832+
<div style={{ fontSize: 12, color: 'red' }}>Hello World</div>
3833+
</div>
3834+
))).to.equal(true);
3835+
expect(spy).to.have.property('callCount', 0);
3836+
});
3837+
3838+
it('should not match on a root node that doesn\'t looks like the rendered one', () => {
3839+
const spy = sinon.spy();
3840+
const spy2 = sinon.spy();
3841+
const wrapper = shallow((
3842+
<div>
3843+
<div onClick={spy} style={{ fontSize: 12, color: 'red' }}>Hello World</div>
3844+
</div>
3845+
)).first();
3846+
expect(wrapper.matchesElement(<div><div>Bonjour le monde</div></div>)).to.equal(false);
3847+
expect(wrapper.matchesElement((
3848+
<div>
3849+
<div onClick={spy} style={{ fontSize: 12, color: 'blue' }}>Hello World</div>
3850+
</div>
3851+
))).to.equal(false);
3852+
expect(wrapper.matchesElement((
3853+
<div>
3854+
<div onClick={spy2}>Hello World</div>
3855+
</div>
3856+
))).to.equal(false);
3857+
expect(wrapper.matchesElement((
3858+
<div>
3859+
<div style={{ fontSize: 13, color: 'red' }}>Hello World</div>
3860+
</div>
3861+
))).to.equal(false);
3862+
expect(spy).to.have.property('callCount', 0);
3863+
expect(spy2).to.have.property('callCount', 0);
3864+
});
3865+
35983866
context('unmounting phase', () => {
35993867
it('calls componentWillUnmount', () => {
36003868
const spy = sinon.spy();

0 commit comments

Comments
 (0)