From a52b697d1e2a0c4683009750b11d7f98bd70d41c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 1 Sep 2016 17:25:21 +0100 Subject: [PATCH 0001/1577] Enable watch implicitly unless on CI (#533) Fixes #532 --- package.json | 2 +- scripts/eject.js | 2 +- scripts/init.js | 2 +- scripts/test.js | 5 +++++ tasks/e2e.sh | 11 ++++++----- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 08901febc..1899f89b5 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "create-react-app": "node global-cli/index.js --scripts-version \"$PWD/`npm pack`\"", "e2e": "tasks/e2e.sh", "start": "node scripts/start.js --debug-template", - "test": "node scripts/test.js --debug-template --watch --env=jsdom" + "test": "node scripts/test.js --debug-template --env=jsdom" }, "files": [ "PATENTS", diff --git a/scripts/eject.js b/scripts/eject.js index 74d1cf09f..7fef22687 100644 --- a/scripts/eject.js +++ b/scripts/eject.js @@ -101,7 +101,7 @@ prompt( delete appPackage.scripts['eject']; Object.keys(appPackage.scripts).forEach(function (key) { appPackage.scripts[key] = appPackage.scripts[key] - .replace(/react-scripts test/g, 'jest') + .replace(/react-scripts test/g, 'jest --watch') .replace(/react-scripts (\w+)/g, 'node scripts/$1.js'); }); diff --git a/scripts/init.js b/scripts/init.js index 9402b930e..5beed2e1b 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -26,7 +26,7 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { appPackage.scripts = { 'start': 'react-scripts start', 'build': 'react-scripts build', - 'test': 'react-scripts test --watch --env=jsdom', + 'test': 'react-scripts test --env=jsdom', 'eject': 'react-scripts eject' }; diff --git a/scripts/test.js b/scripts/test.js index b173656b5..b8d047333 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -22,6 +22,11 @@ if (debugTemplateIndex !== -1) { argv.splice(debugTemplateIndex, 1); } +// Watch unless on CI +if (!process.env.CI) { + argv.push('--watch'); +} + argv.push('--config', JSON.stringify(createJestConfig( relativePath => path.resolve(__dirname, '..', relativePath), path.resolve(paths.appSrc, '..') diff --git a/tasks/e2e.sh b/tasks/e2e.sh index 1e841d869..4fedfe63b 100755 --- a/tasks/e2e.sh +++ b/tasks/e2e.sh @@ -67,8 +67,8 @@ test -e build/static/css/*.css test -e build/static/media/*.svg test -e build/favicon.ico -# Run tests, overriding watch option to disable it -npm test -- --watch=no +# Run tests with CI flag +CI=true npm test # Uncomment when snapshot testing is enabled by default: # test -e template/src/__snapshots__/App.test.js.snap @@ -101,8 +101,8 @@ test -e build/static/css/*.css test -e build/static/media/*.svg test -e build/favicon.ico -# Run tests, overriding watch option to disable it -npm test -- --watch=no +# Run tests with CI flag +CI=true npm test # Uncomment when snapshot testing is enabled by default: # test -e src/__snapshots__/App.test.js.snap @@ -120,7 +120,8 @@ test -e build/static/css/*.css test -e build/static/media/*.svg test -e build/favicon.ico -# Run tests, overriding watch option to disable it +# Run tests, overring the watch option to disable it +# TODO: make CI flag respected after ejecting as well npm test -- --watch=no # Uncomment when snapshot testing is enabled by default: # test -e src/__snapshots__/App.test.js.snap From 6fe904cd8fd3c4abe01aa55b57983020755194c6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 1 Sep 2016 17:51:28 +0100 Subject: [PATCH 0002/1577] Disable react-constant-elements because of bugs (#534) Disabled because of issues: * https://github.com/facebookincubator/create-react-app/issues/525 * https://phabricator.babeljs.io/search/query/pCNlnC2xzwzx/ TODO: Enable again when these issues are resolved. --- config/babel.prod.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/babel.prod.js b/config/babel.prod.js index 32f4bd665..107872181 100644 --- a/config/babel.prod.js +++ b/config/babel.prod.js @@ -33,6 +33,10 @@ module.exports = { regenerator: true }], // Optimization: hoist JSX that never changes out of render() - require.resolve('babel-plugin-transform-react-constant-elements') + // Disabled because of issues: + // * https://github.com/facebookincubator/create-react-app/issues/525 + // * https://phabricator.babeljs.io/search/query/pCNlnC2xzwzx/ + // TODO: Enable again when these issues are resolved. + // require.resolve('babel-plugin-transform-react-constant-elements') ] }; From 5645aa921dac273a3f5f75b06c62e79d854f9733 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 1 Sep 2016 18:01:11 +0100 Subject: [PATCH 0003/1577] Resolve babel-runtime relative to the config (#535) This makes generators work with Jest. Fixes #255. Replaces #262. --- config/babel.dev.js | 7 ++++++- config/babel.prod.js | 7 ++++++- config/webpack.config.dev.js | 11 ++--------- config/webpack.config.prod.js | 11 ++--------- package.json | 2 +- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/config/babel.dev.js b/config/babel.dev.js index 565331090..ebaab1156 100644 --- a/config/babel.dev.js +++ b/config/babel.dev.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +var path = require('path'); + module.exports = { // Don't try to find .babelrc because we want to force this configuration. babelrc: false, @@ -33,7 +35,10 @@ module.exports = { [require.resolve('babel-plugin-transform-runtime'), { helpers: false, polyfill: false, - regenerator: true + regenerator: true, + // Resolve the Babel runtime relative to the config. + // You can safely remove this after ejecting: + moduleName: path.dirname(require.resolve('babel-runtime/package')) }] ] }; diff --git a/config/babel.prod.js b/config/babel.prod.js index 107872181..d1f105f57 100644 --- a/config/babel.prod.js +++ b/config/babel.prod.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +var path = require('path'); + module.exports = { // Don't try to find .babelrc because we want to force this configuration. babelrc: false, @@ -30,7 +32,10 @@ module.exports = { [require.resolve('babel-plugin-transform-runtime'), { helpers: false, polyfill: false, - regenerator: true + regenerator: true, + // Resolve the Babel runtime relative to the config. + // You can safely remove this after ejecting: + moduleName: path.dirname(require.resolve('babel-runtime/package')) }], // Optimization: hoist JSX that never changes out of render() // Disabled because of issues: diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 2a66d3dd8..c72435ac8 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -68,15 +68,8 @@ module.exports = { // These are the reasonable defaults supported by the Node ecosystem. extensions: ['.js', '.json', ''], alias: { - // This `alias` section can be safely removed after ejection. - // We do this because `babel-runtime` may be inside `react-scripts`, - // so when `babel-plugin-transform-runtime` imports it, it will not be - // available to the app directly. This is a temporary solution that lets - // us ship support for generators. However it is far from ideal, and - // if we don't have a good solution, we should just make `babel-runtime` - // a dependency in generated projects. - // See https://github.com/facebookincubator/create-react-app/issues/255 - 'babel-runtime/regenerator': require.resolve('babel-runtime/regenerator'), + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 'react-native': 'react-native-web' } }, diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 48a37d04e..e64758b8f 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -63,15 +63,8 @@ module.exports = { // These are the reasonable defaults supported by the Node ecosystem. extensions: ['.js', '.json', ''], alias: { - // This `alias` section can be safely removed after ejection. - // We do this because `babel-runtime` may be inside `react-scripts`, - // so when `babel-plugin-transform-runtime` imports it, it will not be - // available to the app directly. This is a temporary solution that lets - // us ship support for generators. However it is far from ideal, and - // if we don't have a good solution, we should just make `babel-runtime` - // a dependency in generated projects. - // See https://github.com/facebookincubator/create-react-app/issues/255 - 'babel-runtime/regenerator': require.resolve('babel-runtime/regenerator'), + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 'react-native': 'react-native-web' } }, diff --git a/package.json b/package.json index 1899f89b5..5dbf4b918 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "babel-plugin-transform-object-rest-spread": "6.8.0", "babel-plugin-transform-react-constant-elements": "6.9.1", "babel-plugin-transform-regenerator": "6.14.0", - "babel-plugin-transform-runtime": "6.12.0", + "babel-plugin-transform-runtime": "6.15.0", "babel-preset-latest": "6.14.0", "babel-preset-react": "6.11.1", "babel-runtime": "6.11.6", From bb725e09b21702249c6d1eab255b334040b66fe9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 1 Sep 2016 18:26:07 +0100 Subject: [PATCH 0004/1577] Remove unnecessary describe() --- template/src/App.test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/template/src/App.test.js b/template/src/App.test.js index 55dbf38ce..b84af98d7 100644 --- a/template/src/App.test.js +++ b/template/src/App.test.js @@ -2,9 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -describe('App', () => { - it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - }); +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); }); From 48aa7ab74b35c7e87c0973f12bddf5cb0901eb50 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 1 Sep 2016 20:40:15 +0100 Subject: [PATCH 0005/1577] Update deps --- config/eslint.js | 1 + package.json | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/config/eslint.js b/config/eslint.js index 8eaeacbd7..e42a2a5e2 100644 --- a/config/eslint.js +++ b/config/eslint.js @@ -121,6 +121,7 @@ module.exports = { 'no-sequences': 'warn', 'no-shadow-restricted-names': 'warn', 'no-sparse-arrays': 'warn', + 'no-template-curly-in-string': 'warn', 'no-this-before-super': 'warn', 'no-throw-literal': 'warn', 'no-undef': 'warn', diff --git a/package.json b/package.json index 5dbf4b918..3a0969720 100644 --- a/package.json +++ b/package.json @@ -45,13 +45,13 @@ "chalk": "1.1.3", "connect-history-api-fallback": "1.3.0", "cross-spawn": "4.0.0", - "css-loader": "0.23.1", + "css-loader": "0.24.0", "detect-port": "1.0.0", - "eslint": "3.2.2", - "eslint-loader": "1.4.1", - "eslint-plugin-flowtype": "2.4.0", + "eslint": "3.4.0", + "eslint-loader": "1.5.0", + "eslint-plugin-flowtype": "2.11.4", "eslint-plugin-import": "1.12.0", - "eslint-plugin-jsx-a11y": "2.0.1", + "eslint-plugin-jsx-a11y": "2.2.1", "eslint-plugin-react": "5.2.2", "extract-text-webpack-plugin": "1.0.1", "file-loader": "0.9.0", @@ -60,21 +60,21 @@ "gzip-size": "3.0.0", "html-loader": "0.4.3", "html-webpack-plugin": "2.22.0", - "http-proxy-middleware": "0.17.0", - "jest": "15.0.1", + "http-proxy-middleware": "0.17.1", + "jest": "15.1.0", "json-loader": "0.5.4", "object-assign": "4.1.0", "opn": "4.0.2", "path-exists": "3.0.0", - "postcss-loader": "0.9.1", + "postcss-loader": "0.11.1", "promise": "7.1.1", "recursive-readdir": "2.0.0", "rimraf": "2.5.4", "strip-ansi": "3.0.1", "style-loader": "0.13.1", "url-loader": "0.5.7", - "webpack": "1.13.1", - "webpack-dev-server": "1.14.1", + "webpack": "1.13.2", + "webpack-dev-server": "1.15.1", "whatwg-fetch": "1.0.0" }, "devDependencies": { From 58195ec286d914d5d22a6346c484344f859edfea Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 2 Sep 2016 00:37:37 +0100 Subject: [PATCH 0006/1577] Document testing (#538) * Document testing * Update README.md * Update README.md * Clarify our recommendations on testing * Okay, that was too much. :-) * Add a few more things --- README.md | 14 ++- template/README.md | 216 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 225 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2fa9f43d1..0541db4b8 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ my-app/ src/ App.css App.js + App.test.js index.css index.js logo.svg @@ -76,6 +77,13 @@ You will see the build errors and lint warnings in the console. Build errors +### `npm test` + +Runs the test watcher in an interactive mode. +By default, runs tests related to files changes since the last commit. + +[Read more about testing.](template/README.md#running-tests) + ### `npm run build` Builds the app for production to the `build` folder.
@@ -108,7 +116,7 @@ You can also read its latest version [here](https://github.com/facebookincubator * Autoprefixed CSS, so you don’t need `-webkit` or other prefixes. * A `build` script to bundle JS, CSS, and images for production, with sourcemaps. -**The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. Currently, it doesn’t support testing either. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything. +**The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything. **You don’t have to use this.** Historically it has been easy to [gradually adopt](https://www.youtube.com/watch?v=BF58ZJ1ZQxY) React. However many people create new single-page React apps from scratch every day. We’ve heard [loud](https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4) and [clear](https://twitter.com/thomasfuchs/status/708675139253174273) that this process can be error-prone and tedious, especially if this is your first JavaScript build stack. This project is an attempt to figure out a good way to start developing React apps. @@ -127,7 +135,6 @@ You don’t have to ever use `eject`. The curated feature set is suitable for sm Some features are currently **not supported**: * Server rendering. -* Testing. * Some experimental syntax extensions (e.g. decorators). * CSS Modules. * LESS or Sass. @@ -144,7 +151,8 @@ Currently it is a thin layer on top of many amazing community projects, such as: * [Babel](http://babeljs.io/) with ES6 and extensions used by Facebook (JSX, [object spread](https://github.com/sebmarkbage/ecmascript-rest-spread/commits/master), [class properties](https://github.com/jeffmo/es-class-public-fields)) * [Autoprefixer](https://github.com/postcss/autoprefixer) * [ESLint](http://eslint.org/) -* and more. +* [Jest](http://facebook.github.io/jest) +* and others. All of them are transitive dependencies of the provided npm package. diff --git a/template/README.md b/template/README.md index 31c2802d0..d4134eb23 100644 --- a/template/README.md +++ b/template/README.md @@ -10,6 +10,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Folder Structure](#folder-structure) - [Available Scripts](#available-scripts) - [npm start](#npm-start) + - [npm test](#npm-test) - [npm run build](#npm-run-build) - [npm run eject](#npm-run-eject) - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) @@ -24,6 +25,17 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Integrating with a Node Backend](#integrating-with-a-node-backend) - [Proxying API Requests in Development](#proxying-api-requests-in-development) - [Adding `` Tags](#adding-meta-tags) +- [Running Tests](#running-tests) + - [Filename Conventions](#filename-conventions) + - [Command Line Interface](#command-line-interface) + - [Version Control Integration](#version-control-integration) + - [Writing Tests](#writing-tests) + - [Testing Components](#testing-components) + - [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries) + - [Coverage Reporting](#coverage-reporting) + - [Continuous Integration](#continuous-integration) + - [Disabling jsdom](#disabling-jsdom) + - [Experimental Snapshot Testing](#experimental-snapshot-testing) - [Deployment](#deployment) - [Now](#now) - [Heroku](#heroku) @@ -97,6 +109,11 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.
You will also see any lint errors in the console. +### `npm test` + +Launches the test runner in the interactive watch mode. +See the section about [running tests](#running-tests) for more information. + ### `npm run build` Builds the app for production to the `build` folder.
@@ -425,7 +442,7 @@ render() { The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this value, we need to have it defined in the environment: -### Windows (cmd.exe) +#### Windows (cmd.exe) ```cmd set REACT_APP_SECRET_CODE=abcdef&&npm start @@ -433,7 +450,7 @@ set REACT_APP_SECRET_CODE=abcdef&&npm start (Note: the lack of whitespace is intentional.) -### Linux, OS X (Bash) +#### Linux, OS X (Bash) ```bash REACT_APP_SECRET_CODE=abcdef npm start @@ -522,6 +539,201 @@ Then, on the server, regardless of the backend you use, you can read `index.html If you use a Node server, you can even share the route matching logic between the client and the server. However duplicating it also works fine in simple cases. +## Running Tests + +>Note: this feature is available with `react-scripts@0.3.0` and higher. +>[Read the migration guide to learn how to enable it in older projects!](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030) + +Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it, give it another try. + +Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness. + +While Jest provides browser globals such as `window` thanks to [jsdom](https://github.com/tmpvar/jsdom), they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks. + +We recommend that you use a separate tool for browser end-to-end tests if you need them. They are beyond the scope of Create React App. + +### Filename Conventions + +Jest will look for test files with any of the following popular naming conventions: + +* Files with `.js` suffix in `__tests__` folders. +* Files with `.test.js` suffix. +* Files with `.spec.js` suffix. + +The `.test.js` / `.spec.js` files (or the `__tests__` folders) can be located at any depth under the `src` top level folder. + +We recommend to put the test files (or `__tests__` folders) next to the code they are testing so that relative imports appear shorter. For example, if `App.test.js` and `App.js` are in the same folder, the test just needs to `import App from './App'` instead of a long relative path. + +### Command Line Interface + +When you run `npm test`, Jest will launch in the watch mode. Every time you save a file, it will re-run the tests, just like `npm start` recompiles the code. + +The watcher includes an interactive command-line interface with the ability to run all tests, or focus on a search pattern. It is designed this way so that you could keep it open and enjoy fast re-runs. You can learn the commands from the “Watch Usage” note that the watcher prints after every run: + +![Jest watch mode](http://facebook.github.io/jest/img/blog/15-watch.gif) + +### Version Control Integration + +By default, when you run `npm test`, Jest will only run the tests related to files changed since last commit. This is an optimization designed to make your tests runs fast regardless of how many tests you have. However it assumes that you don’t often commit the code that doesn’t pass the tests. + +Jest will always explicitly mention that it only ran tests related to the files changed since the last commit. You can also press `a` in the watch mode to force Jest to run all tests. + +Jest will always run all tests on a [continuous integration](#continuous-integration) server or if the project is not inside a Git or Mercurial repository. + +### Writing Tests + +To create tests, add `it()` blocks with the name of the test and its code. You may optionally wrap them in `describe()` blocks for logical grouping but this is neither required nor recommended. + +Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this: + +```js +import sum from './sum'; + +it('sums numbers', () => { + expect(sum(1, 2)).toEqual(3); + expect(sum(2, 2)).toEqual(4); +}); +``` + +All `expect()` matchers supported by Jest are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value). +You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](http://facebook.github.io/jest/docs/api.html#tobecalled) to create “spies” or mock functions. + +### Testing Components + +There is a broad spectrum of component testing techniques. They range from a “smoke test” verifying that a component renders without throwing, to shallow rendering and testing some of the output, to full rendering and testing component lifecycle and state changes. + +Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components: + +```js +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); +}); +``` + +This test mounts a component and makes sure that it didn’t throw during rendering. Tests like this provide a lot value with very little effort so they are great as a starting point, and this is the test you will find in `src/App.test.js`. + +When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior. + +If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). You can write a smoke test with it too: + +```sh +npm install --save-dev enzyme react-addons-test-utils +``` + +```js +import React from 'react'; +import { shallow } from 'enzyme'; +import App from './App'; + +it('renders without crashing', () => { + shallow(); +}); +``` + +Unlike the previous smoke test using `ReactDOM.render()`, this test only renders `` and doesn’t go deeper. For example, even if `` itself renders a `