diff --git a/package.json b/package.json index a7ae60dd3..09051cb98 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "version": "npm run build", "clean": "rimraf build", "lint": "eslint src/**", - "test": "mocha --compilers js:babel-core/register --recursive src/**/__tests__/*.js", + "test": "mocha --compilers js:babel-core/register --recursive withDom.js src/**/__tests__/*.js", "check": "npm run lint && npm run test:all", "build": "babel src --out-dir build", - "test:watch": "mocha --compilers js:babel-core/register --recursive src/**/__tests__/*.js --watch", - "test:only": "mocha --compilers js:babel-core/register --watch", + "test:only": "mocha --compilers js:babel-core/register --watch withDom.js", + "test:watch": "mocha --compilers js:babel-core/register --recursive withDom.js src/**/__tests__/*.js --watch", "test:describeWithDOMOnly": "mocha --compilers js:babel-core/register --recursive src/**/__tests__/describeWithDOM/describeWithDOMOnly-spec.js", "test:describeWithDOMSkip": "mocha --compilers js:babel-core/register --recursive src/**/__tests__/describeWithDOM/describeWithDOMSkip-spec.js", "test:all": "npm run react:13 && npm test && npm run test:describeWithDOMOnly && npm run test:describeWithDOMSkip && npm run react:14 && npm test && npm run test:describeWithDOMOnly && npm run test:describeWithDOMSkip", diff --git a/src/ReactWrapper.js b/src/ReactWrapper.js index 071edc814..7eac39949 100644 --- a/src/ReactWrapper.js +++ b/src/ReactWrapper.js @@ -137,6 +137,26 @@ export default class ReactWrapper { return this; } + unmount() { + if (this.root !== this) { + throw new Error('ReactWrapper::unmount() can only be called on the root'); + } + this.single(() => { + this.component.setState({ mount: false }); + }); + return this; + } + + mount() { + if (this.root !== this) { + throw new Error('ReactWrapper::mount() can only be called on the root'); + } + this.single(() => { + this.component.setState({ mount: true }); + }); + return this; + } + /** * A method that sets the props of the root component, and re-renders. Useful for when you are * wanting to test how the component behaves over time with changing props. Calling this, for diff --git a/src/ReactWrapperComponent.jsx b/src/ReactWrapperComponent.jsx index 590689eb6..520f5201d 100644 --- a/src/ReactWrapperComponent.jsx +++ b/src/ReactWrapperComponent.jsx @@ -26,6 +26,7 @@ export default function createWrapperComponent(node, options = {}) { getInitialState() { return { + mount: true, props: this.props.props, context: this.props.context, }; @@ -62,8 +63,10 @@ export default function createWrapperComponent(node, options = {}) { render() { const { Component } = this.props; + const { mount, props } = this.state; + if (!mount) return null; return ( - + ); }, }; diff --git a/src/__tests__/ReactWrapper-spec.js b/src/__tests__/ReactWrapper-spec.js index 0ff8df3d4..af70bf000 100644 --- a/src/__tests__/ReactWrapper-spec.js +++ b/src/__tests__/ReactWrapper-spec.js @@ -1,3 +1,4 @@ +import { describeWithDOM, describeIf } from './_helpers'; import React from 'react'; import { expect } from 'chai'; import sinon from 'sinon'; @@ -5,9 +6,7 @@ import { mount, render, ReactWrapper, - describeWithDOM, } from '../'; -import { describeIf } from './_helpers'; import { REACT013 } from '../version'; describeWithDOM('mount', () => { @@ -490,6 +489,68 @@ describeWithDOM('mount', () => { }); }); + + 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 ( +
+ {this.props.id} +
+ ); + } + } + const wrapper = mount(); + 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 ( +
+ {this.props.id} +
+ ); + } + } + const wrapper = mount(); + expect(spy.calledOnce).to.equal(false); + wrapper.unmount(); + expect(spy.calledOnce).to.equal(true); + }); + + }); + describe('.simulate(eventName, data)', () => { it('should simulate events', () => { diff --git a/src/__tests__/Utils-spec.js b/src/__tests__/Utils-spec.js index 229829b92..06f45ea66 100644 --- a/src/__tests__/Utils-spec.js +++ b/src/__tests__/Utils-spec.js @@ -1,4 +1,5 @@ -import React from 'react/addons'; +import { describeWithDOM } from './_helpers.js'; +import React from 'react'; import { expect } from 'chai'; import sinon from 'sinon'; import { @@ -12,10 +13,7 @@ import { selectorType, mapNativeEventNames, } from '../Utils'; -import { - describeWithDOM, - mount, -} from '../'; +import { mount } from '../'; describe('Utils', () => { diff --git a/src/__tests__/_helpers.js b/src/__tests__/_helpers.js index 91d619dcb..12f00361b 100644 --- a/src/__tests__/_helpers.js +++ b/src/__tests__/_helpers.js @@ -1,3 +1,14 @@ +export function describeWithDOM(a, b) { + describe('(uses jsdom)', () => { + if (global.document) { + describe(a, b); + } else { + // if jsdom isn't available, skip every test in this describe context + describe.skip(a, b); + } + }); +} + /** * Simple wrapper around mocha describe which allows a boolean to be passed in first which * determines whether or not the test will be run diff --git a/withDom.js b/withDom.js new file mode 100644 index 000000000..19b49d36b --- /dev/null +++ b/withDom.js @@ -0,0 +1,22 @@ +if (!global.document) { + try { + const jsdom = require('jsdom').jsdom; // could throw + + const exposedProperties = ['window', 'navigator', 'document']; + + global.document = jsdom(''); + global.window = document.defaultView; + Object.keys(document.defaultView).forEach((property) => { + if (typeof global[property] === 'undefined') { + exposedProperties.push(property); + global[property] = document.defaultView[property]; + } + }); + + global.navigator = { + userAgent: 'node.js', + }; + } catch (e) { + // jsdom is not supported... + } +}