diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 7c894cf75..f529c4c3d 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -2945,7 +2945,7 @@ describe('shallow', () => { } } const result = shallow(); - expect(result.state('count')).to.equal(2); + expect(result.state()).to.have.property('count', 2); expect(spy).to.have.property('callCount', 2); }); }); @@ -2981,19 +2981,17 @@ describe('shallow', () => { render() { spy('render'); - return
{this.state.foo}
; + const { foo } = this.state; + return
{foo}
; } } Foo.contextTypes = { foo: PropTypes.string, }; - const wrapper = shallow( - , - { - context: { foo: 'context' }, - }, - ); + const wrapper = shallow(, { + context: { foo: 'context' }, + }); wrapper.setProps({ foo: 'baz' }); wrapper.setProps({ foo: 'bax' }); expect(spy.args).to.deep.equal([ @@ -3595,6 +3593,276 @@ describe('shallow', () => { }); }); + context('unmounting phase', () => { + it('should call componentWillUnmount', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + componentWillUnmount() { + spy(); + } + render() { + return
foo
; + } + } + const wrapper = shallow(); + wrapper.unmount(); + expect(spy).to.have.property('callCount', 1); + }); + }); + + context('component instance', () => { + it('should call `componentDidUpdate` when component’s `setState` is called', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + foo: 'init', + }; + } + componentDidUpdate() { + spy(); + } + onChange() { + // enzyme can't handle the update because `this` is a ReactComponent instance, + // not a ShallowWrapper instance. + this.setState({ foo: 'onChange update' }); + } + render() { + return
{this.state.foo}
; + } + } + const wrapper = shallow(); + + wrapper.setState({ foo: 'wrapper setState update' }); + expect(wrapper.state('foo')).to.equal('wrapper setState update'); + expect(spy).to.have.property('callCount', 1); + + wrapper.instance().onChange(); + expect(wrapper.state('foo')).to.equal('onChange update'); + expect(spy).to.have.property('callCount', 2); + }); + }); + + describeIf(REACT16, 'support getSnapshotBeforeUpdate', () => { + it('should call getSnapshotBeforeUpdate and pass snapshot to componentDidUpdate', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + foo: 'bar', + }; + } + componentDidUpdate(prevProps, prevState, snapshot) { + spy('componentDidUpdate', prevProps, this.props, prevState, this.state, snapshot); + } + getSnapshotBeforeUpdate(prevProps, prevState) { + spy('getSnapshotBeforeUpdate', prevProps, this.props, prevState, this.state); + return { snapshot: 'ok' }; + } + render() { + spy('render'); + return
foo
; + } + } + const wrapper = shallow(); + spy.reset(); + wrapper.setProps({ name: 'bar' }); + expect(spy.args).to.deep.equal([ + ['render'], + ['getSnapshotBeforeUpdate', { name: 'foo' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'bar' }], + ['componentDidUpdate', { name: 'foo' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'bar' }, { snapshot: 'ok' }], + ]); + spy.reset(); + wrapper.setState({ foo: 'baz' }); + expect(spy.args).to.deep.equal([ + ['render'], + ['getSnapshotBeforeUpdate', { name: 'bar' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'baz' }], + ['componentDidUpdate', { name: 'bar' }, { name: 'bar' }, { foo: 'bar' }, { foo: 'baz' }, { snapshot: 'ok' }], + ]); + }); + }); + + it('should not call when disableLifecycleMethods flag is true', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + componentDidMount() { + spy(); + } + render() { + return
foo
; + } + } + shallow(, { disableLifecycleMethods: true }); + expect(spy).to.have.property('callCount', 0); + }); + + it('should call shouldComponentUpdate when disableLifecycleMethods flag is true', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + foo: 'bar', + }; + } + shouldComponentUpdate() { + spy(); + return false; + } + render() { + return
{this.state.foo}
; + } + } + const wrapper = shallow( + , + { + context: { foo: 'foo' }, + disableLifecycleMethods: true, + }, + ); + expect(spy).to.have.property('callCount', 0); + wrapper.setProps({ foo: 'bar' }); + expect(spy).to.have.property('callCount', 1); + wrapper.setState({ foo: 'bar' }); + expect(spy).to.have.property('callCount', 2); + wrapper.setContext({ foo: 'bar' }); + expect(spy).to.have.property('callCount', 3); + }); + }); + + it('works with class components that return null', () => { + class Foo extends React.Component { + render() { + return null; + } + } + const wrapper = shallow(); + expect(wrapper).to.have.length(1); + expect(wrapper.html()).to.equal(null); + expect(wrapper.type()).to.equal(null); + const rendered = wrapper.render(); + expect(rendered.length).to.equal(0); + expect(rendered.html()).to.equal(null); + }); + + itIf(REACT16, 'works with class components that return arrays', () => { + class Foo extends React.Component { + render() { + return [
,
]; + } + } + const wrapper = shallow(); + expect(wrapper).to.have.lengthOf(2); + expect(wrapper.find('div')).to.have.lengthOf(2); + }); + + itIf(is('>=15 || ^16.0.0-alpha'), 'works with SFCs that return null', () => { + const Foo = () => null; + + const wrapper = shallow(); + expect(wrapper).to.have.length(1); + expect(wrapper.html()).to.equal(null); + expect(wrapper.type()).to.equal(null); + const rendered = wrapper.render(); + expect(rendered.length).to.equal(0); + expect(rendered.html()).to.equal(null); + }); + + describe('.tap()', () => { + it('should call the passed function with current ShallowWrapper and returns itself', () => { + const spy = sinon.spy(); + const wrapper = shallow(( +
    +
  • xxx
  • +
  • yyy
  • +
  • zzz
  • +
+ )).find('li'); + const result = wrapper.tap(spy); + expect(spy.calledWith(wrapper)).to.equal(true); + expect(result).to.equal(wrapper); + }); + }); + + describe('.key()', () => { + it('should return the key of the node', () => { + const wrapper = shallow(( +
    + {['foo', 'bar', ''].map(s =>
  • {s}
  • )} +
+ )).find('li'); + expect(wrapper.at(0).key()).to.equal('foo'); + expect(wrapper.at(1).key()).to.equal('bar'); + expect(wrapper.at(2).key()).to.equal(''); + }); + + it('should return null when no key is specified', () => { + const wrapper = shallow(( +
    +
  • foo
  • +
+ )).find('li'); + expect(wrapper.key()).to.equal(null); + }); + }); + + describe('.matchesElement(node)', () => { + it('should match on a root node that looks like the rendered one', () => { + const spy = sinon.spy(); + const wrapper = shallow(( +
+
Hello World
+
+ )).first(); + expect(wrapper.matchesElement(
Hello World
)).to.equal(true); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(true); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(true); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(true); + expect(spy).to.have.property('callCount', 0); + }); + + it('should not match on a root node that doesn\'t looks like the rendered one', () => { + const spy = sinon.spy(); + const spy2 = sinon.spy(); + const wrapper = shallow(( +
+
Hello World
+
+ )).first(); + expect(wrapper.matchesElement(
Bonjour le monde
)).to.equal(false); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(false); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(false); + expect(wrapper.matchesElement(( +
+
Hello World
+
+ ))).to.equal(false); + expect(spy).to.have.property('callCount', 0); + expect(spy2).to.have.property('callCount', 0); + }); + context('unmounting phase', () => { it('calls componentWillUnmount', () => { const spy = sinon.spy();