diff --git a/.eslintrc.js b/.eslintrc.js index c3b79f83d1..0f8fb04706 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,6 +2,6 @@ const clientESLintConfig = require('./config/eslint'); module.exports = Object.assign({}, clientESLintConfig, { env: Object.assign({}, clientESLintConfig.env, { - node: true + node: true, }) }); diff --git a/.gitignore b/.gitignore index 2a5e753909..f19abba428 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build .DS_Store *.tgz my-app* +template/src/__tests__/__snapshots__/ diff --git a/bin/react-scripts.js b/bin/react-scripts.js index abfe06dd13..5838183395 100644 --- a/bin/react-scripts.js +++ b/bin/react-scripts.js @@ -5,8 +5,9 @@ var args = process.argv.slice(3); switch (script) { case 'build': -case 'start': case 'eject': +case 'start': +case 'test': var result = spawn.sync( 'node', [require.resolve('../scripts/' + script)].concat(args), diff --git a/config/eslint.js b/config/eslint.js index 5d6831b825..513ad2de02 100644 --- a/config/eslint.js +++ b/config/eslint.js @@ -28,6 +28,7 @@ module.exports = { browser: true, commonjs: true, es6: true, + jest: true, node: true }, diff --git a/config/jest/CSSStub.js b/config/jest/CSSStub.js new file mode 100644 index 0000000000..05fb9d0d70 --- /dev/null +++ b/config/jest/CSSStub.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +module.exports = {}; diff --git a/config/jest/FileStub.js b/config/jest/FileStub.js new file mode 100644 index 0000000000..96d28629fe --- /dev/null +++ b/config/jest/FileStub.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +module.exports = "test-file-stub"; diff --git a/config/jest/transform.js b/config/jest/transform.js new file mode 100644 index 0000000000..9f51b1a855 --- /dev/null +++ b/config/jest/transform.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +const babelDev = require('../babel.dev'); +const babelJest = require('babel-jest'); + +module.exports = babelJest.createTransformer(babelDev); diff --git a/global-cli/index.js b/global-cli/index.js index ee4c0881eb..d1319aba06 100644 --- a/global-cli/index.js +++ b/global-cli/index.js @@ -87,7 +87,10 @@ function createApp(name, verbose, version) { version: '0.0.1', private: true, }; - fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(packageJson)); + fs.writeFileSync( + path.join(root, 'package.json'), + JSON.stringify(packageJson, null, 2) + ); var originalDirectory = process.cwd(); process.chdir(root); diff --git a/package.json b/package.json index 59569d8a67..d29de30fe0 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,11 @@ "url": "https://github.com/facebookincubator/create-react-app/issues" }, "scripts": { - "start": "node scripts/start.js --debug-template", "build": "node scripts/build.js --debug-template", "create-react-app": "node global-cli/index.js --scripts-version \"$PWD/`npm pack`\"", - "test": "tasks/e2e.sh" + "e2e": "tasks/e2e.sh", + "start": "node scripts/start.js --debug-template", + "test": "node scripts/test.js --debug-template" }, "files": [ "PATENTS", @@ -30,6 +31,7 @@ "autoprefixer": "6.3.7", "babel-core": "6.11.4", "babel-eslint": "6.1.2", + "babel-jest": "14.1.0", "babel-loader": "6.2.4", "babel-plugin-syntax-trailing-function-commas": "6.8.0", "babel-plugin-transform-class-properties": "6.11.5", @@ -57,6 +59,7 @@ "fs-extra": "0.30.0", "gzip-size": "3.0.0", "html-webpack-plugin": "2.22.0", + "jest": "14.1.0", "json-loader": "0.5.4", "opn": "4.0.2", "postcss-loader": "0.9.1", @@ -70,8 +73,9 @@ }, "devDependencies": { "bundle-deps": "1.0.0", - "react": "^15.2.1", - "react-dom": "^15.2.1" + "react": "^15.3.0", + "react-dom": "^15.3.0", + "react-test-renderer": "^15.3.0" }, "optionalDependencies": { "fsevents": "1.0.14" diff --git a/scripts/eject.js b/scripts/eject.js index 0319b00b34..c3570e86e3 100644 --- a/scripts/eject.js +++ b/scripts/eject.js @@ -7,11 +7,12 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +var createJestConfig = require('./utils/create-jest-config'); var fs = require('fs'); var path = require('path'); +var prompt = require('./utils/prompt'); var rimrafSync = require('rimraf').sync; var spawnSync = require('cross-spawn').sync; -var prompt = require('./utils/prompt'); prompt( 'Are you sure you want to eject? This action is permanent.', @@ -37,6 +38,9 @@ prompt( path.join('config', 'polyfills.js'), path.join('config', 'webpack.config.dev.js'), path.join('config', 'webpack.config.prod.js'), + path.join('config', 'jest', 'CSSStub.js'), + path.join('config', 'jest', 'FileStub.js'), + path.join('config', 'jest', 'transform.js'), path.join('scripts', 'build.js'), path.join('scripts', 'start.js'), path.join('scripts', 'utils', 'chrome.applescript'), @@ -59,6 +63,7 @@ prompt( // Copy the files over fs.mkdirSync(path.join(appPath, 'config')); fs.mkdirSync(path.join(appPath, 'config', 'flow')); + fs.mkdirSync(path.join(appPath, 'config', 'jest')); fs.mkdirSync(path.join(appPath, 'scripts')); fs.mkdirSync(path.join(appPath, 'scripts', 'utils')); @@ -96,6 +101,11 @@ prompt( }); delete appPackage.scripts['eject']; + appPackage.scripts.test = 'jest'; + appPackage.jest = createJestConfig( + filePath => path.join('', filePath) + ); + // explicitly specify ESLint config path for editor plugins appPackage.eslintConfig = { extends: './config/eslint.js', diff --git a/scripts/init.js b/scripts/init.js index a26ad799cf..50cc9d25ba 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -19,13 +19,17 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {}; + appPackage.devDependencies = appPackage.devDependencies || {}; ['react', 'react-dom'].forEach(function (key) { appPackage.dependencies[key] = ownPackage.devDependencies[key]; }); + ['react-test-renderer'].forEach(function (key) { + appPackage.devDependencies[key] = ownPackage.devDependencies[key]; + }); // Setup the script rules appPackage.scripts = {}; - ['start', 'build', 'eject'].forEach(function(command) { + ['start', 'build', 'eject', 'test'].forEach(function(command) { appPackage.scripts[command] = 'react-scripts ' + command; }); diff --git a/scripts/test.js b/scripts/test.js new file mode 100644 index 0000000000..9602637afe --- /dev/null +++ b/scripts/test.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +process.env.NODE_ENV = 'test'; + +const createJestConfig = require('./utils/create-jest-config'); +const jest = require('jest'); +const path = require('path'); +const paths = require('../config/paths'); + +const argv = process.argv.slice(2); + +const index = argv.indexOf('--debug-template'); +if (index !== -1) { + argv.splice(index, 1); +} + +argv.push('--config', JSON.stringify(createJestConfig( + relativePath => path.resolve(__dirname, '..', relativePath), + path.resolve(paths.appSrc, '..') +))); + +jest.run(argv); diff --git a/scripts/utils/create-jest-config.js b/scripts/utils/create-jest-config.js new file mode 100644 index 0000000000..282705f0dd --- /dev/null +++ b/scripts/utils/create-jest-config.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +module.exports = (resolve, rootDir) => { + const config = { + automock: false, + moduleNameMapper: { + '^[./a-zA-Z0-9$_-]+\\.(jpg|png|gif|eot|svg|ttf|woff|woff2|mp4|webm)$': resolve('config/jest/FileStub.js'), + '^[./a-zA-Z0-9$_-]+\\.css$': resolve('config/jest/CSSStub.js') + }, + persistModuleRegistryBetweenSpecs: true, + scriptPreprocessor: resolve('config/jest/transform.js'), + setupFiles: [ + resolve('config/polyfills.js') + ], + testEnvironment: 'node' + }; + if (rootDir) { + config.rootDir = rootDir; + } + return config; +}; diff --git a/tasks/e2e.sh b/tasks/e2e.sh index af9d5907d3..8189847f7f 100755 --- a/tasks/e2e.sh +++ b/tasks/e2e.sh @@ -11,6 +11,7 @@ cd "$(dirname "$0")" function cleanup { echo 'Cleaning up.' cd $initial_path + rm ../template/src/__tests__/__snapshots__/App-test.js.snap rm -rf $temp_cli_path $temp_app_path } @@ -38,6 +39,7 @@ set -x # npm pack the two directories to make sure they are valid npm modules initial_path=$PWD + cd .. # A hacky way to avoid bundling dependencies. @@ -65,6 +67,10 @@ test -e build/static/js/*.js test -e build/static/css/*.css test -e build/static/media/*.svg +# Run tests +npm run test +test -e template/src/__tests__/__snapshots__/App-test.js.snap + # Pack CLI cd global-cli npm install @@ -90,6 +96,10 @@ test -e build/static/js/*.js test -e build/static/css/*.css test -e build/static/media/*.svg +# Run tests +npm run test +test -e src/__tests__/__snapshots__/App-test.js.snap + # Test the server npm start -- --smoke-test @@ -103,6 +113,10 @@ test -e build/static/js/*.js test -e build/static/css/*.css test -e build/static/media/*.svg +# Run tests +npm run test +test -e src/__tests__/__snapshots__/App-test.js.snap + # Test the server npm start -- --smoke-test diff --git a/template/src/__tests__/App-test.js b/template/src/__tests__/App-test.js new file mode 100644 index 0000000000..7d36ff192a --- /dev/null +++ b/template/src/__tests__/App-test.js @@ -0,0 +1,11 @@ +import React from 'react'; +import App from '../App'; +import renderer from 'react-test-renderer'; + +describe('App', () => { + it('renders a welcome view', () => { + const instance = renderer.create(); + const tree = instance.toJSON(); + expect(tree).toMatchSnapshot(); + }); +});