From 9099570b94712dc46da322219d659c045e5b7de4 Mon Sep 17 00:00:00 2001
From: Fabrizio Castellarin <f.castellarin@gmail.com>
Date: Mon, 23 Jan 2017 20:43:16 +0100
Subject: [PATCH] Use a more sophisticated template for end-to-end testing.
 (#1187)

* Use a more sophisticated template for end-to-end testing.

* Not publish integration tests to npm

* Use "commander" for  cli argv handling

* Handle different scripts version forms and exits without a name given

* Prepare the commands for testing with a template

* Fix dev "template" path

* Add various features to test

* Test various features separately

* Test language features

* Comment unused e2e.sh lines

* Add "development" tests

* Test environment variables

* Test webpack plugins

* Replace kitchensink README

* Switch integration tests from jest to mocha

* Use `fs-extra`

* Use the correct folders

* Do some cleanup

* Print a better message for `--template`

* Test `npm start` with and without https

* Separate fast e2e testing from kitchensink testing

* Hide `--internal-testing-template` (former `--template`) CLI option
---
 .travis.yml                                   |  20 +-
 package.json                                  |   2 +-
 packages/create-react-app/index.js            |  21 +-
 packages/react-scripts/.npmignore             |   1 +
 .../fixtures/kitchensink/.babelrc             |   3 +
 .../react-scripts/fixtures/kitchensink/.env   |   1 +
 .../fixtures/kitchensink/.flowconfig          |   8 +
 .../kitchensink/.template.dependencies.json   |  10 +
 .../fixtures/kitchensink/gitignore            |  15 ++
 .../kitchensink/integration/env.test.js       |  24 ++
 .../kitchensink/integration/initDOM.js        |  62 +++++
 .../kitchensink/integration/syntax.test.js    |  96 ++++++++
 .../kitchensink/integration/webpack.test.js   |  44 ++++
 .../fixtures/kitchensink/public/favicon.ico   | Bin 0 -> 24838 bytes
 .../fixtures/kitchensink/public/index.html    |  12 +
 .../fixtures/kitchensink/src/App.js           | 103 +++++++++
 .../fixtures/kitchensink/src/App.test.js      |   8 +
 .../fixtures/kitchensink/src/absoluteLoad.js  |   6 +
 .../src/features/env/FileEnvVariables.js      |   5 +
 .../src/features/env/FileEnvVariables.test.js |  10 +
 .../kitchensink/src/features/env/NodePath.js  |  25 ++
 .../src/features/env/NodePath.test.js         |  10 +
 .../src/features/env/ShellEnvVariables.js     |   5 +
 .../features/env/ShellEnvVariables.test.js    |  10 +
 .../src/features/syntax/ArrayDestructuring.js |  34 +++
 .../syntax/ArrayDestructuring.test.js         |  10 +
 .../src/features/syntax/ArraySpread.js        |  33 +++
 .../src/features/syntax/ArraySpread.test.js   |  10 +
 .../src/features/syntax/AsyncAwait.js         |  33 +++
 .../src/features/syntax/AsyncAwait.test.js    |  10 +
 .../src/features/syntax/ClassProperties.js    |  20 ++
 .../features/syntax/ClassProperties.test.js   |  10 +
 .../src/features/syntax/ComputedProperties.js |  33 +++
 .../syntax/ComputedProperties.test.js         |  10 +
 .../features/syntax/CustomInterpolation.js    |  43 ++++
 .../syntax/CustomInterpolation.test.js        |  10 +
 .../src/features/syntax/DefaultParameters.js  |  33 +++
 .../features/syntax/DefaultParameters.test.js |  10 +
 .../features/syntax/DestructuringAndAwait.js  |  33 +++
 .../syntax/DestructuringAndAwait.test.js      |  10 +
 .../src/features/syntax/Generators.js         |  35 +++
 .../src/features/syntax/Generators.test.js    |  10 +
 .../features/syntax/ObjectDestructuring.js    |  34 +++
 .../syntax/ObjectDestructuring.test.js        |  10 +
 .../src/features/syntax/ObjectSpread.js       |  33 +++
 .../src/features/syntax/ObjectSpread.test.js  |  10 +
 .../src/features/syntax/Promises.js           |  34 +++
 .../src/features/syntax/Promises.test.js      |  10 +
 .../src/features/syntax/RestAndDefault.js     |  33 +++
 .../features/syntax/RestAndDefault.test.js    |  10 +
 .../src/features/syntax/RestParameters.js     |  33 +++
 .../features/syntax/RestParameters.test.js    |  10 +
 .../features/syntax/TemplateInterpolation.js  |  33 +++
 .../syntax/TemplateInterpolation.test.js      |  10 +
 .../src/features/webpack/CssInclusion.js      |   6 +
 .../src/features/webpack/CssInclusion.test.js |  10 +
 .../src/features/webpack/ImageInclusion.js    |   6 +
 .../features/webpack/ImageInclusion.test.js   |  10 +
 .../src/features/webpack/JsonInclusion.js     |   6 +
 .../features/webpack/JsonInclusion.test.js    |  10 +
 .../src/features/webpack/NoExtInclusion.js    |  10 +
 .../features/webpack/NoExtInclusion.test.js   |  10 +
 .../src/features/webpack/SvgInclusion.js      |   6 +
 .../src/features/webpack/SvgInclusion.test.js |  10 +
 .../features/webpack/UnknownExtInclusion.js   |  10 +
 .../webpack/UnknownExtInclusion.test.js       |  10 +
 .../webpack/assets/aFileWithExt.unknown       |   1 +
 .../features/webpack/assets/aFileWithoutExt   |   1 +
 .../src/features/webpack/assets/abstract.json |   3 +
 .../src/features/webpack/assets/logo.svg      |   7 +
 .../src/features/webpack/assets/style.css     |   4 +
 .../features/webpack/assets/tiniest-cat.jpg   | Bin 0 -> 691 bytes
 .../fixtures/kitchensink/src/index.js         |   8 +
 .../fixtures/kitchensink/src/subfolder/lol.js |   1 +
 packages/react-scripts/scripts/init.js        |  20 +-
 tasks/e2e-installs.sh                         | 152 ++++++++++++
 tasks/e2e-kitchensink.sh                      | 218 ++++++++++++++++++
 tasks/{e2e.sh => e2e-simple.sh}               |  60 -----
 78 files changed, 1653 insertions(+), 74 deletions(-)
 create mode 100644 packages/react-scripts/.npmignore
 create mode 100644 packages/react-scripts/fixtures/kitchensink/.babelrc
 create mode 100644 packages/react-scripts/fixtures/kitchensink/.env
 create mode 100644 packages/react-scripts/fixtures/kitchensink/.flowconfig
 create mode 100644 packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
 create mode 100644 packages/react-scripts/fixtures/kitchensink/gitignore
 create mode 100644 packages/react-scripts/fixtures/kitchensink/integration/env.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/public/favicon.ico
 create mode 100644 packages/react-scripts/fixtures/kitchensink/public/index.html
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/App.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/App.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/index.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js
 create mode 100755 tasks/e2e-installs.sh
 create mode 100755 tasks/e2e-kitchensink.sh
 rename tasks/{e2e.sh => e2e-simple.sh} (73%)

diff --git a/.travis.yml b/.travis.yml
index bb9c81120..8f3582466 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 ---
 language: node_js
 node_js:
-  - 0.10
   - 4
   - 6
 cache:
@@ -9,7 +8,20 @@ cache:
   - node_modules
   - packages/create-react-app/node_modules
   - packages/react-scripts/node_modules
-script: tasks/e2e.sh
+script:
+ - 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
+ - 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
+ - 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi'
 env:
-  - USE_YARN=no
-  - USE_YARN=yes
+  global:
+    - USE_YARN=no
+  matrix:
+    - TEST_SUITE=simple
+    - TEST_SUITE=installs
+    - TEST_SUITE=kitchensink
+matrix:
+  include:
+    - node_js: 0.10
+      env: TEST_SUITE=simple
+    - node_js: 6
+      env: USE_YARN=yes TEST_SUITE=simple
diff --git a/package.json b/package.json
index 6645ea279..6705e0413 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
     "build": "node packages/react-scripts/scripts/build.js",
     "changelog": "lerna-changelog",
     "create-react-app": "tasks/cra.sh",
-    "e2e": "tasks/e2e.sh",
+    "e2e": "tasks/e2e-simple.sh",
     "postinstall": "lerna bootstrap",
     "publish": "tasks/release.sh",
     "start": "node packages/react-scripts/scripts/start.js",
diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js
index 9ab11a5e9..4dbb37f49 100644
--- a/packages/create-react-app/index.js
+++ b/packages/create-react-app/index.js
@@ -52,6 +52,7 @@ if (currentNodeVersion.split('.')[0] < 4) {
   process.exit(1);
 }
 
+var commander = require('commander');
 var fs = require('fs-extra');
 var path = require('path');
 var execSync = require('child_process').execSync;
@@ -60,7 +61,7 @@ var semver = require('semver');
 
 var projectName;
 
-var program = require('commander')
+var program = commander
   .version(require('./package.json').version)
   .arguments('<project-directory>')
   .usage(chalk.green('<project-directory>') + ' [options]')
@@ -69,6 +70,7 @@ var program = require('commander')
   })
   .option('--verbose', 'print additional logs')
   .option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts')
+  .allowUnknownOption()
   .on('--help', function () {
     console.log('    Only ' + chalk.green('<project-directory>') + ' is required.');
     console.log();
@@ -82,7 +84,7 @@ var program = require('commander')
     console.log('      ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new'));
     console.log();
   })
-  .parse(process.argv)
+  .parse(process.argv);
 
 if (typeof projectName === 'undefined') {
   console.error('Please specify the project directory:');
@@ -95,9 +97,14 @@ if (typeof projectName === 'undefined') {
   process.exit(1);
 }
 
-createApp(projectName, program.verbose, program.scriptsVersion);
+var hiddenProgram = new commander.Command()
+  .option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' +
+    'use a non-standard application template')
+  .parse(process.argv)
+
+createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate);
 
-function createApp(name, verbose, version) {
+function createApp(name, verbose, version, template) {
   var root = path.resolve(name);
   var appName = path.basename(root);
 
@@ -130,7 +137,7 @@ function createApp(name, verbose, version) {
   console.log('Installing ' + chalk.cyan('react-scripts') + '...');
   console.log();
 
-  run(root, appName, version, verbose, originalDirectory);
+  run(root, appName, version, verbose, originalDirectory, template);
 }
 
 function shouldUseYarn() {
@@ -163,7 +170,7 @@ function install(packageToInstall, verbose, callback) {
   });
 }
 
-function run(root, appName, version, verbose, originalDirectory) {
+function run(root, appName, version, verbose, originalDirectory, template) {
   var packageToInstall = getInstallPackage(version);
   var packageName = getPackageName(packageToInstall);
 
@@ -183,7 +190,7 @@ function run(root, appName, version, verbose, originalDirectory) {
       'init.js'
     );
     var init = require(scriptsPath);
-    init(root, appName, verbose, originalDirectory);
+    init(root, appName, verbose, originalDirectory, template);
   });
 }
 
diff --git a/packages/react-scripts/.npmignore b/packages/react-scripts/.npmignore
new file mode 100644
index 000000000..76163ab96
--- /dev/null
+++ b/packages/react-scripts/.npmignore
@@ -0,0 +1 @@
+/fixtures
diff --git a/packages/react-scripts/fixtures/kitchensink/.babelrc b/packages/react-scripts/fixtures/kitchensink/.babelrc
new file mode 100644
index 000000000..5686105b9
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/.babelrc
@@ -0,0 +1,3 @@
+{
+  "presets": ["latest"]
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/.env b/packages/react-scripts/fixtures/kitchensink/.env
new file mode 100644
index 000000000..3e6e8a5a3
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/.env
@@ -0,0 +1 @@
+REACT_APP_FILE_ENV_MESSAGE=fromtheenvfile
diff --git a/packages/react-scripts/fixtures/kitchensink/.flowconfig b/packages/react-scripts/fixtures/kitchensink/.flowconfig
new file mode 100644
index 000000000..c65836228
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/.flowconfig
@@ -0,0 +1,8 @@
+[ignore]
+<PROJECT_ROOT>/node_modules/fbjs/.*
+
+[include]
+
+[libs]
+
+[options]
diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
new file mode 100644
index 000000000..8adac4570
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
@@ -0,0 +1,10 @@
+{
+  "dependencies": {
+    "babel-preset-latest": "6.16.0",
+    "babel-register": "6.18.0",
+    "babel-polyfill": "6.20.0",
+    "chai": "3.5.0",
+    "jsdom": "9.8.3",
+    "mocha": "3.2.0"
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/gitignore b/packages/react-scripts/fixtures/kitchensink/gitignore
new file mode 100644
index 000000000..6c96c5cff
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/gitignore
@@ -0,0 +1,15 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+
+# testing
+coverage
+
+# production
+build
+
+# misc
+.DS_Store
+.env
+npm-debug.log
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js
new file mode 100644
index 000000000..a179aa7cb
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js
@@ -0,0 +1,24 @@
+import { expect } from 'chai'
+import initDOM from './initDOM'
+
+describe('Integration', () => {
+  describe('Environment variables', () => {
+    it('NODE_PATH', async () => {
+      const doc = await initDOM('node-path')
+
+      expect(doc.getElementById('feature-node-path').childElementCount).to.equal(4)
+    })
+
+    it('shell env variables', async () => {
+      const doc = await initDOM('shell-env-variables')
+
+      expect(doc.getElementById('feature-shell-env-variables').textContent).to.equal('fromtheshell.')
+    })
+
+    it('file env variables', async () => {
+      const doc = await initDOM('file-env-variables')
+
+      expect(doc.getElementById('feature-file-env-variables').textContent).to.equal('fromtheenvfile.')
+    })
+  })
+})
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
new file mode 100644
index 000000000..c04e60d4c
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
@@ -0,0 +1,62 @@
+const fs = require('fs')
+const http = require('http')
+const jsdom = require('jsdom')
+const path = require('path')
+
+let getMarkup
+let resourceLoader
+// this value could be tweaked in order to let the resource
+// retriever get every file and jsdom execute react
+let timeToWaitForJsToExecute
+
+if (process.env.E2E_FILE) {
+  const file = path.isAbsolute(process.env.E2E_FILE)
+    ? process.env.E2E_FILE
+    : path.join(process.cwd(), process.env.E2E_FILE)
+
+  const markup = fs.readFileSync(file, 'utf8')
+  getMarkup = () => markup
+
+  resourceLoader = (resource, callback) => callback(
+    null,
+    fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8')
+  )
+
+  timeToWaitForJsToExecute = 0
+} else if (process.env.E2E_URL) {
+  getMarkup = () => new Promise(resolve => {
+    http.get(process.env.E2E_URL, (res) => {
+      let rawData = ''
+      res.on('data', chunk => rawData += chunk)
+      res.on('end', () => resolve(rawData))
+    })
+  })
+
+  resourceLoader = (resource, callback) => {
+    return resource.defaultFetch(callback)
+  }
+
+  timeToWaitForJsToExecute = 100
+} else {
+  it.only('can run jsdom (at least one of "E2E_FILE" or "E2E_URL" environment variables must be provided)', () => {
+    expect(new Error('This isn\'t the error you are looking for.')).toBeUndefined()
+  })
+}
+
+export default feature => new Promise(async resolve => {
+  const markup = await getMarkup()
+  const host = process.env.E2E_URL || 'http://localhost:3000'
+  const doc = jsdom.jsdom(markup, {
+    features : {
+      FetchExternalResources : ['script', 'css'],
+      ProcessExternalResources : ['script'],
+    },
+    resourceLoader,
+    url: `${host}#${feature}`,
+    virtualConsole: jsdom.createVirtualConsole().sendTo(console),
+  })
+
+  doc.defaultView.addEventListener('load', () => {
+    setTimeout(() => resolve(doc), timeToWaitForJsToExecute)
+  }, false)
+})
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
new file mode 100644
index 000000000..c8b7fbbc5
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
@@ -0,0 +1,96 @@
+import { expect } from 'chai'
+import initDOM from './initDOM'
+
+describe('Integration', () => {
+  describe('Language syntax', () => {
+    it('array destructuring', async () => {
+      const doc = await initDOM('array-destructuring')
+
+      expect(doc.getElementById('feature-array-destructuring').childElementCount).to.equal(4)
+    })
+
+    it('array spread', async () => {
+      const doc = await initDOM('array-spread')
+
+      expect(doc.getElementById('feature-array-spread').childElementCount).to.equal(4)
+    })
+
+    it('async/await', async () => {
+      const doc = await initDOM('async-await')
+
+      expect(doc.getElementById('feature-async-await').childElementCount).to.equal(4)
+    })
+
+    it('class properties', async () => {
+      const doc = await initDOM('class-properties')
+
+      expect(doc.getElementById('feature-class-properties').childElementCount).to.equal(4)
+    })
+
+    it('computed properties', async () => {
+      const doc = await initDOM('computed-properties')
+
+      expect(doc.getElementById('feature-computed-properties').childElementCount).to.equal(4)
+    })
+
+    it('custom interpolation', async () => {
+      const doc = await initDOM('custom-interpolation')
+
+      expect(doc.getElementById('feature-custom-interpolation').childElementCount).to.equal(4)
+    })
+
+    it('default parameters', async () => {
+      const doc = await initDOM('default-parameters')
+
+      expect(doc.getElementById('feature-default-parameters').childElementCount).to.equal(4)
+    })
+
+    it('destructuring and await', async () => {
+      const doc = await initDOM('destructuring-and-await')
+
+      expect(doc.getElementById('feature-destructuring-and-await').childElementCount).to.equal(4)
+    })
+
+    it('generators', async () => {
+      const doc = await initDOM('generators')
+
+      expect(doc.getElementById('feature-generators').childElementCount).to.equal(4)
+    })
+
+    it('object destructuring', async () => {
+      const doc = await initDOM('object-destructuring')
+
+      expect(doc.getElementById('feature-object-destructuring').childElementCount).to.equal(4)
+    })
+
+    it('object spread', async () => {
+      const doc = await initDOM('object-spread')
+
+      expect(doc.getElementById('feature-object-spread').childElementCount).to.equal(4)
+    })
+
+    it('promises', async () => {
+      const doc = await initDOM('promises')
+
+      expect(doc.getElementById('feature-promises').childElementCount).to.equal(4)
+    })
+
+    it('rest + default', async () => {
+      const doc = await initDOM('rest-and-default')
+
+      expect(doc.getElementById('feature-rest-and-default').childElementCount).to.equal(4)
+    })
+
+    it('rest parameters', async () => {
+      const doc = await initDOM('rest-parameters')
+
+      expect(doc.getElementById('feature-rest-parameters').childElementCount).to.equal(4)
+    })
+
+    it('template interpolation', async () => {
+      const doc = await initDOM('template-interpolation')
+
+      expect(doc.getElementById('feature-template-interpolation').childElementCount).to.equal(4)
+    })
+  })
+})
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
new file mode 100644
index 000000000..9e2826b8f
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
@@ -0,0 +1,44 @@
+import { expect } from 'chai'
+import initDOM from './initDOM'
+
+describe('Integration', () => {
+  describe('Webpack plugins', () => {
+    it('css inclusion', async () => {
+      const doc = await initDOM('css-inclusion')
+
+      expect(doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, ''))
+        .to.match(/#feature-css-inclusion\{background:.+;color:.+}/)
+    })
+
+    it('image inclusion', async () => {
+      const doc = await initDOM('image-inclusion')
+
+      expect(doc.getElementById('feature-image-inclusion').src).to.match(/^data:image\/jpeg;base64.+==$/)
+    })
+
+    it('no ext inclusion', async () => {
+      const doc = await initDOM('no-ext-inclusion')
+
+      expect(doc.getElementById('feature-no-ext-inclusion').textContent)
+        .to.equal('This is just a file without an extension.')
+    })
+
+    it('json inclusion', async () => {
+      const doc = await initDOM('json-inclusion')
+
+      expect(doc.getElementById('feature-json-inclusion').textContent).to.equal('This is an abstract.')
+    })
+
+    it('svg inclusion', async () => {
+      const doc = await initDOM('svg-inclusion')
+
+      expect(doc.getElementById('feature-svg-inclusion').src).to.match(/\/static\/media\/logo\..+\.svg$/)
+    })
+
+    it('unknown ext inclusion', async () => {
+      const doc = await initDOM('unknown-ext-inclusion')
+
+      expect(doc.getElementById('feature-unknown-ext-inclusion').textContent).to.equal('Whoooo, spooky!.')
+    })
+  })
+})
diff --git a/packages/react-scripts/fixtures/kitchensink/public/favicon.ico b/packages/react-scripts/fixtures/kitchensink/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..5c125de5d897c1ff5692a656485b3216123dcd89
GIT binary patch
literal 24838
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
F{{x|jBbfjI

literal 0
HcmV?d00001

diff --git a/packages/react-scripts/fixtures/kitchensink/public/index.html b/packages/react-scripts/fixtures/kitchensink/public/index.html
new file mode 100644
index 000000000..ce76aae90
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/public/index.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    <title>React App</title>
+  </head>
+  <body>
+    <div id="root"></div>
+  </body>
+</html>
diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js
new file mode 100644
index 000000000..66cd45541
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/App.js
@@ -0,0 +1,103 @@
+import React from 'react';
+
+class App extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { feature: null };
+
+    this.setFeature = this.setFeature.bind(this);
+  }
+
+  componentDidMount() {
+    switch (location.hash.slice(1)) {
+      case 'array-destructuring':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ArrayDestructuring').default));
+        break;
+      case 'array-spread':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ArraySpread').default));
+        break;
+      case 'async-await':
+        require.ensure([], () => this.setFeature(require('./features/syntax/AsyncAwait').default));
+        break;
+      case 'class-properties':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ClassProperties').default));
+        break;
+      case 'computed-properties':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ComputedProperties').default));
+        break;
+      case 'css-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/CssInclusion').default));
+        break;
+      case 'custom-interpolation':
+        require.ensure([], () => this.setFeature(require('./features/syntax/CustomInterpolation').default));
+        break;
+      case 'default-parameters':
+        require.ensure([], () => this.setFeature(require('./features/syntax/DefaultParameters').default));
+        break;
+      case 'destructuring-and-await':
+        require.ensure([], () => this.setFeature(require('./features/syntax/DestructuringAndAwait').default));
+        break;
+      case 'file-env-variables':
+        require.ensure([], () => this.setFeature(require('./features/env/FileEnvVariables').default));
+        break;
+      case 'generators':
+        require.ensure([], () => this.setFeature(require('./features/syntax/Generators').default));
+        break;
+      case 'image-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/ImageInclusion').default));
+        break;
+      case 'json-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/JsonInclusion').default));
+        break;
+      case 'node-path':
+        require.ensure([], () => this.setFeature(require('./features/env/NodePath').default));
+        break;
+      case 'no-ext-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/NoExtInclusion').default));
+        break;
+      case 'object-destructuring':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ObjectDestructuring').default));
+        break;
+      case 'object-spread':
+        require.ensure([], () => this.setFeature(require('./features/syntax/ObjectSpread').default));
+        break;
+      case 'promises':
+        require.ensure([], () => this.setFeature(require('./features/syntax/Promises').default));
+        break;
+      case 'rest-and-default':
+        require.ensure([], () => this.setFeature(require('./features/syntax/RestAndDefault').default));
+        break;
+      case 'rest-parameters':
+        require.ensure([], () => this.setFeature(require('./features/syntax/RestParameters').default));
+        break;
+      case 'shell-env-variables':
+        require.ensure([], () => this.setFeature(require('./features/env/ShellEnvVariables').default));
+        break;
+      case 'svg-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/SvgInclusion').default));
+        break;
+      case 'template-interpolation':
+        require.ensure([], () => this.setFeature(require('./features/syntax/TemplateInterpolation').default));
+        break;
+      case 'unknown-ext-inclusion':
+        require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default)
+        );
+        break;
+      default:
+        this.setFeature(null);
+        break;
+    }
+  }
+
+  setFeature(feature) {
+    this.setState({ feature });
+  }
+
+  render() {
+    const Feature = this.state.feature;
+    return Feature ? <Feature /> : null;
+  }
+}
+
+export default App;
diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.test.js b/packages/react-scripts/fixtures/kitchensink/src/App.test.js
new file mode 100644
index 000000000..b84af98d7
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/App.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+it('renders without crashing', () => {
+  const div = document.createElement('div');
+  ReactDOM.render(<App />, div);
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js
new file mode 100644
index 000000000..dc769fe1e
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js
@@ -0,0 +1,6 @@
+export default () => [
+  { id: 1, name: '1' },
+  { id: 2, name: '2' },
+  { id: 3, name: '3' },
+  { id: 4, name: '4' }
+]
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js
new file mode 100644
index 000000000..c7b7c5d53
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js
@@ -0,0 +1,5 @@
+import React from 'react'
+
+export default () => (
+  <span id="feature-file-env-variables">{process.env.REACT_APP_FILE_ENV_MESSAGE}.</span>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js
new file mode 100644
index 000000000..c9d802be9
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import FileEnvVariables from './FileEnvVariables';
+
+describe('.env variables', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<FileEnvVariables />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
new file mode 100644
index 000000000..2d2f474f1
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
@@ -0,0 +1,25 @@
+import React from 'react'
+import load from 'absoluteLoad'
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-node-path">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js
new file mode 100644
index 000000000..05b981853
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import NodePath from './NodePath';
+
+describe('NODE_PATH', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<NodePath />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js
new file mode 100644
index 000000000..37b80ec60
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js
@@ -0,0 +1,5 @@
+import React from 'react'
+
+export default () => (
+  <span id="feature-shell-env-variables">{process.env.REACT_APP_SHELL_ENV_MESSAGE}.</span>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js
new file mode 100644
index 000000000..981bee67c
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ShellEnvVariables from './ShellEnvVariables';
+
+describe('shell env variables', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ShellEnvVariables />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js
new file mode 100644
index 000000000..38e351209
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js
@@ -0,0 +1,34 @@
+import React from 'react'
+
+function load() {
+  return [
+    [1, '1'],
+    [2, '2'],
+    [3, '3'],
+    [4, '4']
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-array-destructuring">
+        {this.state.users.map(user => {
+          const [id, name] = user;
+          return <div key={id}>{name}</div>
+        })}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js
new file mode 100644
index 000000000..617df2a6c
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ArrayDestructuring from './ArrayDestructuring';
+
+describe('array destructuring', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ArrayDestructuring />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js
new file mode 100644
index 000000000..d7d9a4591
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load(users) {
+  return [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    ...users
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load([{ id: 42, name: '42' }]);
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-array-spread">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js
new file mode 100644
index 000000000..85fade6e3
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ArraySpread from './ArraySpread';
+
+describe('array spread', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ArraySpread />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js
new file mode 100644
index 000000000..01c686405
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+async function load() {
+  return [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = await load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-async-await">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js
new file mode 100644
index 000000000..072f16fff
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import AsyncAwait from './AsyncAwait';
+
+describe('async/await', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<AsyncAwait />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js
new file mode 100644
index 000000000..65e500d64
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js
@@ -0,0 +1,20 @@
+import React from 'react'
+
+export default class extends React.Component {
+  users = [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ];
+
+  render() {
+    return (
+      <div id="feature-class-properties">
+        {this.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js
new file mode 100644
index 000000000..71d851dd2
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ClassProperties from './ClassProperties';
+
+describe('class properties', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ClassProperties />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js
new file mode 100644
index 000000000..c7a24cb7f
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load(prefix) {
+  return [
+    { id: 1, [prefix + 'name']: '1' },
+    { id: 2, [prefix + 'name']: '2' },
+    { id: 3, [prefix + 'name']: '3' },
+    { id: 4, [prefix + 'name']: '4' }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load('user_');
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-computed-properties">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.user_name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js
new file mode 100644
index 000000000..4e9aaf17a
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ComputedProperties from './ComputedProperties';
+
+describe('computed properties', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ComputedProperties />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js
new file mode 100644
index 000000000..032989683
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js
@@ -0,0 +1,43 @@
+import React from 'react'
+
+const styled = ([style]) => style.trim()
+  .split(/\s*;\s*/)
+  .map(rule => rule.split(/\s*:\s*/))
+  .reduce((rules, rule) => ({ ...rules, [rule[0]]: rule[1] }), {});
+
+function load() {
+  return [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    const veryInlineStyle = styled`
+      background: palevioletred;
+      color: papayawhip;
+    `;
+
+    return (
+      <div id="feature-custom-interpolation">
+        {this.state.users.map(user => (
+          <div key={user.id} style={veryInlineStyle}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js
new file mode 100644
index 000000000..10b1df278
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import CustomInterpolation from './CustomInterpolation';
+
+describe('custom interpolation', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<CustomInterpolation />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js
new file mode 100644
index 000000000..3a676358a
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load(id = 0) {
+  return [
+    { id: id + 1, name: '1' },
+    { id: id + 2, name: '2' },
+    { id: id + 3, name: '3' },
+    { id: id + 4, name: '4' }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-default-parameters">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js
new file mode 100644
index 000000000..b5ece2446
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import DefaultParameters from './DefaultParameters';
+
+describe('default parameters', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<DefaultParameters />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js
new file mode 100644
index 000000000..291ed3bcc
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+async function load() {
+  return { users: [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ] };
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const { users } = await load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-destructuring-and-await">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js
new file mode 100644
index 000000000..14521e307
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import DestructuringAndAwait from './DestructuringAndAwait';
+
+describe('destructuring and await', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<DestructuringAndAwait />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js
new file mode 100644
index 000000000..4f2fc95ca
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js
@@ -0,0 +1,35 @@
+import React from 'react'
+
+function * load(limit) {
+  let i = 1;
+  while (i <= limit) {
+    yield { id: i, name: i };
+    i++;
+  }
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  componentDidMount() {
+    const users = [];
+    for (let user of load(4)) {
+      users.push(user);
+    }
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-generators">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js
new file mode 100644
index 000000000..1fd36cdbe
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Generators from './Generators';
+
+describe('generators', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<Generators />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js
new file mode 100644
index 000000000..c6edbee27
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js
@@ -0,0 +1,34 @@
+import React from 'react'
+
+function load() {
+  return [
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-object-destructuring">
+        {this.state.users.map(user => {
+          const { id, name } = user;
+          return <div key={id}>{name}</div>
+        })}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js
new file mode 100644
index 000000000..7ed28147d
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ObjectDestructuring from './ObjectDestructuring';
+
+describe('object destructuring', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ObjectDestructuring />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js
new file mode 100644
index 000000000..ca41004f9
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load(baseUser) {
+  return [
+    { id: 1, name: '1', ...baseUser },
+    { id: 2, name: '2', ...baseUser },
+    { id: 3, name: '3', ...baseUser },
+    { id: 4, name: '4', ...baseUser }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load({ age: 42 });
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-object-spread">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}: {user.age}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js
new file mode 100644
index 000000000..9de96c264
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ObjectSpread from './ObjectSpread';
+
+describe('object spread', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ObjectSpread />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js
new file mode 100644
index 000000000..c0e13c81e
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js
@@ -0,0 +1,34 @@
+import React from 'react'
+
+function load() {
+  return Promise.resolve([
+    { id: 1, name: '1' },
+    { id: 2, name: '2' },
+    { id: 3, name: '3' },
+    { id: 4, name: '4' }
+  ]);
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  componentDidMount() {
+    load().then(users => {
+      this.setState({ users });
+    });
+  }
+
+  render() {
+    return (
+      <div id="feature-promises">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
new file mode 100644
index 000000000..96b4d298d
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Promises from './Promises';
+
+describe('promises', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<Promises />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js
new file mode 100644
index 000000000..a977e8c83
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load({ id, ...rest } = { id: 0, user: { id: 42, name: '42' } }) {
+  return [
+    { id: id + 1, name: '1' },
+    { id: id + 2, name: '2' },
+    { id: id + 3, name: '3' },
+    rest.user
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load();
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-rest-and-default">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js
new file mode 100644
index 000000000..95f4a19fa
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import RestAndDefault from './RestAndDefault';
+
+describe('rest + default', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<RestAndDefault />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js
new file mode 100644
index 000000000..9dcc33fa0
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load({ id = 0, ...rest }) {
+  return [
+    { id: id + 1, name: '1' },
+    { id: id + 2, name: '2' },
+    { id: id + 3, name: '3' },
+    rest.user
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load({ id: 0, user: { id: 42, name: '42' } });
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-rest-parameters">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js
new file mode 100644
index 000000000..8e0977133
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import RestParameters from './RestParameters';
+
+describe('rest parameters', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<RestParameters />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js
new file mode 100644
index 000000000..505ee6173
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+function load(name) {
+  return [
+    { id: 1, name: `${name}1` },
+    { id: 2, name: `${name}2` },
+    { id: 3, name: `${name}3` },
+    { id: 4, name: `${name}4` }
+  ];
+}
+
+export default class extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = { users: [] };
+  }
+
+  async componentDidMount() {
+    const users = load('user_');
+    this.setState({ users });
+  }
+
+  render() {
+    return (
+      <div id="feature-template-interpolation">
+        {this.state.users.map(user => (
+          <div key={user.id}>{user.name}</div>
+        ))}
+      </div>
+    );
+  }
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js
new file mode 100644
index 000000000..b49af029a
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import TemplateInterpolation from './TemplateInterpolation';
+
+describe('template interpolation', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<TemplateInterpolation />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js
new file mode 100644
index 000000000..f35d11e0a
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import './assets/style.css'
+
+export default () => (
+  <p id="feature-css-inclusion">We love useless text.</p>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js
new file mode 100644
index 000000000..f3e10a441
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import CssInclusion from './CssInclusion';
+
+describe('css inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<CssInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js
new file mode 100644
index 000000000..d793408a5
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import tiniestCat from './assets/tiniest-cat.jpg'
+
+export default () => (
+  <img id="feature-image-inclusion" src={tiniestCat} alt="tiniest cat" />
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js
new file mode 100644
index 000000000..85e68d51e
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ImageInclusion from './ImageInclusion';
+
+describe('image inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<ImageInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js
new file mode 100644
index 000000000..8050b9cfd
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import { abstract } from './assets/abstract.json'
+
+export default () => (
+  <summary id="feature-json-inclusion">{abstract}</summary>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js
new file mode 100644
index 000000000..70043bd05
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import JsonInclusion from './JsonInclusion';
+
+describe('JSON inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<JsonInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js
new file mode 100644
index 000000000..70b2d2b21
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import aFileWithoutExt from './assets/aFileWithoutExt'
+
+const text = aFileWithoutExt.includes('base64')
+  ? atob(aFileWithoutExt.split('base64,')[1]).trim()
+  : aFileWithoutExt
+
+export default () => (
+  <p id="feature-no-ext-inclusion">{text}.</p>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js
new file mode 100644
index 000000000..666f94f38
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import NoExtInclusion from './NoExtInclusion';
+
+describe('no ext inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<NoExtInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js
new file mode 100644
index 000000000..7c2fdb2e4
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import logo from './assets/logo.svg'
+
+export default () => (
+  <img id="feature-svg-inclusion" src={logo} alt="logo" />
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js
new file mode 100644
index 000000000..507da0140
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import SvgInclusion from './SvgInclusion';
+
+describe('svg inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<SvgInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js
new file mode 100644
index 000000000..30aeccd6f
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import aFileWithExtUnknown from './assets/aFileWithExt.unknown'
+
+const text = aFileWithExtUnknown.includes('base64')
+  ? atob(aFileWithExtUnknown.split('base64,')[1]).trim()
+  : aFileWithExtUnknown
+
+export default () => (
+  <p id="feature-unknown-ext-inclusion">{text}.</p>
+)
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js
new file mode 100644
index 000000000..46724e0a2
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import UnknownExtInclusion from './UnknownExtInclusion';
+
+describe('unknown ext inclusion', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<UnknownExtInclusion />, div);
+  });
+});
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown
new file mode 100644
index 000000000..a5de8eb32
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown
@@ -0,0 +1 @@
+Whoooo, spooky!
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt
new file mode 100644
index 000000000..dbcc1afda
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt
@@ -0,0 +1 @@
+This is just a file without an extension
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json
new file mode 100644
index 000000000..5d21a7168
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json
@@ -0,0 +1,3 @@
+{
+  "abstract": "This is an abstract."
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg
new file mode 100644
index 000000000..6b60c1042
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
+    <g fill="#61DAFB">
+        <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
+        <circle cx="420.9" cy="296.5" r="45.7"/>
+        <path d="M520.5 78.1z"/>
+    </g>
+</svg>
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css
new file mode 100644
index 000000000..c399d1aca
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css
@@ -0,0 +1,4 @@
+#feature-css-inclusion {
+  background: palevioletred;
+  color: papayawhip;
+}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c658e9922a765fa62442f996a1c4fa028c731f45
GIT binary patch
literal 691
zcmex=<NpH&0WUXCHwH#VMur3+WcdG&!P+^<)iK0B$VwqSMK`M;HC>_1P|rX?qqI0P
zFI~aY%U!`Mz|~!$%*;qrN1?DZF(<R6Qo&Zi+<@!<Z3brsE_QYfb~Y{!4i0WEE^a;v
z0X|+{K3P#QK?!AfRTU+9B}FxDbA2^U6HP@W14ly>OKUqjJ5@awPiGqsb6Y!WkRgm*
z+}wP;d@=$8GS(VO8rCF({|6WZIT#oj7?~NB7?=bZnFSgDA7PMZU|?hgdKKhbC}3n_
zW?^Mx=iubx1}fMpz`(@F%*@2X%*x8b0#scKlxJWOWEE00bYv3_Ok`Io6ftU?xR68H
zY2!iBpo<?=jFXC*IJv~cB_yR()zmdKwM<OS%q=XfoLyYq+&w(Kf<r>X!XqN1l2cOC
z(lau%ic3n%$}1|Xnp;}i+B-VCCQY6)b=ve9GiNPYykzOJ<ttXM+O&Dg)@|E&?A&$e
z@R6g(j-NPr>eA&aSFc^aar4&0M~|O8efIpt%U2&ieg5+G+xH(oe}VkP$iNKo7TjlO
z{t^WGi;0DWnS~wXFGi+vAZ8Y1VO2C_6LJh>Pb?HxGHT=yahkYr<3Ubk<Dd_sNktdA
u#8gZks(u7{4eT@GJk~^(&)^<I`0Ew}4>Kb$@|Xn~>>0YcG#2pxzX<@98RLln

literal 0
HcmV?d00001

diff --git a/packages/react-scripts/fixtures/kitchensink/src/index.js b/packages/react-scripts/fixtures/kitchensink/src/index.js
new file mode 100644
index 000000000..0ea36197b
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/index.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+ReactDOM.render(
+  <App />,
+  document.getElementById('root')
+);
diff --git a/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js
new file mode 100644
index 000000000..6c7375d54
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js
@@ -0,0 +1 @@
+module.exports = function() { return `haha` }
diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js
index e438e5a2e..864005eca 100644
--- a/packages/react-scripts/scripts/init.js
+++ b/packages/react-scripts/scripts/init.js
@@ -12,7 +12,7 @@ var path = require('path');
 var spawn = require('cross-spawn');
 var chalk = require('chalk');
 
-module.exports = function(appPath, appName, verbose, originalDirectory) {
+module.exports = function(appPath, appName, verbose, originalDirectory, template) {
   var ownPackageName = require(path.join(__dirname, '..', 'package.json')).name;
   var ownPath = path.join(appPath, 'node_modules', ownPackageName);
   var appPackage = require(path.join(appPath, 'package.json'));
@@ -41,7 +41,13 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
   }
 
   // Copy the files for the user
-  fs.copySync(path.join(ownPath, 'template'), appPath);
+  var templatePath = template ? path.resolve(originalDirectory, template) : path.join(ownPath, 'template');
+  if (fs.existsSync(templatePath)) {
+    fs.copySync(templatePath, appPath);
+  } else {
+    console.error('Could not locate supplied template: ' + chalk.green(templatePath));
+    return;
+  }
 
   // Rename gitignore after the fact to prevent npm from renaming it to .npmignore
   // See: https://github.com/npm/npm/issues/1862
@@ -76,6 +82,16 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
   }
   args.push('react', 'react-dom');
 
+  // Install additional template dependencies, if present
+  var templateDependenciesPath = path.join(appPath, '.template.dependencies.json');
+  if (fs.existsSync(templateDependenciesPath)) {
+    var templateDependencies = require(templateDependenciesPath).dependencies;
+    args = args.concat(Object.keys(templateDependencies).map(function (key) {
+      return key + '@' + templateDependencies[key];
+    }));
+    fs.unlinkSync(templateDependenciesPath);
+  }
+
   console.log('Installing react and react-dom using ' + command + '...');
   console.log();
 
diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh
new file mode 100755
index 000000000..f3c1d8d3b
--- /dev/null
+++ b/tasks/e2e-installs.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+# 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.
+
+# ******************************************************************************
+# This is an end-to-end test intended to run on CI.
+# You can also run it locally but it's slow.
+# ******************************************************************************
+
+# Start in tasks/ even if run from root directory
+cd "$(dirname "$0")"
+
+# CLI and app temporary locations
+# http://unix.stackexchange.com/a/84980
+temp_cli_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_cli_path'`
+temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'`
+
+function cleanup {
+  echo 'Cleaning up.'
+  cd $root_path
+  # Uncomment when snapshot testing is enabled by default:
+  # rm ./packages/react-scripts/template/src/__snapshots__/App.test.js.snap
+  rm -rf $temp_cli_path $temp_app_path
+}
+
+# Error messages are redirected to stderr
+function handle_error {
+  echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2;
+  cleanup
+  echo 'Exiting with error.' 1>&2;
+  exit 1
+}
+
+function handle_exit {
+  cleanup
+  echo 'Exiting without error.' 1>&2;
+  exit
+}
+
+function create_react_app {
+  node "$temp_cli_path"/node_modules/create-react-app/index.js $*
+}
+
+# Exit the script with a helpful error message when any error is encountered
+trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR
+
+# Cleanup before exit on any termination signal
+trap 'set +x; handle_exit' SIGQUIT SIGTERM SIGINT SIGKILL SIGHUP
+
+# Echo every command being executed
+set -x
+
+# Go to root
+cd ..
+root_path=$PWD
+
+npm install
+
+# If the node version is < 4, the script should just give an error.
+if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ]
+then
+  cd $temp_app_path
+  err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''`
+  [[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1
+fi
+
+if [ "$USE_YARN" = "yes" ]
+then
+  # Install Yarn so that the test can use it to install packages.
+  npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed.
+  yarn cache clean
+fi
+
+# ******************************************************************************
+# First, pack and install create-react-app.
+# ******************************************************************************
+
+# Pack CLI
+cd $root_path/packages/create-react-app
+cli_path=$PWD/`npm pack`
+
+# Install the CLI in a temporary location
+cd $temp_cli_path
+npm install $cli_path
+
+# ******************************************************************************
+# Test --scripts-version with a version number
+# ******************************************************************************
+
+cd $temp_app_path
+create_react_app --scripts-version=0.4.0 test-app-version-number
+cd test-app-version-number
+
+# Check corresponding scripts version is installed.
+test -e node_modules/react-scripts
+grep '"version": "0.4.0"' node_modules/react-scripts/package.json
+
+# ******************************************************************************
+# Test --scripts-version with a tarball url
+# ******************************************************************************
+
+cd $temp_app_path
+create_react_app --scripts-version=https://registry.npmjs.org/react-scripts/-/react-scripts-0.4.0.tgz test-app-tarball-url
+cd test-app-tarball-url
+
+# Check corresponding scripts version is installed.
+test -e node_modules/react-scripts
+grep '"version": "0.4.0"' node_modules/react-scripts/package.json
+
+# ******************************************************************************
+# Test --scripts-version with a custom fork of react-scripts
+# ******************************************************************************
+
+cd $temp_app_path
+create_react_app --scripts-version=react-scripts-fork test-app-fork
+cd test-app-fork
+
+# Check corresponding scripts version is installed.
+test -e node_modules/react-scripts-fork
+
+# ******************************************************************************
+# Test nested folder path as the project name
+# ******************************************************************************
+
+#Testing a path that exists
+cd $temp_app_path
+mkdir test-app-nested-paths-t1
+cd test-app-nested-paths-t1
+mkdir -p test-app-nested-paths-t1/aa/bb/cc/dd
+create_react_app test-app-nested-paths-t1/aa/bb/cc/dd
+cd test-app-nested-paths-t1/aa/bb/cc/dd
+npm start -- --smoke-test
+
+#Testing a path that does not exist
+cd $temp_app_path
+create_react_app test-app-nested-paths-t2/aa/bb/cc/dd
+cd test-app-nested-paths-t2/aa/bb/cc/dd
+npm start -- --smoke-test
+
+#Testing a path that is half exists
+cd $temp_app_path
+mkdir -p test-app-nested-paths-t3/aa
+create_react_app test-app-nested-paths-t3/aa/bb/cc/dd
+cd test-app-nested-paths-t3/aa/bb/cc/dd
+npm start -- --smoke-test
+
+# Cleanup
+cleanup
diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh
new file mode 100755
index 000000000..eecd6ed62
--- /dev/null
+++ b/tasks/e2e-kitchensink.sh
@@ -0,0 +1,218 @@
+#!/bin/bash
+# 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.
+
+# ******************************************************************************
+# This is an end-to-end kitchensink test intended to run on CI.
+# You can also run it locally but it's slow.
+# ******************************************************************************
+
+# Start in tasks/ even if run from root directory
+cd "$(dirname "$0")"
+
+# CLI and app temporary locations
+# http://unix.stackexchange.com/a/84980
+temp_cli_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_cli_path'`
+temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'`
+
+function cleanup {
+  echo 'Cleaning up.'
+  cd $root_path
+  # Uncomment when snapshot testing is enabled by default:
+  # rm ./packages/react-scripts/template/src/__snapshots__/App.test.js.snap
+  rm -rf $temp_cli_path $temp_app_path
+}
+
+# Error messages are redirected to stderr
+function handle_error {
+  echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2;
+  cleanup
+  echo 'Exiting with error.' 1>&2;
+  exit 1
+}
+
+function handle_exit {
+  cleanup
+  echo 'Exiting without error.' 1>&2;
+  exit
+}
+
+function create_react_app {
+  node "$temp_cli_path"/node_modules/create-react-app/index.js $*
+}
+
+# Exit the script with a helpful error message when any error is encountered
+trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR
+
+# Cleanup before exit on any termination signal
+trap 'set +x; handle_exit' SIGQUIT SIGTERM SIGINT SIGKILL SIGHUP
+
+# Echo every command being executed
+set -x
+
+# Go to root
+cd ..
+root_path=$PWD
+
+npm install
+
+# If the node version is < 4, the script should just give an error.
+if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ]
+then
+  cd $temp_app_path
+  err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''`
+  [[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1
+fi
+
+if [ "$USE_YARN" = "yes" ]
+then
+  # Install Yarn so that the test can use it to install packages.
+  npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed.
+  yarn cache clean
+fi
+
+# ******************************************************************************
+# First, pack react-scripts and create-react-app so we can use them.
+# ******************************************************************************
+
+# Pack CLI
+cd $root_path/packages/create-react-app
+cli_path=$PWD/`npm pack`
+
+# Go to react-scripts
+cd $root_path/packages/react-scripts
+
+# Save package.json because we're going to touch it
+cp package.json package.json.orig
+
+# Replace own dependencies (those in the `packages` dir) with the local paths
+# of those packages.
+node $root_path/tasks/replace-own-deps.js
+
+# Remove .npmignore so the test template is added
+rm $root_path/packages/react-scripts/.npmignore
+
+# Finally, pack react-scripts
+scripts_path=$root_path/packages/react-scripts/`npm pack`
+
+# Restore package.json
+rm package.json
+mv package.json.orig package.json
+
+# ******************************************************************************
+# Now that we have packed them, create a clean app folder and install them.
+# ******************************************************************************
+
+# Install the CLI in a temporary location
+cd $temp_cli_path
+npm install $cli_path
+
+# Install the app in a temporary location
+cd $temp_app_path
+create_react_app --scripts-version=$scripts_path --internal-testing-template=$root_path/packages/react-scripts/fixtures/kitchensink test-kitchensink
+
+# ******************************************************************************
+# Now that we used create-react-app to create an app depending on react-scripts,
+# let's make sure all npm scripts are in the working state.
+# ******************************************************************************
+
+# Enter the app directory
+cd test-kitchensink
+
+# Test the build
+NODE_PATH=src REACT_APP_SHELL_ENV_MESSAGE=fromtheshell npm run build
+# Check for expected output
+test -e build/*.html
+test -e build/static/js/main.*.js
+
+# Unit tests
+REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  CI=true \
+  NODE_PATH=src \
+  npm test -- --no-cache --testPathPattern="/src/"
+
+# Test "development" environment
+tmp_server_log=`mktemp`
+PORT=3001 \
+  REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  NODE_PATH=src \
+  nohup npm start &>$tmp_server_log &
+grep -q 'The app is running at:' <(tail -f $tmp_server_log)
+E2E_URL="http://localhost:3001" \
+  REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  CI=true NODE_PATH=src \
+  node node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
+
+# Test "production" environment
+E2E_FILE=./build/index.html \
+  CI=true \
+  NODE_PATH=src \
+  node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js
+
+# Uncomment when snapshot testing is enabled by default:
+# test -e src/__snapshots__/App.test.js.snap
+
+# Test the server
+REACT_APP_SHELL_ENV_MESSAGE=fromtheshell NODE_PATH=src npm start -- --smoke-test
+REACT_APP_SHELL_ENV_MESSAGE=fromtheshell HTTPS=true NODE_PATH=src npm start -- --smoke-test
+
+# ******************************************************************************
+# Finally, let's check that everything still works after ejecting.
+# ******************************************************************************
+
+# Eject...
+echo yes | npm run eject
+
+# ...but still link to the local packages
+npm link $root_path/packages/babel-preset-react-app
+npm link $root_path/packages/eslint-config-react-app
+npm link $root_path/packages/react-dev-utils
+npm link $root_path/packages/react-scripts
+
+# ...and we need  to remove template's .babelrc
+rm .babelrc
+
+# Test the build
+NODE_PATH=src REACT_APP_SHELL_ENV_MESSAGE=fromtheshell npm run build
+# Check for expected output
+test -e build/*.html
+test -e build/static/js/main.*.js
+
+# Unit tests
+REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  CI=true \
+  NODE_PATH=src \
+  npm test -- --no-cache --testPathPattern="/src/"
+
+# Test "development" environment
+tmp_server_log=`mktemp`
+PORT=3002 \
+  REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  NODE_PATH=src \
+  nohup npm start &>$tmp_server_log &
+grep -q 'The app is running at:' <(tail -f $tmp_server_log)
+E2E_URL="http://localhost:3002" \
+  REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
+  CI=true NODE_PATH=src \
+  NODE_ENV=production \
+  node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js
+
+# Test "production" environment
+E2E_FILE=./build/index.html \
+  CI=true \
+  NODE_ENV=production \
+  NODE_PATH=src \
+  node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js
+
+# Uncomment when snapshot testing is enabled by default:
+# test -e src/__snapshots__/App.test.js.snap
+
+# Test the server
+REACT_APP_SHELL_ENV_MESSAGE=fromtheshell NODE_PATH=src npm start -- --smoke-test
+
+# Cleanup
+cleanup
diff --git a/tasks/e2e.sh b/tasks/e2e-simple.sh
similarity index 73%
rename from tasks/e2e.sh
rename to tasks/e2e-simple.sh
index 27cd0ae45..f8baf743d 100755
--- a/tasks/e2e.sh
+++ b/tasks/e2e-simple.sh
@@ -195,66 +195,6 @@ npm test -- --watch=no
 # Test the server
 npm start -- --smoke-test
 
-# ******************************************************************************
-# Test --scripts-version with a version number
-# ******************************************************************************
-
-cd $temp_app_path
-create_react_app --scripts-version=0.4.0 test-app-version-number
-cd test-app-version-number
-
-# Check corresponding scripts version is installed.
-test -e node_modules/react-scripts
-grep '"version": "0.4.0"' node_modules/react-scripts/package.json
-
-# ******************************************************************************
-# Test --scripts-version with a tarball url
-# ******************************************************************************
-
-cd $temp_app_path
-create_react_app --scripts-version=https://registry.npmjs.org/react-scripts/-/react-scripts-0.4.0.tgz test-app-tarball-url
-cd test-app-tarball-url
-
-# Check corresponding scripts version is installed.
-test -e node_modules/react-scripts
-grep '"version": "0.4.0"' node_modules/react-scripts/package.json
-
-# ******************************************************************************
-# Test --scripts-version with a custom fork of react-scripts
-# ******************************************************************************
-
-cd $temp_app_path
-create_react_app --scripts-version=react-scripts-fork test-app-fork
-cd test-app-fork
-
-# Check corresponding scripts version is installed.
-test -e node_modules/react-scripts-fork
-
-# ******************************************************************************
-# Test nested folder path as the project name
-# ******************************************************************************
-
-#Testing a path that exists
-cd $temp_app_path
-mkdir test-app-nested-paths-t1
-cd test-app-nested-paths-t1
-mkdir -p test-app-nested-paths-t1/aa/bb/cc/dd
-create_react_app test-app-nested-paths-t1/aa/bb/cc/dd
-cd test-app-nested-paths-t1/aa/bb/cc/dd
-npm start -- --smoke-test
-
-#Testing a path that does not exist
-cd $temp_app_path
-create_react_app test-app-nested-paths-t2/aa/bb/cc/dd
-cd test-app-nested-paths-t2/aa/bb/cc/dd
-npm start -- --smoke-test
-
-#Testing a path that is half exists
-cd $temp_app_path
-mkdir -p test-app-nested-paths-t3/aa
-create_react_app test-app-nested-paths-t3/aa/bb/cc/dd
-cd test-app-nested-paths-t3/aa/bb/cc/dd
-npm start -- --smoke-test
 
 # Cleanup
 cleanup
-- 
GitLab