UNPKG

kitchensink

Version:

Dispatch's awesome components and style guide

1,682 lines (1,456 loc) 92.6 kB
import { describeWithDOM, describeIf, itIf, itWithData, generateEmptyRenderData, } from './_helpers'; import React from 'react'; import { expect } from 'chai'; import { mount, render, ReactWrapper, } from '../src/'; import sinon from 'sinon'; import { REACT013, REACT15 } from '../src/version'; describeWithDOM('mount', () => { describe('context', () => { it('can pass in context', () => { const SimpleComponent = React.createClass({ contextTypes: { name: React.PropTypes.string, }, render() { return <div>{this.context.name}</div>; }, }); const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.text()).to.equal('foo'); }); it('can pass context to the child of mounted component', () => { const SimpleComponent = React.createClass({ contextTypes: { name: React.PropTypes.string, }, render() { return <div>{this.context.name}</div>; }, }); const ComplexComponent = React.createClass({ render() { return <div><SimpleComponent /></div>; }, }); const childContextTypes = { name: React.PropTypes.string.isRequired, }; const context = { name: 'foo' }; const wrapper = mount(<ComplexComponent />, { context, childContextTypes }); expect(wrapper.find(SimpleComponent)).to.have.length(1); }); it('should not throw if context is passed in but contextTypes is missing', () => { const SimpleComponent = React.createClass({ render() { return <div>{this.context.name}</div>; }, }); const context = { name: 'foo' }; expect(() => mount(<SimpleComponent />, { context })).to.not.throw(Error); }); it('is instrospectable through context API', () => { const SimpleComponent = React.createClass({ contextTypes: { name: React.PropTypes.string, }, render() { return <div>{this.context.name}</div>; }, }); const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.context().name).to.equal(context.name); expect(wrapper.context('name')).to.equal(context.name); }); describeIf(!REACT013, 'stateless components', () => { it('can pass in context', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); SimpleComponent.contextTypes = { name: React.PropTypes.string }; const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.text()).to.equal('foo'); }); it('can pass context to the child of mounted component', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); SimpleComponent.contextTypes = { name: React.PropTypes.string }; const ComplexComponent = () => ( <div><SimpleComponent /></div> ); const childContextTypes = { name: React.PropTypes.string.isRequired, }; const context = { name: 'foo' }; const wrapper = mount(<ComplexComponent />, { context, childContextTypes }); expect(wrapper.find(SimpleComponent)).to.have.length(1); }); it('should not throw if context is passed in but contextTypes is missing', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); const context = { name: 'foo' }; expect(() => mount(<SimpleComponent />, { context })).to.not.throw(Error); }); it('is instrospectable through context API', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); SimpleComponent.contextTypes = { name: React.PropTypes.string }; const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.context().name).to.equal(context.name); expect(wrapper.context('name')).to.equal(context.name); }); it('works with stateless components', () => { const Foo = ({ foo }) => ( <div> <div className="bar">bar</div> <div className="qoo">{foo}</div> </div> ); Foo.contextTypes = { _: React.PropTypes.string, }; const wrapper = mount(<Foo foo="qux" />, { context: { _: 'foo', }, }); expect(wrapper.context('_')).to.equal('foo'); }); }); }); describeIf(!REACT013, 'stateless components', () => { it('works with stateless components', () => { const Foo = ({ foo }) => ( <div> <div className="bar">bar</div> <div className="qoo">{foo}</div> </div> ); const wrapper = mount(<Foo foo="qux" />); expect(wrapper.type()).to.equal(Foo); expect(wrapper.find('.bar')).to.have.length(1); expect(wrapper.find('.qoo').text()).to.equal('qux'); }); it('supports findDOMNode with stateless components', () => { const Foo = ({ foo }) => ( <div>{foo}</div> ); const wrapper = mount(<Foo foo="qux" />); expect(wrapper.text()).to.equal('qux'); }); it('works with nested stateless', () => { const TestItem = () => ( <div className="item">1</div> ); const Test = () => ( <div className="box"> <TestItem test="123" /> <TestItem /> <TestItem /> </div> ); const wrapper = mount(<Test />); const children = wrapper.find('.box').children(); expect(children).to.have.length(3); expect(children.at(0).props().test).to.equal('123'); expect(wrapper.find(TestItem)).to.have.length(3); expect(wrapper.find(TestItem).first().props().test).to.equal('123'); }); }); describe('.contains(node)', () => { it('should allow matches on the root node', () => { const a = <div className="foo" />; const b = <div className="foo" />; const c = <div className="bar" />; expect(mount(a).contains(b)).to.equal(true); expect(mount(a).contains(c)).to.equal(false); }); it('should allow matches on a nested node', () => { const wrapper = mount( <div> <div className="foo" /> </div> ); const b = <div className="foo" />; expect(wrapper.contains(b)).to.equal(true); }); it('should match composite components', () => { class Foo extends React.Component { render() { return <div />; } } const wrapper = mount( <div> <Foo /> </div> ); const b = <Foo />; expect(wrapper.contains(b)).to.equal(true); }); it('should do something with arrays of nodes', () => { const wrapper = mount( <div> <span>Hello</span> <div>Goodbye</div> <span>More</span> </div> ); const fails = [ <span>wrong</span>, <div>Goodbye</div>, ]; const passes1 = [ <span>Hello</span>, <div>Goodbye</div>, ]; const passes2 = [ <div>Goodbye</div>, <span>More</span>, ]; expect(wrapper.contains(fails)).to.equal(false); expect(wrapper.contains(passes1)).to.equal(true); expect(wrapper.contains(passes2)).to.equal(true); }); describeIf(!REACT013, 'stateless components', () => { it('should match composite components', () => { const Foo = () => <div />; const wrapper = mount( <div> <Foo /> </div> ); const b = <Foo />; expect(wrapper.contains(b)).to.equal(true); }); }); }); describe('.find(selector)', () => { it('should find an element based on a class name', () => { const wrapper = mount( <div> <input className="foo" /> </div> ); expect(wrapper.find('.foo').type()).to.equal('input'); }); it('should find an SVG element based on a class name', () => { const wrapper = mount( <div> <svg className="foo" /> </div> ); expect(wrapper.find('.foo').type()).to.equal('svg'); }); it('should find an element based on a tag name', () => { const wrapper = mount( <div> <input className="foo" /> </div> ); expect(wrapper.find('input').props().className).to.equal('foo'); }); it('should find an element based on a tag name and class name', () => { const wrapper = mount( <div> <input className="foo" /> </div> ); expect(wrapper.find('input.foo').length).to.equal(1); }); it('should find an element based on a tag name and id', () => { const wrapper = mount( <div> <input id="foo" /> </div> ); expect(wrapper.find('input#foo').length).to.equal(1); }); it('should find an element based on a tag name, id, and class name', () => { const wrapper = mount( <div> <input id="foo" className="bar" /> </div> ); expect(wrapper.find('input#foo.bar').length).to.equal(1); }); it('should find a component based on a constructor', () => { class Foo extends React.Component { render() { return <div />; } } const wrapper = mount( <div> <Foo className="foo" /> </div> ); expect(wrapper.find(Foo).type()).to.equal(Foo); }); it('should find a component based on a component displayName', () => { class Foo extends React.Component { render() { return <div />; } } const wrapper = mount( <div> <Foo className="foo" /> </div> ); expect(wrapper.find('Foo').type()).to.equal(Foo); }); it('should find component based on a react prop', () => { const wrapper = mount( <div> <span htmlFor="foo" /> </div> ); expect(wrapper.find('[htmlFor="foo"]')).to.have.length(1); expect(wrapper.find('[htmlFor]')).to.have.length(1); }); it('should compound tag and prop selector', () => { const wrapper = mount( <div> <span htmlFor="foo" /> </div> ); expect(wrapper.find('span[htmlFor="foo"]')).to.have.length(1); expect(wrapper.find('span[htmlFor]')).to.have.length(1); }); it('should not find components with invalid attributes', () => { // Invalid attributes aren't valid JSX, so manual instantiation is necessary const wrapper = mount( React.createElement('div', null, React.createElement('span', { '123-foo': 'bar', '-foo': 'bar', ':foo': 'bar', })) ); expect(wrapper.find('[-foo]')).to.have.length(0, '-foo'); expect(wrapper.find('[:foo]')).to.have.length(0, ':foo'); expect(wrapper.find('[123-foo]')).to.have.length(0, '123-foo'); }); it('should support data prop selectors', () => { const wrapper = mount( <div> <span data-foo="bar" /> <span data-foo-123="bar2" /> <span data-123-foo="bar3" /> <span data-foo_bar="bar4" /> </div> ); expect(wrapper.find('[data-foo="bar"]')).to.have.length(1); expect(wrapper.find('[data-foo]')).to.have.length(1); expect(wrapper.find('[data-foo-123]')).to.have.length(1); expect(wrapper.find('[data-foo-123="bar2"]')).to.have.length(1); expect(wrapper.find('[data-123-foo]')).to.have.length(1); expect(wrapper.find('[data-123-foo="bar3"]')).to.have.length(1); expect(wrapper.find('[data-foo_bar]')).to.have.length(1); expect(wrapper.find('[data-foo_bar="bar4"]')).to.have.length(1); }); it('should find components with multiple matching props', () => { const onChange = () => ({}); const wrapper = mount( <div> <span htmlFor="foo" onChange={onChange} preserveAspectRatio="xMaxYMax" /> </div> ); expect(wrapper.find('span[htmlFor="foo"][onChange]')).to.have.length(1); expect(wrapper.find('span[htmlFor="foo"][preserveAspectRatio="xMaxYMax"]')).to.have.length(1); }); it('should not find property when undefined', () => { const wrapper = mount( <div> <span data-foo={undefined} /> </div> ); expect(wrapper.find('[data-foo]')).to.have.length(0); }); it('should support boolean and numeric values for matching props', () => { const wrapper = mount( <div> <span value={1} /> <a value={false} /> </div> ); expect(wrapper.find('span[value=1]')).to.have.length(1); expect(wrapper.find('span[value=2]')).to.have.length(0); expect(wrapper.find('a[value=false]')).to.have.length(1); expect(wrapper.find('a[value=true]')).to.have.length(0); }); it('should not find key or ref via property selector', () => { class Foo extends React.Component { render() { const arrayOfComponents = [<div key="1" />, <div key="2" />]; return ( <div> <div ref="foo" /> {arrayOfComponents} </div> ); } } const wrapper = mount(<Foo />); expect(wrapper.find('div[ref="foo"]')).to.have.length(0); expect(wrapper.find('div[key="1"]')).to.have.length(0); expect(wrapper.find('[ref]')).to.have.length(0); expect(wrapper.find('[key]')).to.have.length(0); }); it('should find multiple elements based on a class name', () => { const wrapper = mount( <div> <input className="foo" /> <button className="foo" /> </div> ); expect(wrapper.find('.foo').length).to.equal(2); }); it('should find multiple elements based on a tag name', () => { const wrapper = mount( <div> <input className="foo" /> <input /> <button /> </div> ); expect(wrapper.find('input').length).to.equal(2); expect(wrapper.find('button').length).to.equal(1); }); it('should find multiple elements based on a constructor', () => { const wrapper = mount( <div> <input className="foo" /> <input /> <button /> </div> ); expect(wrapper.find('input').length).to.equal(2); expect(wrapper.find('button').length).to.equal(1); }); it('should support object property selectors', () => { const wrapper = mount( <div> <input data-test="ref" className="foo" type="text" /> <input data-test="ref" type="text" /> <button data-test="ref" prop={undefined} /> <span data-test="ref" prop={null} /> <div data-test="ref" prop={123} /> <input data-test="ref" prop={false} /> <a data-test="ref" prop /> </div> ); expect(wrapper.find({ a: 1 })).to.have.length(0); expect(wrapper.find({ 'data-test': 'ref' })).to.have.length(7); expect(wrapper.find({ className: 'foo' })).to.have.length(1); expect(wrapper.find({ prop: undefined })).to.have.length(1); expect(wrapper.find({ prop: null })).to.have.length(1); expect(wrapper.find({ prop: 123 })).to.have.length(1); expect(wrapper.find({ prop: false })).to.have.length(1); expect(wrapper.find({ prop: true })).to.have.length(1); }); it('should support complex and nested object property selectors', () => { const testFunction = () => ({}); const wrapper = mount( <div> <span more={[{ id: 1 }]} data-test="ref" prop onChange={testFunction} /> <a more={[{ id: 1 }]} data-test="ref" /> <div more={{ item: { id: 1 } }} data-test="ref" /> <input style={{ height: 20 }} data-test="ref" /> </div> ); expect(wrapper.find({ 'data-test': 'ref' })).to.have.length(4); expect(wrapper.find({ more: { a: 1 } })).to.have.length(0); expect(wrapper.find({ more: [{ id: 1 }] })).to.have.length(2); expect(wrapper.find({ more: { item: { id: 1 } } })).to.have.length(1); expect(wrapper.find({ style: { height: 20 } })).to.have.length(1); expect(wrapper .find({ more: [{ id: 1 }], 'data-test': 'ref', prop: true, onChange: testFunction }) ).to.have.length(1); }); it('should throw when given empty object, null, or an array', () => { const wrapper = mount( <div> <input className="foo" type="text" /> </div> ); expect(() => wrapper.find({})).to.throw(Error); expect(() => wrapper.find([])).to.throw(Error); expect(() => wrapper.find(null)).to.throw(Error); }); it('Should query attributes with spaces in their values', () => { const wrapper = mount( <div> <h1 data-foo="foo bar">Hello</h1> <h1 data-foo="bar baz quz">World</h1> </div> ); expect(wrapper.find('[data-foo]')).to.have.length(2); expect(wrapper.find('[data-foo="foo bar"]')).to.have.length(1); expect(wrapper.find('[data-foo="bar baz quz"]')).to.have.length(1); expect(wrapper.find('[data-foo="bar baz"]')).to.have.length(0); expect(wrapper.find('[data-foo="foo bar"]')).to.have.length(0); expect(wrapper.find('[data-foo="bar baz quz"]')).to.have.length(0); }); describeIf(!REACT013, 'stateless function components', () => { it('should find a component based on a constructor', () => { const Foo = () => <div />; const wrapper = mount( <div> <Foo className="foo" /> </div> ); expect(wrapper.find(Foo).type()).to.equal(Foo); }); it('should find a component based on a component displayName', () => { const Foo = () => <div />; const wrapper = mount( <div> <Foo className="foo" /> </div> ); expect(wrapper.find('Foo').type()).to.equal(Foo); }); it('should not find key via property selector', () => { const Foo = () => { const arrayOfComponents = [<div key="1" />, <div key="2" />]; return ( <div> {arrayOfComponents} </div> ); }; const wrapper = mount(<Foo />); expect(wrapper.find('div[key="1"]')).to.have.length(0); expect(wrapper.find('[key]')).to.have.length(0); }); }); }); describe('.findWhere(predicate)', () => { it('should return all elements for a truthy test', () => { const wrapper = mount( <div> <input className="foo" /> <input /> </div> ); expect(wrapper.findWhere(() => true).length).to.equal(3); }); it('should return no elements for a falsy test', () => { const wrapper = mount( <div> <input className="foo" /> <input /> </div> ); expect(wrapper.findWhere(() => false).length).to.equal(0); }); it('should call the predicate with the wrapped node as the first argument', () => { const wrapper = mount( <div> <div className="foo bar" /> <div className="foo baz" /> <div className="foo bux" /> </div> ); const stub = sinon.stub(); stub.returns(true); const spy = sinon.spy(stub); wrapper.findWhere(spy); expect(spy.callCount).to.equal(4); expect(spy.args[0][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[1][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[2][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[3][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[1][0].hasClass('bar')).to.equal(true); expect(spy.args[2][0].hasClass('baz')).to.equal(true); expect(spy.args[3][0].hasClass('bux')).to.equal(true); }); it('finds nodes', () => { class Foo extends React.Component { render() { return ( <div> <span data-foo={this.props.selector} /> <i data-foo={this.props.selector} /> </div> ); } } const selector = 'blah'; const wrapper = mount(<Foo selector={selector} />); const foundSpan = wrapper.findWhere(n => ( n.type() === 'span' && n.props()['data-foo'] === selector )); expect(foundSpan.type()).to.equal('span'); const foundNotSpan = wrapper.findWhere(n => ( n.type() !== 'span' && n.props()['data-foo'] === selector )); expect(foundNotSpan.type()).to.equal('i'); }); it('finds nodes when conditionally rendered', () => { class Foo extends React.Component { render() { return ( <div> <span data-foo={this.props.selector} /> {this.props.selector === 'baz' ? <i data-foo={this.props.selector} /> : null} </div> ); } } const selector = 'blah'; const wrapper = mount(<Foo selector={selector} />); const foundSpan = wrapper.findWhere(n => ( n.type() === 'span' && n.props()['data-foo'] === selector )); expect(foundSpan.type()).to.equal('span'); const foundNotSpan = wrapper.findWhere(n => ( n.type() !== 'span' && n.props()['data-foo'] === selector )); expect(foundNotSpan).to.have.length(0); }); it('should return props object when props() is called', () => { class Foo extends React.Component { render() { return ( <div data-foo={this.props.data}>Test Component</div> ); } } const content = 'blah'; const wrapper = mount(<Foo data={content} />); expect(wrapper.props()).to.deep.equal({ data: content }); }); it('should return shallow rendered string when debug() is called', () => { class Foo extends React.Component { render() { return ( <div data-foo={this.props.data}>Test Component</div> ); } } const content = 'blah'; const wrapper = mount(<Foo data={content} />); expect(wrapper.debug()).to.equal( `<Foo data="${content}"> <div data-foo="${content}"> Test Component </div> </Foo>` ); }); describeIf(!REACT013, 'stateless functional components', () => { it('finds nodes', () => { const SFC = function SFC({ selector }) { return ( <div> <span data-foo={selector} /> <i data-foo={selector} /> </div> ); }; const selector = 'blah'; const wrapper = mount(<SFC selector={selector} />); const foundSpan = wrapper.findWhere(n => ( n.type() === 'span' && n.props()['data-foo'] === selector )); expect(foundSpan.type()).to.equal('span'); const foundNotSpan = wrapper.findWhere(n => ( n.type() !== 'span' && n.props()['data-foo'] === selector )); expect(foundNotSpan.type()).to.equal('i'); }); it('finds nodes when conditionally rendered', () => { const SFC = function SFC({ selector }) { return ( <div> <span data-foo={selector} /> {selector === 'baz' ? <i data-foo={selector} /> : null} </div> ); }; const selector = 'blah'; const wrapper = mount(<SFC selector={selector} />); const foundSpan = wrapper.findWhere(n => ( n.type() === 'span' && n.props()['data-foo'] === selector )); expect(foundSpan.type()).to.equal('span'); const foundNotSpan = wrapper.findWhere(n => ( n.type() !== 'span' && n.props()['data-foo'] === selector )); expect(foundNotSpan).to.have.length(0); }); it('should return props object when props() is called', () => { const SFC = function SFC({ data }) { return ( <div data-foo={data}>Test SFC</div> ); }; const content = 'blah'; const wrapper = mount(<SFC data={content} />); expect(wrapper.props()).to.deep.equal({ data: content }); }); it('should return shallow rendered string when debug() is called', () => { const SFC = function SFC({ data }) { return ( <div data-foo={data}>Test SFC</div> ); }; const content = 'blah'; const wrapper = mount(<SFC data={content} />); expect(wrapper.debug()).to.equal( `<SFC data="${content}"> <div data-foo="${content}"> Test SFC </div> </SFC>` ); }); }); it('should not pass in null or false nodes', () => { const wrapper = mount( <div> <div className="foo bar" /> {null} {false} </div> ); const stub = sinon.stub(); stub.returns(true); const spy = sinon.spy(stub); wrapper.findWhere(spy); expect(spy.callCount).to.equal(2); }); }); describe('.setProps(newProps)', () => { it('should set props for a component multiple times', () => { class Foo extends React.Component { render() { return ( <div className={this.props.id}> {this.props.id} </div> ); } } const wrapper = mount(<Foo id="foo" />); expect(wrapper.find('.foo').length).to.equal(1); wrapper.setProps({ id: 'bar', foo: 'bla' }); expect(wrapper.find('.bar').length).to.equal(1); }); it('should call componentWillReceiveProps for new renders', () => { const spy = sinon.spy(); class Foo extends React.Component { constructor(props) { super(props); this.componentWillReceiveProps = spy; } render() { return ( <div className={this.props.id}> {this.props.id} </div> ); } } const nextProps = { id: 'bar', foo: 'bla' }; const wrapper = mount(<Foo id="foo" />); expect(spy.calledOnce).to.equal(false); wrapper.setProps(nextProps); expect(spy.calledOnce).to.equal(true); expect(spy.calledWith(nextProps)).to.equal(true); }); it('should merge newProps with oldProps', () => { class Foo extends React.Component { render() { return ( <div {...this.props} /> ); } } const wrapper = mount(<Foo a="a" b="b" />); expect(wrapper.props().a).to.equal('a'); expect(wrapper.props().b).to.equal('b'); wrapper.setProps({ b: 'c', d: 'e' }); expect(wrapper.props().a).to.equal('a'); expect(wrapper.props().b).to.equal('c'); expect(wrapper.props().d).to.equal('e'); }); it('should throw if an exception occurs during render', () => { class Trainwreck extends React.Component { render() { const { user } = this.props; return ( <div> {user.name.givenName} </div> ); } } const validUser = { name: { givenName: 'Brian', }, }; const wrapper = mount(<Trainwreck user={validUser} />); const setInvalidProps = () => { wrapper.setProps({ user: {}, }); }; expect(setInvalidProps).to.throw(); }); describeIf(!REACT013, 'stateless function components', () => { it('should set props for a component multiple times', () => { const Foo = (props) => ( <div className={props.id}> {props.id} </div> ); const wrapper = mount(<Foo id="foo" />); expect(wrapper.find('.foo').length).to.equal(1); wrapper.setProps({ id: 'bar', foo: 'bla' }); expect(wrapper.find('.bar').length).to.equal(1); }); it('should merge newProps with oldProps', () => { const Foo = (props) => ( <div {...props} /> ); const wrapper = mount(<Foo a="a" b="b" />); expect(wrapper.props().a).to.equal('a'); expect(wrapper.props().b).to.equal('b'); wrapper.setProps({ b: 'c', d: 'e' }); expect(wrapper.props().a).to.equal('a'); expect(wrapper.props().b).to.equal('c'); expect(wrapper.props().d).to.equal('e'); }); it('should throw if an exception occurs during render', () => { const Trainwreck = ({ user }) => ( <div> {user.name.givenName} </div> ); const validUser = { name: { givenName: 'Brian', }, }; const wrapper = mount(<Trainwreck user={validUser} />); const setInvalidProps = () => { wrapper.setProps({ user: {}, }); }; expect(setInvalidProps).to.throw(); }); }); }); describe('.setContext(newContext)', () => { it('should set context for a component multiple times', () => { const SimpleComponent = React.createClass({ contextTypes: { name: React.PropTypes.string, }, render() { return <div>{this.context.name}</div>; }, }); const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.text()).to.equal('foo'); wrapper.setContext({ name: 'bar' }); expect(wrapper.text()).to.equal('bar'); wrapper.setContext({ name: 'baz' }); expect(wrapper.text()).to.equal('baz'); }); it('should throw if it is called when shallow didnt include context', () => { const SimpleComponent = React.createClass({ contextTypes: { name: React.PropTypes.string, }, render() { return <div>{this.context.name}</div>; }, }); const wrapper = mount(<SimpleComponent />); expect(() => wrapper.setContext({ name: 'bar' })).to.throw(Error); }); describeIf(!REACT013, 'stateless function components', () => { it('should set context for a component multiple times', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); SimpleComponent.contextTypes = { name: React.PropTypes.string }; const context = { name: 'foo' }; const wrapper = mount(<SimpleComponent />, { context }); expect(wrapper.text()).to.equal('foo'); wrapper.setContext({ name: 'bar' }); expect(wrapper.text()).to.equal('bar'); wrapper.setContext({ name: 'baz' }); expect(wrapper.text()).to.equal('baz'); }); it('should throw if it is called when shallow didnt include context', () => { const SimpleComponent = (props, context) => ( <div>{context.name}</div> ); SimpleComponent.contextTypes = { name: React.PropTypes.string }; const wrapper = mount(<SimpleComponent />); expect(() => wrapper.setContext({ name: 'bar' })).to.throw(Error); }); }); }); describe('.mount()', () => { it('should call componentWillUnmount()', () => { const willMount = sinon.spy(); const didMount = sinon.spy(); const willUnmount = sinon.spy(); class Foo extends React.Component { constructor(props) { super(props); this.componentWillUnmount = willUnmount; this.componentWillMount = willMount; this.componentDidMount = didMount; } render() { return ( <div className={this.props.id}> {this.props.id} </div> ); } } const wrapper = mount(<Foo id="foo" />); expect(willMount.callCount).to.equal(1); expect(didMount.callCount).to.equal(1); expect(willUnmount.callCount).to.equal(0); wrapper.unmount(); expect(willMount.callCount).to.equal(1); expect(didMount.callCount).to.equal(1); expect(willUnmount.callCount).to.equal(1); wrapper.mount(); expect(willMount.callCount).to.equal(2); expect(didMount.callCount).to.equal(2); expect(willUnmount.callCount).to.equal(1); }); }); describe('.unmount()', () => { it('should call componentWillUnmount()', () => { const spy = sinon.spy(); class Foo extends React.Component { constructor(props) { super(props); this.componentWillUnmount = spy; } render() { return ( <div className={this.props.id}> {this.props.id} </div> ); } } const wrapper = mount(<Foo id="foo" />); expect(spy.calledOnce).to.equal(false); wrapper.unmount(); expect(spy.calledOnce).to.equal(true); }); }); describe('.simulate(eventName, data)', () => { it('should simulate events', () => { class Foo extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; this.incrementCount = this.incrementCount.bind(this); } incrementCount() { this.setState({ count: this.state.count + 1 }); } render() { return ( <a className={`clicks-${this.state.count}`} onClick={this.incrementCount} > foo </a> ); } } const wrapper = mount(<Foo />); expect(wrapper.find('.clicks-0').length).to.equal(1); wrapper.simulate('click'); expect(wrapper.find('.clicks-1').length).to.equal(1); }); it('should pass in event data', () => { const spy = sinon.spy(); class Foo extends React.Component { render() { return ( <a onClick={spy}>foo</a> ); } } const wrapper = mount(<Foo />); wrapper.simulate('click', { someSpecialData: 'foo' }); expect(spy.calledOnce).to.equal(true); expect(spy.args[0][0].someSpecialData).to.equal('foo'); }); it('should throw a descriptive error for invalid events', () => { const wrapper = mount(<div>foo</div>); expect(wrapper.simulate.bind(wrapper, 'invalidEvent')) .to.throw(TypeError, "ReactWrapper::simulate() event 'invalidEvent' does not exist"); }); describe('Normalizing JS event names', () => { it('should convert lowercase events to React camelcase', () => { const spy = sinon.spy(); const clickSpy = sinon.spy(); class Foo extends React.Component { render() { return ( <a onClick={clickSpy} onDoubleClick={spy}>foo</a> ); } } const wrapper = mount(<Foo />); wrapper.simulate('dblclick'); expect(spy.calledOnce).to.equal(true); wrapper.simulate('click'); expect(clickSpy.calledOnce).to.equal(true); }); describeIf(!REACT013, 'normalizing mouseenter', () => { it('should convert lowercase events to React camelcase', () => { const spy = sinon.spy(); class Foo extends React.Component { render() { return ( <a onMouseEnter={spy}>foo</a> ); } } const wrapper = mount(<Foo />); wrapper.simulate('mouseenter'); expect(spy.calledOnce).to.equal(true); }); }); }); describeIf(!REACT013, 'stateless function component', () => { it('should pass in event data', () => { const spy = sinon.spy(); const Foo = () => ( <a onClick={spy}>foo</a> ); const wrapper = mount(<Foo />); wrapper.simulate('click', { someSpecialData: 'foo' }); expect(spy.calledOnce).to.equal(true); expect(spy.args[0][0].someSpecialData).to.equal('foo'); }); }); }); describe('.setState(newState)', () => { it('should set the state of the root node', () => { class Foo extends React.Component { constructor(props) { super(props); this.state = { id: 'foo' }; } render() { return ( <div className={this.state.id} /> ); } } const wrapper = mount(<Foo />); expect(wrapper.find('.foo').length).to.equal(1); wrapper.setState({ id: 'bar' }); expect(wrapper.find('.bar').length).to.equal(1); }); it('allows setState inside of componentDidMount', () => { // NOTE: this test is a test to ensure that the following issue is // fixed: https://github.com/airbnb/enzyme/issues/27 class MySharona extends React.Component { constructor(props) { super(props); this.state = { mounted: false }; } componentDidMount() { this.setState({ mounted: true }); } render() { return <div>{this.state.mounted ? 'a' : 'b'}</div>; } } const wrapper = mount(<MySharona />); expect(wrapper.find('div').text()).to.equal('a'); }); }); describe('.is(selector)', () => { it('should return true when selector matches current element', () => { const wrapper = mount(<div className="foo bar baz" />); expect(wrapper.is('.foo')).to.equal(true); }); it('should allow for compound selectors', () => { const wrapper = mount(<div className="foo bar baz" />); expect(wrapper.is('.foo.bar')).to.equal(true); }); it('should ignore insignificant whitespace', () => { const className = ` foo `; const wrapper = mount(<div className={className} />); expect(wrapper.is('.foo')).to.equal(true); }); it('should handle all significant whitespace', () => { const className = `foo bar baz`; const wrapper = mount(<div className={className} />); expect(wrapper.is('.foo.bar.baz')).to.equal(true); }); it('should return false when selector does not match', () => { const wrapper = mount(<div className="bar baz" />); expect(wrapper.is('.foo')).to.equal(false); }); }); describe('.isEmptyRender()', () => { const emptyRenderValues = generateEmptyRenderData(); itWithData(emptyRenderValues, 'when a React class component returns: ', (data) => { const Foo = React.createClass({ render() { return data.value; }, }); const wrapper = mount(<Foo />); expect(wrapper.isEmptyRender()).to.equal(data.expectResponse); }); itWithData(emptyRenderValues, 'when an ES2015 class component returns: ', (data) => { class Foo extends React.Component { render() { return data.value; } } const wrapper = mount(<Foo />); expect(wrapper.isEmptyRender()).to.equal(data.expectResponse); }); it('should not return true for HTML elements', () => { const wrapper = mount(<div className="bar baz" />); expect(wrapper.isEmptyRender()).to.equal(false); }); describeIf(REACT15, 'stateless function components', () => { itWithData(emptyRenderValues, 'when a component returns: ', (data) => { function Foo() { return data.value; } const wrapper = mount(<Foo />); expect(wrapper.isEmptyRender()).to.equal(data.expectResponse); }); }); }); describe('.not(selector)', () => { it('filters to things not matching a selector', () => { const wrapper = mount( <div> <div className="foo bar baz" /> <div className="foo" /> <div className="bar baz" /> <div className="baz" /> <div className="foo bar" /> </div> ); expect(wrapper.find('.foo').not('.bar').length).to.equal(1); expect(wrapper.find('.baz').not('.foo').length).to.equal(2); expect(wrapper.find('.foo').not('div').length).to.equal(0); }); }); describe('.filter(selector)', () => { it('should return a new wrapper of just the nodes that matched the selector', () => { const wrapper = mount( <div> <div className="foo bar baz" /> <div className="foo" /> <div className="bar baz"> <div className="foo bar baz" /> <div className="foo" /> </div> <div className="baz" /> <div className="foo bar" /> </div> ); expect(wrapper.find('.foo').filter('.bar').length).to.equal(3); expect(wrapper.find('.bar').filter('.foo').length).to.equal(3); expect(wrapper.find('.bar').filter('.bax').length).to.equal(0); expect(wrapper.find('.foo').filter('.baz.bar').length).to.equal(2); }); it('should only look in the current wrappers nodes, not their children', () => { const wrapper = mount( <div> <div className="foo"> <div className="bar" /> </div> <div className="foo bar" /> </div> ); expect(wrapper.find('.foo').filter('.bar').length).to.equal(1); }); }); describe('.filterWhere(predicate)', () => { it('should filter only the nodes of the wrapper', () => { const wrapper = mount( <div> <div className="foo bar" /> <div className="foo baz" /> <div className="foo bux" /> </div> ); const stub = sinon.stub(); stub.onCall(0).returns(false); stub.onCall(1).returns(true); stub.onCall(2).returns(false); const baz = wrapper.find('.foo').filterWhere(stub); expect(baz.length).to.equal(1); expect(baz.hasClass('baz')).to.equal(true); }); it('should call the predicate with the wrapper as the first argument', () => { const wrapper = mount( <div> <div className="foo bar" /> <div className="foo baz" /> <div className="foo bux" /> </div> ); const stub = sinon.stub(); stub.returns(true); const spy = sinon.spy(stub); wrapper.find('.foo').filterWhere(spy); expect(spy.callCount).to.equal(3); expect(spy.args[0][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[1][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[2][0]).to.be.instanceOf(ReactWrapper); expect(spy.args[0][0].hasClass('bar')).to.equal(true); expect(spy.args[1][0].hasClass('baz')).to.equal(true); expect(spy.args[2][0].hasClass('bux')).to.equal(true); }); }); describe('.text()', () => { const matchesRender = function matchesRender(node) { const actual = mount(node).text(); const expected = render(node).text(); expect(expected).to.equal(actual); }; it('should handle simple text nodes', () => { const wrapper = mount( <div>some text</div> ); expect(wrapper.text()).to.equal('some text'); }); it('should handle nodes with mapped children', () => { class Foo extends React.Component { render() { return ( <div> {this.props.items.map(x => x)} </div> ); } } matchesRender(<Foo items={['abc', 'def', 'hij']} />); matchesRender( <Foo items={[ <i key={1}>abc</i>, <i key={2}>def</i>, <i key={3}>hij</i>, ]} /> ); }); it('should render composite components smartly', () => { class Foo extends React.Component { render() { return <div>foo</div>; } } const wrapper = mount( <div> <Foo /> <div>test</div> </div> ); expect(wrapper.text()).to.equal('footest'); }); it('should handle html entities', () => { matchesRender(<div>&gt;</div>); }); describeIf(!REACT013, 'stateless function components', () => { it('should handle nodes with mapped children', () => { const Foo = (props) => ( <div>{props.items.map(x => x)}</div> ); matchesRender(<Foo items={['abc', 'def', 'hij']} />); matchesRender( <Foo items={[ <i key={1}>abc</i>, <i key={2}>def</i>, <i key={3}>hij</i>, ]} /> ); }); it('should render composite components smartly', () => { const Foo = () => ( <div>foo</div> ); const wrapper = mount( <div> <Foo /> <div>test</div> </div> ); expect(wrapper.text()).to.equal('footest'); }); }); }); describe('.props()', () => { it('should return the props object', () => { const fn = () => ({}); const wrapper = mount( <div id="fooId" className="bax" onClick={fn} > <div className="baz" /> <div className="foo" /> </div> ); expect(wrapper.props().className).to.equal('bax'); expect(wrapper.props().onClick).to.equal(fn); expect(wrapper.props().id).to.equal('fooId'); }); it('should be allowed to be used on an inner node', () => { const fn = () => ({}); const wrapper = mount( <div className="bax"> <div className="baz" onClick={fn} /> <div className="foo" id="fooId" /> </div> ); expect(wrapper.find('.baz').props().onClick).to.equal(fn); expect(wrapper.find('.foo').props().id).to.equal('fooId'); }); it('called on root should return props of root node', () => { class Foo extends React.Component { render() { return ( <div className={this.props.bar} id={this.props.foo} /> ); } } const wrapper = mount(<Foo foo="hi" bar="bye" />); expect(wrapper.props()).to.eql({ bar: 'bye', foo: 'hi' }); }); describeIf(!REACT013, 'stateless function components', () => { it('called on root should return props of root node', () => { const Foo = ({ bar, foo }) => ( <div className={bar} id={foo} /> ); const wrapper = mount(<Foo foo="hi" bar="bye" />); expect(wrapper.props()).to.eql({ bar: 'bye', foo: 'hi' }); }); }); }); describe('.prop(name)', () => { it('should return the props of key `name`', () => { const fn = () => ({}); const wrapper = mount( <div id="fooId" className="bax" onClick={fn} > <div className="baz" /> <div className="foo" /> </div> ); expect(wrapper.prop('className')).to.equal('bax'); expect(wrapper.prop('onClick')).to.equal(fn); expect(wrapper.prop('id')).to.equal('fooId'); }); it('should be allowed to be used on an inner node', () => { const fn = () => ({}); const wrapper = mount( <div className="bax"> <div className="baz" onClick={fn} /> <div className="foo" id="fooId" /> </div> ); expect(wrapper.find('.baz').prop('onClick')).to.equal(fn); expect(wrapper.find('.foo').prop('id')).to.equal('fooId'); }); it('should return props of root rendered node', () => { class Foo extends React.Component { render() { return ( <div className={this.props.bar} id={this.props.foo} /> ); } } const wrapper = mount(<Foo foo="hi" bar="bye" />); expect(wrapper.prop('className')).to.equal(undefined); expect(wrapper.prop('id')).to.equal(undefined); expect(wrapper.prop('foo')).to.equal('hi'); expect(wrapper.prop('bar')).to.equal('bye'); }); describeIf(!REACT013, 'stateless function components', () => { it('should return props of root rendered node', () => { const Foo = ({ bar, foo }) => ( <div className={bar} id={foo} /> ); const wrapper = mount(<Foo foo="hi" bar="bye" />); expect(wrapper.prop('className')).to.equal(undefined); expect(wrapper.prop('id')).to.equal(undefined); expect(wrapper.prop('foo')).to.equal('hi'); expect(wrapper.prop('bar')).to.equal('bye'); }); }); }); describe('.state(name)', () => { it('should return the state object', () => { class Foo extends React.Component { constructor(props) { super(props); this.state = { foo: 'foo' }; } render() { return <div />; } } const wrapper = mount(<Foo />); expect(wrapper.state()).to.eql({ foo: 'foo' }); }); it('should return the current state after state transitions', () => { class Foo extends React.Component { constructor(props) { super(props); this.state = { foo: 'foo' }; } render() { return <div />; } } const wrapper = mount(<Foo />); wrapper.setState({ foo: 'bar' }); expect(wrapper.state()).to.eql({ foo: 'bar' }); }); it('should allow a state property name be passed in as an argument', () => { class Foo extends React.Component { constructor(props) { super(props); this.state = { foo: 'foo' }; } render() { return <div />; } } const wrapper = mount(<Foo />); expect(wrapper.state('foo')).to.equal('foo'); }); }); describe('.children([selector])', () => { it('should return empty wrapper