From 0b5374a447c06ae7b908a44e71d9941e719ba128 Mon Sep 17 00:00:00 2001 From: Leland Richardson Date: Sun, 6 Dec 2015 15:38:17 -0800 Subject: [PATCH] Rip out sinon, mocha, and jsdom dependencies This refactor addresses several issues regarding enzyme working in a variety of environments, and the library just generally not making any assumptions about the environment that tests will be run in. For the most part, this amounts to: - remove direct dependency on jsdom - remove direct dependency on sinon - remove assumed dependency on mocha In addition to this, I would like to create several "example" projects that are some basic boilerplate to getting enzyme up and running with some combination of bundler/test runner/etc. These projects can end up being devDependencies to enzyme and we can run their tests as part of our tests, which will ensure that changes we make to enzyme will be compatible with environments we claim to support moving forward. Lastly, as a matter of organization, tests have been moved from `src/__tests__/*` to `tests/*`. Left to do for this to be mergable is: [ ] Add a "guides" section in the docs explaining how to use enzyme in different environments [ ] Add example projects as dev dependencies, include their tests in enzyme's test script --- .travis.yml | 24 ++--- README.md | 48 ++++++--- docs/README.md | 9 +- docs/api/mount.md | 7 +- docs/future.md | 13 +-- docs/guides.md | 9 ++ docs/guides/browserify.md | 50 ++++++++++ docs/guides/jest.md | 32 ++++++ docs/guides/jsdom.md | 98 +++++++++++++++++++ docs/guides/karma.md | 64 ++++++++++++ docs/guides/mocha.md | 29 ++++++ docs/guides/react-native.md | 45 +++++++++ docs/guides/webpack.md | 50 ++++++++++ docs/installation/jsdom.md | 57 ----------- example-test.sh | 6 ++ package.json | 28 +++--- src/Utils.js | 26 ----- src/__tests__/_helpers.js | 34 ------- .../describeWithDOMOnly-spec.js | 19 ---- .../describeWithDOMSkip-spec.js | 19 ---- src/index.js | 46 --------- {src/__tests__ => test}/.eslintrc | 0 {src/__tests__ => test}/Debug-spec.js | 6 +- {src/__tests__ => test}/ReactWrapper-spec.js | 6 +- .../ShallowTraversal-spec.js | 4 +- .../__tests__ => test}/ShallowWrapper-spec.js | 4 +- {src/__tests__ => test}/Utils-spec.js | 28 +----- src/describeWithDOM.js => test/_helpers.js | 43 +++++--- 28 files changed, 501 insertions(+), 303 deletions(-) create mode 100644 docs/guides.md create mode 100644 docs/guides/browserify.md create mode 100644 docs/guides/jest.md create mode 100644 docs/guides/jsdom.md create mode 100644 docs/guides/karma.md create mode 100644 docs/guides/mocha.md create mode 100644 docs/guides/react-native.md create mode 100644 docs/guides/webpack.md delete mode 100644 docs/installation/jsdom.md create mode 100644 example-test.sh delete mode 100644 src/__tests__/_helpers.js delete mode 100644 src/__tests__/describeWithDOM/describeWithDOMOnly-spec.js delete mode 100644 src/__tests__/describeWithDOM/describeWithDOMSkip-spec.js rename {src/__tests__ => test}/.eslintrc (100%) rename {src/__tests__ => test}/Debug-spec.js (98%) rename {src/__tests__ => test}/ReactWrapper-spec.js (99%) rename {src/__tests__ => test}/ShallowTraversal-spec.js (99%) rename {src/__tests__ => test}/ShallowWrapper-spec.js (99%) rename {src/__tests__ => test}/Utils-spec.js (88%) rename src/describeWithDOM.js => test/_helpers.js (60%) diff --git a/.travis.yml b/.travis.yml index 9f22ae267..5eca9c613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,26 @@ language: node_js node_js: - - "5.1" - - "5.0" - - "4.2" - - "4.1" - - "4.0" - - "iojs-v3.3" + - "5" + - "4" + - "iojs" - "0.12" - - "0.11" - "0.10" before_install: - 'if [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then case "$(npm --version)" in 1.*) npm install -g npm@1.4.28 ;; 2.*) npm install -g npm@2 ;; esac ; fi' - 'if [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then npm install -g npm; fi' before_script: "sh install-relevant-react.sh" script: - - 'if [ "${TRAVIS_NODE_VERSION}" = "4.2" ]; then npm run lint && npm run travis ; elif [ "${TRAVIS_NODE_VERSION}" = "0.12" ]; then npm run travis ; else npm test ; fi' + - 'if [ -z "$REACT" ] && [ "${TRAVIS_NODE_VERSION}" = "4" ]; then npm run test:env -- "${EXAMPLE}" ; elif [ -z "$REACT" ]; then echo "Test Skipped" ; elif [ "${TRAVIS_NODE_VERSION}" = "4" ]; then npm run lint && npm run travis ; elif [ "${TRAVIS_NODE_VERSION}" = "0.12" ]; then npm run travis ; else npm test ; fi' after_script: - - 'if [ "${TRAVIS_NODE_VERSION}" = "4.2" ] || [ "${TRAVIS_NODE_VERSION}" = "0.12" ]; then cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' + - 'if [ "${TRAVIS_NODE_VERSION}" = "4" ] || [ "${TRAVIS_NODE_VERSION}" = "0.12" ]; then cat ./coverage/lcov.info | ./node_modules/.bin/coveralls ; fi' env: - REACT=0.13 - REACT=0.14 + - EXAMPLE=mocha + - EXAMPLE=karma + - EXAMPLE=react-native + - EXAMPLE=karma-webpack + - EXAMPLE=jest sudo: false matrix: fast_finish: true - allow_failures: - - node_js: "5.0" - - node_js: "4.1" - - node_js: "4.0" - - node_js: "0.11" diff --git a/README.md b/README.md index 93cfa1c3a..d22060ecf 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,16 @@ If you are interested in using enzyme with custom Chai.js assertions and conveni testing your React components, you can consider using [chai-enzyme](https://github.com/producthunt/chai-enzyme). +[Using Enzyme with Mocha](/docs/guides/mocha.md) +[Using Enzyme with Karma](/docs/guides/karma.md) +[Using Enzyme with Jasmine](/docs/guides/jasmine.md) +[Using Enzyme with Browserify](/docs/guides/browserify.md) +[Using Enzyme with WebPack](/docs/guides/webpack.md) +[Using Enzyme with JSDOM](/docs/guides/jsdom.md) +[Using Enzyme with React Native](/docs/guides/react-native.md) +[Using Enzyme with Jest](/docs/guides/jest.md) + + ### [Installation](/docs/installation/README.md) To get started with enzyme, you can simply install it with npm: @@ -46,6 +56,7 @@ Basic Usage ## [Shallow Rendering](/docs/api/shallow.md) ```jsx +import React from 'react'; import { shallow } from 'enzyme'; import sinon from 'sinon'; @@ -86,22 +97,14 @@ Read the full [API Documentation](/docs/api/shallow.md) -## [JSDOM Full Rendering](/docs/api/mount.md) +## [Full DOM Rendering](/docs/api/mount.md) ```jsx -import { - describeWithDOM, - mount, - spyLifecycle, -} from 'enzyme'; - -describeWithDOM('', () => { +import React from 'react'; +import sinon from 'sinon'; +import { mount } from 'enzyme'; - it('calls componentDidMount', () => { - spyLifecycle(Foo); - const wrapper = mount(); - expect(Foo.prototype.componentDidMount.calledOnce).to.equal(true); - }); +describe('', () => { it('allows us to set props', () => { const wrapper = mount(); @@ -118,6 +121,13 @@ describeWithDOM('', () => { wrapper.find('button').simulate('click'); expect(onButtonClick.calledOnce).to.equal(true); }); + + it('calls componentDidMount', () => { + sinon.spy(Foo.prototype, 'componentDidMount'); + const wrapper = mount(); + expect(Foo.prototype.componentDidMount.calledOnce).to.be.true; + Foo.prototype.componentDidMount.restore(); + }); }); ``` @@ -128,6 +138,7 @@ Read the full [API Documentation](/docs/api/mount.md) ## [Static Rendered Markup](/docs/api/render.md) ```jsx +import React from 'react'; import { render } from 'enzyme'; describe('', () => { @@ -148,9 +159,16 @@ describe('', () => { Read the full [API Documentation](/docs/api/render.md) -### [Future](/docs/future.md) +### Future + +[Enzyme Future](/docs/future.md) + + +### Contributing + +See the [Contributors Guide](/CONTRIBUTING.md) + -### [Contributing](/CONTRIBUTING.md) ### License diff --git a/docs/README.md b/docs/README.md index b76cfd393..9607f62f7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,17 @@ ## Table of Contents * [Introduction](/README.md) +* [Guides](/docs/guides.md) + * [Browserify](/docs/guides/browserify.md) + * [WebPack](/docs/guides/webpack.md) + * [JSDOM](/docs/guides/jsdom.md) + * [Jest](/docs/guides/jest.md) + * [Karma](/docs/guides/karma.md) + * [Mocha](/docs/guides/mocha.md) + * [React Native](/docs/guides/react-native.md) * [Installation](/docs/installation/README.md) * [Working with React 0.13.x](/docs/installation/react-013.md) * [Working with React 0.14.x](/docs/installation/react-014.md) - * [Working with jsDom](/docs/installation/jsdom.md) * [API Reference](/docs/api/README.md) * [Shallow Rendering](/docs/api/shallow.md) * [find(selector)](/docs/api/ShallowWrapper/find.md) diff --git a/docs/api/mount.md b/docs/api/mount.md index 8a6ae6468..2086347c4 100644 --- a/docs/api/mount.md +++ b/docs/api/mount.md @@ -4,8 +4,11 @@ Full DOM rendering is ideal for use cases where you have components that may int or may require the full lifecycle in order to fully test the component (ie, `componentDidMount` etc.) -Full DOM rendering depends on a library called [jsdom](https://github.com/tmpvar/jsdom) which is -essentially a headless browser implemented completely in JS. +Full DOM rendering requires that a full DOM API be available at the global scope. This means that +it must be run in an environment that at least "looks like" a browser environment. If you do not +want to run your tests inside of a browser, the recommended approach to using `mount` is to depend +on a library called [jsdom](https://github.com/tmpvar/jsdom) which is essentially a headless browser +implemented completely in JS. ```jsx import { mount } from 'enzyme'; diff --git a/docs/future.md b/docs/future.md index 10ba45d2a..7258d1e6f 100644 --- a/docs/future.md +++ b/docs/future.md @@ -7,12 +7,6 @@ There are several things we'd like to address with Enzyme that often get asked. of projects that we plan on addressing in the near future: -#### React Native Support - -We would like to have enzyme provide full support for react components created with React Native, -and potentially some RN-specific methods to facilitate testing there. - - #### Improved CSS Selector Support Currently, "hierarchical" CSS selectors are not supported in Enzyme. That means that the only CSS @@ -25,3 +19,10 @@ we would like to provide a more complete subset of all valid CSS selectors. Event simulation is limited for Shallow rendering. Event propagation is not supported, and one must supply their own event objects. We would like to provide tools to more fully simulate real interaction. + + +### Improved Keyboard + Mouse Simulation + +Many react components involve simulating form input or complex mouse interaction. Simulating this +using the event simulation API that Enzyme provides is cumbersome and impractical. We are looking for +an expressive way to solve this problem, even if it is a library that lives outside of enzyme. diff --git a/docs/guides.md b/docs/guides.md new file mode 100644 index 000000000..c53e8eed0 --- /dev/null +++ b/docs/guides.md @@ -0,0 +1,9 @@ +# Enzyme Guides + +- [*Using Enzyme with Browserify*](guides/browserify.md) +- [*Using Enzyme with WebPack*](guides/webpack.md) +- [*Using Enzyme with JSDOM*](guides/jsdom.md) +- [*Using Enzyme with React Native*](guides/react-native.md) +- [*Using Enzyme with Jest*](guides/jest.md) +- [*Using Enzyme with Karma*](guides/karma.md) +- [*Using Enzyme with Mocha*](guides/mocha.md) diff --git a/docs/guides/browserify.md b/docs/guides/browserify.md new file mode 100644 index 000000000..d55989f02 --- /dev/null +++ b/docs/guides/browserify.md @@ -0,0 +1,50 @@ +# Using Enzyme with Browserify + +If you are using a test runner that runs code in a browser-based environment, you may be using +[browserify]() in order to bundle your React code. + +Browserify uses static analysis to create a dependency graph at build-time of your source code to +build a bundle. Enzyme has a hand full of conditional `require()` calls in it in order to remain +compatible with React 0.13 and React 0.14. + +Unfortunately, these conditional requires mean there is a bit of extra setup with bundlers like +browserify. + +In your browserify configuration, you simply need to make sure that the following two files are +labeled as "external", which means they will be ignored: + +``` +react/lib/ReactContext +react/lib/ExecutionEnvironment +``` + +Here is an example piece of configuration code marking these as external: + +```js +var browserify = require('browserify'); + +var b = browserify(); + +// make sure to mark these as external! +b.external('react/lib/ReactContext'); +b.external('react/lib/ExecutionEnvironment'); + +// the rest of your browserify configuration +``` + + +## React 0.13 Compatibility + +If you are using React 0.13, the instructions above will be the same but with a different list of +externals: + +``` +react-dom +react-dom/server +react-addons-test-utils +``` + + +## Example Projects + +- [enzyme-example-karma](https://github.com/lelandrichardson/enzyme-example-karma) diff --git a/docs/guides/jest.md b/docs/guides/jest.md new file mode 100644 index 000000000..4637deafd --- /dev/null +++ b/docs/guides/jest.md @@ -0,0 +1,32 @@ +# Using Jest with Enzyme + +If you are using Jest with enzyme and using Jest's "automocking" feature, you will need to mark +several modules to be unmocked in your `package.json`: + +```js +/* package.json */ + +"jest": { + "scriptPreprocessor": "/node_modules/babel-jest", + "unmockedModulePathPatterns": [ + "react", + "react-dom", + "react-addons-test-utils", + "fbjs", + "enzyme", + "cheerio", + "htmlparser2", + "underscore", + "lodash", + "domhandler", + "object.assign", + "define-properties", + "function-bind", + "object-keys" + ] +} +``` + +## Example Projects + +- [enzyme-example-ject](https://github.com/lelandrichardson/enzyme-example-jest) diff --git a/docs/guides/jsdom.md b/docs/guides/jsdom.md new file mode 100644 index 000000000..4d43e478f --- /dev/null +++ b/docs/guides/jsdom.md @@ -0,0 +1,98 @@ +# Using Enzyme with JSDOM + +JSDOM is a JavaScript based headless browser that can be used to create a realistic testing environment. + +Since enzyme's [`mount`](../api/mount.md) API requires a DOM, JSDOM is required in order to use +`mount` if you are not already in a browser environment (ie, a Node environment). + +For the best experience with enzyme, it is recommended that you load a document into the global +scope *before* requiring React for the first time. It is very important that the below script +gets run *before* React's code is run. + +As a result, a standalone script like the one below is generally a good approach: + +```js +/* setup.js */ + +var jsdom = require('jsdom').jsdom; + +var 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' +}; + +documentRef = document; +``` + + +## `describeWithDOM` API and clearing the document after every test + +In previous versions of enzyme, there was a public `describeWithDOM` API which loaded in a new +JSDOM documnent into the global namespace before every test, ensuring that tests were deterministic +and did not have side-effects. + +This approach is no longer recommended. React's source code makes several assumptions about the +environment it is running in, and one of them is that the `global.document` that is found at +"require time" is going to be the one and only document it ever needs to worry about. As a result, +this type of "reloading" ends up causing more pain than it prevents. + +It is important, however, to make sure that your tests using the global DOM APIs do not have leaky +side-effects which could change the results of other tests. Until there is a better option, this is +left to you to ensure. + + +## JSDOM + Mocha + +When testing with JSDOM, the `setup.js` file above needs to be run before the test suite runs. If +you are using mocha, this can be done from the command line using the `--require` option: + +```bash +mocha --require setup.js --recursive path/to/test/dir +``` + + +## Node.js Compatibility + +Jsdom requires node 4 or above. As a result, if you want to use it with `mount`, you will need to +make sure node 4 or iojs is on your machine. If you are stuck using an older version of Node, you +may want to try using a browser-based test runner such as [Karma](../guides/karma.md). + + +### Switching between node versions + +Some times you may need to switch between different versions of node, you can use a CLI tool called +`nvm` to quickly switch between node versions. + +To install NVM: + +```bash +brew install nvm +nvm install 4 +``` + +Now your machine will be running Node 4. You can use the `nvm use` command to switch between the two +environments: + +```bash +nvm use 0.12 +``` + +```bash +nvm use 4 +``` + + + +## Example Projects + +- [enzyme-example-mocha](https://github.com/lelandrichardson/enzyme-example-mocha) diff --git a/docs/guides/karma.md b/docs/guides/karma.md new file mode 100644 index 000000000..9b3cfe7b3 --- /dev/null +++ b/docs/guides/karma.md @@ -0,0 +1,64 @@ +# Using Enzyme with Karma + +Karma is a popular test runner that can run tests in browser environments. Enzyme is compatible with +enzyme, but often requires a little bit of configuration. + +This configuration largely depends on which plugins you are using to bundle your JavaScript code. In +the case of Browserify or Webpack, see the below documentation in order to get these up and running. + + +## Enzyme + Karma + Webpack + +See the [webpack guide](webpack.md). + +```js +/* karma.conf.js */ +browserify: { + debug: true, + transform: [ + ['babelify', { presets: ['airbnb'] }] + ], + configure: function(bundle) { + bundle.on('prebundle', function() { + bundle.external('react/lib/ReactContext'); + bundle.external('react/lib/ExecutionEnvironment'); + }); + } +}, +``` + +## Enzyme + Karma + Browserify + +See the [browserify guide](browserify.md). + + +```js +/* karma.conf.js */ + +webpack: { //kind of a copy of your webpack config + devtool: 'inline-source-map', //just do inline source maps instead of the default + module: { + loaders: [ + { + test: /\.js$/, + exclude: /\/node_modules\//, + loader: 'babel', + query: { + presets: ['airbnb'] + } + } + ] + }, + externals: { + 'cheerio': 'window', + 'react/lib/ExecutionEnvironment': true, + 'react/lib/ReactContext': true + } +}, +``` + + +## Example Projects + +- [enzyme-example-karma](https://github.com/lelandrichardson/enzyme-example-karma) +- [enzyme-example-karma-webpack](https://github.com/lelandrichardson/enzyme-example-karma-webpack) diff --git a/docs/guides/mocha.md b/docs/guides/mocha.md new file mode 100644 index 000000000..c4894501e --- /dev/null +++ b/docs/guides/mocha.md @@ -0,0 +1,29 @@ +# Using Enzyme with Mocha + +Enzyme was originally designed to work with Mocha, so getting it up and running with Mocha should +be no problem at all. Simply install it and start using it: + +```bash +npm i --save-dev enzyme +``` + +```jsx +import React from 'react'; +import { mount, shallow } from 'enzyme'; + +describe('', () => { + + it('calls componentDidMount', () => { + const wrapper = mount(); + expect(Foo.prototype.componentDidMount.calledOnce).to.equal(true); + }); + +}); + +``` + + +## Example Projects + +- [enzyme-example-mocha](https://github.com/lelandrichardson/enzyme-example-mocha) +- [enzyme-example-react-native](https://github.com/lelandrichardson/enzyme-example-react-native) diff --git a/docs/guides/react-native.md b/docs/guides/react-native.md new file mode 100644 index 000000000..e25dae9e5 --- /dev/null +++ b/docs/guides/react-native.md @@ -0,0 +1,45 @@ +# Using Enzyme to Test Components in React Native + +As of React 0.18, React Native uses React as a dependency rather than a forked version of the library, +which means it is now possible to use Enzyme's `shallow` with React Native components. + +Unfortunately, React Native has many environmental dependencies that can be hard to simulate without +a host device. + +This can be difficult when you want your test suite to run with typical Continuous Integration servers +such as Travis. + +A pure JS mock of React Native exists and can solve this problem in the majority of use cases. + +To install it, run: + +```bash +npm i --save-dev react-native-mock +``` + +Requiring or importing the `/mock` entry file of this project will input the mock `react-native` +export into the require cache, so that your application uses the mock instead. + +If you are using a test runner such as mocha, this means that you can use the `--require` flag +before you run your test suite, and enzyme should "just work": + + +### Mocha CLI + +```bash +mocha --require react-native-mock/mock --recursive path/to/test/dir +``` + +### In Code + +```js +/* file-that-runs-before-all-of-my-tests.js */ + +// This will mutate `react-native`'s require cache with `react-native-mock`'s. +require('react-native-mock/mock'); // <-- side-effects!!! +``` + + +## Example Projects + +- [enzyme-example-react-native](https://github.com/lelandrichardson/enzyme-example-react-native) diff --git a/docs/guides/webpack.md b/docs/guides/webpack.md new file mode 100644 index 000000000..a61ea7ba1 --- /dev/null +++ b/docs/guides/webpack.md @@ -0,0 +1,50 @@ +# Using Enzyme with Webpack + +If you are using a test runner that runs code in a browser-based environment, you may be using +[webpack]() in order to bundle your React code. + +Webpack uses static analysis to create a dependency graph at build-time of your source code to +build a bundle. Enzyme has a hand full of conditional `require()` calls in it in order to remain +compatible with React 0.13 and React 0.14. + +Unfortunately, these conditional requires mean there is a bit of extra setup with bundlers like +webpack. + +In your webpack configuration, you simply need to make sure that the following two files are +labeled as "external", which means they will be ignored: + +``` +cheerio +react/lib/ReactContext +react/lib/ExecutionEnvironment +``` + +Here is an example piece of configuration code marking these as external: + +```js +/* webpack.config.js */ +// ... +externals: { + 'cheerio': 'window', + 'react/lib/ExecutionEnvironment': true, + 'react/lib/ReactContext': true +} +// ... +``` + +## React 0.13 Compatibility + +If you are using React 0.13, the instructions above will be the same but with a different list of +externals: + +``` +cheerio +react-dom +react-dom/server +react-addons-test-utils +``` + + +## Example Projects + +- [enzyme-example-karma-webpack](https://github.com/lelandrichardson/enzyme-example-karma-webpack) diff --git a/docs/installation/jsdom.md b/docs/installation/jsdom.md deleted file mode 100644 index 2f89cbe27..000000000 --- a/docs/installation/jsdom.md +++ /dev/null @@ -1,57 +0,0 @@ -# Working with jsdom & `mount` - -If you plan on using `mount`, it requires jsdom. Jsdom requires node 4 or above. As a result, if -you want to use `mount`, you will need to make sure node 4 or iojs is on your machine. - - -### Switching between node versions - -Some times you may need to switch between different versions of node, you can use a CLI tool called -`nvm` to quickly switch between node versions. - -To install NVM: - -```bash -brew install nvm -nvm install 4 -``` - -Now your machine will be running Node 4. You can use the `nvm use` command to switch between the two -environments: - -```bash -nvm use 0.12 -``` - -```bash -nvm use 4 -``` - -### Preventing tests from failing on old versions - -If you are worried about tests not passing on versions of node that don't support jsdom, Enzyme -comes with a helper function to wrap your tests in a safety layer such that any tests written -inside of that function will be skipped if jsdom is not available. (Note that this is for mocha -only). - -```jsx -import { mount, shallow } from 'enzyme'; - -describe('MyComponent', () => { - describeWithDOM('interaction', () => { - // these tests will get skipped if jsdom is not available... - it('should do something', () => { - const wrapper = mount(); - // ... - }); - }); - describe('non-interaction', () => { - // these tests will always run - it('should do something', () => { - const wrapper = shallow(); - // ... - }); - }); -}); - -``` diff --git a/example-test.sh b/example-test.sh new file mode 100644 index 000000000..c1dc63a40 --- /dev/null +++ b/example-test.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cd node_modules/enzyme-example-$1 +npm install +npm i ../../ +npm test diff --git a/package.json b/package.json index a1a33530d..47446fb47 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,11 @@ "check": "npm run lint && npm run test:all", "build": "babel src --out-dir build", "test": "npm run lint && npm run test:only", - "test:only": "mocha --require withDom.js --compilers js:babel-core/register --recursive src/**/__tests__/*.js", + "test:only": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js", "test:single": "mocha --require withDom.js --compilers js:babel-core/register --watch", - "test:watch": "mocha --require withDom.js --compilers js:babel-core/register --recursive 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", + "test:watch": "mocha --require withDom.js --compilers js:babel-core/register --recursive test/*.js --watch", + "test:env": "sh ./example-test.sh", + "test:all": "npm run react:13 && npm test && npm run react:14 && npm test", "react:clean": "rimraf node_modules/react node_modules/react-dom node_modules/react-addons-test-utils", "react:13": "npm run react:clean && npm i react@0.13", "react:14": "npm run react:clean && npm i react@0.14 react-dom@0.14 react-addons-test-utils@0.14", @@ -27,7 +26,7 @@ "docs:build": "npm run docs:prepare && gitbook build", "docs:watch": "npm run docs:prepare && gitbook serve", "docs:publish": "npm run docs:clean && npm run docs:build && cd _book && git init && git commit --allow-empty -m 'update book' && git fetch https://github.com/airbnb/enzyme.git gh-pages && git checkout -b gh-pages && git add . && git commit -am 'update book' && git push https://github.com/airbnb/enzyme.git gh-pages --force", - "travis": "babel-istanbul cover --report html _mocha -- --recursive src/**/__tests__/*.js" + "travis": "babel-node ./node_modules/.bin/istanbul cover --report html _mocha -- --require withDom.js test --recursive" }, "repository": { "type": "git", @@ -54,31 +53,32 @@ "is-subset": "^0.1.1", "object.assign": "^4.0.3", "object.values": "^1.0.3", - "sinon": "^1.17.3", "underscore": "^1.8.3" }, "devDependencies": { "babel-cli": "^6.3.17", "babel-core": "^6.3.21", "babel-eslint": "^4.1.8", - "babel-istanbul": "^0.5.9", "babel-preset-airbnb": "^1.0.1", "babel-register": "^6.3.13", "chai": "^3.5.0", "coveralls": "^2.11.6", + "enzyme-example-jest": "^0.1.0", + "enzyme-example-karma": "^0.1.1", + "enzyme-example-karma-webpack": "^0.1.3", + "enzyme-example-mocha": "^0.1.0", + "enzyme-example-react-native": "^0.1.0", "eslint": "^1.10.3", "eslint-config-airbnb": "^4.0.0", "eslint-plugin-react": "^3.16.1", "gitbook-cli": "^1.0.1", - "istanbul": "^0.4.2", + "istanbul": "^1.0.0-alpha.2", + "jsdom": "^6.1.0", "mocha": "^2.4.5", - "rimraf": "^2.5.1" + "rimraf": "^2.5.1", + "sinon": "^1.15.4" }, "peerDependencies": { "react": "0.13.x || 0.14.x" - }, - "optionalDependencies": { - "jsdom": "^3.1.2 || ^5.6.1 || ^6.1.0 || ^7.2.2", - "mocha-jsdom": "^1.0.0" } } diff --git a/src/Utils.js b/src/Utils.js index 28ed699c4..11ba6e4d9 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -24,32 +24,6 @@ export function typeOfNode(node) { return node ? node.type : null; } -export function onPrototype(Component, lifecycle, method) { - const proto = Component.prototype; - Object.getOwnPropertyNames(proto).forEach((name) => { - if (typeof proto[name] !== 'function') return; - switch (name) { - case 'componentDidMount': - case 'componentWillMount': - case 'componentDidUnmount': - case 'componentWillUnmount': - case 'componentWillReceiveProps': - case 'componentDidUpdate': - case 'componentWillUpdate': - case 'shouldComponentUpdate': - case 'render': - if (lifecycle) lifecycle(proto, name); - break; - case 'constructor': - // don't spy on the constructor, even though it shows up in the prototype - break; - default: - if (method) method(proto, name); - break; - } - }); -} - export function getNode(node) { return isDOMComponent(node) ? findDOMNode(node) : node; } diff --git a/src/__tests__/_helpers.js b/src/__tests__/_helpers.js deleted file mode 100644 index 12f00361b..000000000 --- a/src/__tests__/_helpers.js +++ /dev/null @@ -1,34 +0,0 @@ -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 - */ -export function describeIf(test, a, b) { - if (test) { - describe(a, b); - } else { - describe.skip(a, b); - } -} - -/** - * Simple wrapper around mocha it which allows a boolean to be passed in first which - * determines whether or not the test will be run - */ -export function itIf(test, a, b) { - if (test) { - it(a, b); - } else { - it.skip(a, b); - } -} diff --git a/src/__tests__/describeWithDOM/describeWithDOMOnly-spec.js b/src/__tests__/describeWithDOM/describeWithDOMOnly-spec.js deleted file mode 100644 index 10a375bf2..000000000 --- a/src/__tests__/describeWithDOM/describeWithDOMOnly-spec.js +++ /dev/null @@ -1,19 +0,0 @@ -import { expect } from 'chai'; -import { describeWithDOM } from '../..'; - -describe('describeWithDOM', () => { - describe('.only()', () => { - describeWithDOM.only('will skip all tests not called with only', () => { - it('will run only this test', () => { - expect(true).to.equal(true); - }); - }); - - describeWithDOM('will not call other tests', () => { - it('will not run this test', () => { - // purposefully failing test that won't be called - expect(true).to.equal(false); - }); - }); - }); -}); diff --git a/src/__tests__/describeWithDOM/describeWithDOMSkip-spec.js b/src/__tests__/describeWithDOM/describeWithDOMSkip-spec.js deleted file mode 100644 index 6c4738fdc..000000000 --- a/src/__tests__/describeWithDOM/describeWithDOMSkip-spec.js +++ /dev/null @@ -1,19 +0,0 @@ -import { expect } from 'chai'; -import { describeWithDOM } from '../..'; - -describe('describeWithDOM', () => { - describe('.skip()', () => { - describeWithDOM.skip('will skip tests called with skip', () => { - it('will not run this test', () => { - // purposefully failing test that won't be run - expect(true).to.equal(false); - }); - }); - - describeWithDOM('will still call describeWithDOM tests without .skip', () => { - it('will run this test', () => { - expect(true).to.equal(true); - }); - }); - }); -}); diff --git a/src/index.js b/src/index.js index 8e9e261d5..5206344a2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,53 +1,8 @@ import cheerio from 'cheerio'; -import Sinon from 'sinon'; import ReactWrapper from './ReactWrapper'; import ShallowWrapper from './ShallowWrapper'; -import { describeWithDOM } from './describeWithDOM'; -import { onPrototype } from './Utils'; import { renderToStaticMarkup } from './react-compat'; -/** - * @class Enzyme - */ - -export let sinon = Sinon.sandbox.create(); - -export function useSetStateHack() { - let cleanup = false; - before(() => { - if (typeof global.document === 'undefined') { - cleanup = true; - global.document = {}; - } - }); - after(() => { - if (cleanup) { - delete global.document; - } - }); -} - -export function spySetup() { - sinon = Sinon.sandbox.create(); -} - -export function spyTearDown() { - sinon.restore(); -} - -export function useSinon() { - beforeEach(spySetup); - afterEach(spyTearDown); -} - -export function spyLifecycle(Component) { - onPrototype(Component, (proto, name) => sinon.spy(proto, name)); -} - -export function spyMethods(Component) { - onPrototype(Component, null, (proto, name) => sinon.spy(proto, name)); -} - /** * Mounts and renders a react component into the document and provides a testing wrapper around it. * @@ -87,4 +42,3 @@ export function render(node) { export { ShallowWrapper as ShallowWrapper }; export { ReactWrapper as ReactWrapper }; -export { describeWithDOM as describeWithDOM }; diff --git a/src/__tests__/.eslintrc b/test/.eslintrc similarity index 100% rename from src/__tests__/.eslintrc rename to test/.eslintrc diff --git a/src/__tests__/Debug-spec.js b/test/Debug-spec.js similarity index 98% rename from src/__tests__/Debug-spec.js rename to test/Debug-spec.js index fcfc56d19..fbeb0c16f 100644 --- a/src/__tests__/Debug-spec.js +++ b/test/Debug-spec.js @@ -4,10 +4,10 @@ import { spaces, indent, debugNode, -} from '../Debug'; -import { mount } from '../'; +} from '../src/Debug'; +import { mount } from '../src/'; import { describeWithDOM, itIf } from './_helpers'; -import { REACT013 } from '../version'; +import { REACT013 } from '../src/version'; describe('debug', () => { diff --git a/src/__tests__/ReactWrapper-spec.js b/test/ReactWrapper-spec.js similarity index 99% rename from src/__tests__/ReactWrapper-spec.js rename to test/ReactWrapper-spec.js index f322a79ea..75e6ed377 100644 --- a/src/__tests__/ReactWrapper-spec.js +++ b/test/ReactWrapper-spec.js @@ -1,13 +1,13 @@ import { describeWithDOM, describeIf } from './_helpers'; import React from 'react'; import { expect } from 'chai'; -import sinon from 'sinon'; import { mount, render, ReactWrapper, -} from '../'; -import { REACT013 } from '../version'; +} from '../src/'; +import sinon from 'sinon'; +import { REACT013 } from '../src/version'; describeWithDOM('mount', () => { diff --git a/src/__tests__/ShallowTraversal-spec.js b/test/ShallowTraversal-spec.js similarity index 99% rename from src/__tests__/ShallowTraversal-spec.js rename to test/ShallowTraversal-spec.js index 03f9036c3..bff1293ab 100644 --- a/src/__tests__/ShallowTraversal-spec.js +++ b/test/ShallowTraversal-spec.js @@ -3,13 +3,13 @@ import sinon from 'sinon'; import { expect } from 'chai'; import { splitSelector, -} from '../Utils'; +} from '../src/Utils'; import { hasClassName, nodeHasProperty, treeForEach, treeFilter, -} from '../ShallowTraversal'; +} from '../src/ShallowTraversal'; describe('ShallowTraversal', () => { diff --git a/src/__tests__/ShallowWrapper-spec.js b/test/ShallowWrapper-spec.js similarity index 99% rename from src/__tests__/ShallowWrapper-spec.js rename to test/ShallowWrapper-spec.js index f2b6bc959..9ecf1a08f 100644 --- a/src/__tests__/ShallowWrapper-spec.js +++ b/test/ShallowWrapper-spec.js @@ -1,9 +1,9 @@ import React from 'react'; import { expect } from 'chai'; -import { shallow, render, ShallowWrapper } from '../'; +import { shallow, render, ShallowWrapper } from '../src/'; import sinon from 'sinon'; import { describeIf } from './_helpers'; -import { REACT013 } from '../version'; +import { REACT013 } from '../src/version'; describe('shallow', () => { diff --git a/src/__tests__/Utils-spec.js b/test/Utils-spec.js similarity index 88% rename from src/__tests__/Utils-spec.js rename to test/Utils-spec.js index 06f45ea66..5f5d7716b 100644 --- a/src/__tests__/Utils-spec.js +++ b/test/Utils-spec.js @@ -1,7 +1,7 @@ import { describeWithDOM } from './_helpers.js'; import React from 'react'; import { expect } from 'chai'; -import sinon from 'sinon'; +import { mount } from '../src/'; import { coercePropValue, onPrototype, @@ -12,34 +12,10 @@ import { SELECTOR, selectorType, mapNativeEventNames, -} from '../Utils'; -import { mount } from '../'; +} from '../src/Utils'; describe('Utils', () => { - describe('onPrototype', () => { - class Foo { - a() {} - b() {} - componentDidUpdate() {} - } - - const lifecycleSpy = sinon.spy(); - const methodSpy = sinon.spy(); - - onPrototype(Foo, lifecycleSpy, methodSpy); - - expect(lifecycleSpy.callCount).to.equal(1); - expect(lifecycleSpy.args[0][0]).to.equal(Foo.prototype); - expect(lifecycleSpy.args[0][1]).to.equal('componentDidUpdate'); - - expect(methodSpy.callCount).to.equal(2); - expect(methodSpy.args[0][0]).to.equal(Foo.prototype); - expect(methodSpy.args[0][1]).to.equal('a'); - expect(methodSpy.args[1][1]).to.equal('b'); - - }); - describeWithDOM('getNode', () => { it('should return a DOMNode when a DOMComponent is given', () => { diff --git a/src/describeWithDOM.js b/test/_helpers.js similarity index 60% rename from src/describeWithDOM.js rename to test/_helpers.js index 217032ad7..d9011b067 100644 --- a/src/describeWithDOM.js +++ b/test/_helpers.js @@ -1,17 +1,32 @@ -let jsdom; +/** + * Simple wrapper around mocha describe which allows a boolean to be passed in first which + * determines whether or not the test will be run + */ +export function describeIf(test, a, b) { + if (test) { + describe(a, b); + } else { + describe.skip(a, b); + } +} -try { - require('jsdom'); // could throw - jsdom = require('mocha-jsdom'); -} catch (e) { - // jsdom is not supported... +/** + * Simple wrapper around mocha it which allows a boolean to be passed in first which + * determines whether or not the test will be run + */ +export function itIf(test, a, b) { + if (test) { + it(a, b); + } else { + it.skip(a, b); + } } -export function describeWithDOM(a, b) { +function only(a, b) { describe('(uses jsdom)', () => { if (typeof jsdom === 'function') { jsdom(); - describe(a, b); + describe.only(a, b); } else { // if jsdom isn't available, skip every test in this describe context describe.skip(a, b); @@ -19,11 +34,11 @@ export function describeWithDOM(a, b) { }); } -function only(a, b) { +function skip(a, b) { describe('(uses jsdom)', () => { if (typeof jsdom === 'function') { jsdom(); - describe.only(a, b); + describe.skip(a, b); } else { // if jsdom isn't available, skip every test in this describe context describe.skip(a, b); @@ -31,11 +46,10 @@ function only(a, b) { }); } -function skip(a, b) { +export function describeWithDOM(a, b) { describe('(uses jsdom)', () => { - if (typeof jsdom === 'function') { - jsdom(); - describe.skip(a, b); + if (global.document) { + describe(a, b); } else { // if jsdom isn't available, skip every test in this describe context describe.skip(a, b); @@ -45,3 +59,4 @@ function skip(a, b) { describeWithDOM.only = only; describeWithDOM.skip = skip; +