From adab23fa0093846e0ec5349c0f8f7ad6e66fcb5d Mon Sep 17 00:00:00 2001
From: Joe Haddad <timer150@gmail.com>
Date: Sat, 4 Feb 2017 12:43:27 -0500
Subject: [PATCH] e2e: Reduce complexity of e2e and improve Jest coverage
 (#1484)

---
 .../fixtures/kitchensink/.babelrc             |  2 +-
 .../kitchensink/.template.dependencies.json   |  1 -
 .../fixtures/kitchensink/src/App.js           | 53 ++++++++++++-------
 .../fixtures/kitchensink/src/App.test.js      |  8 ---
 .../kitchensink/src/features/env/NodePath.js  | 20 +++----
 .../src/features/env/NodePath.test.js         |  4 +-
 .../src/features/syntax/ArrayDestructuring.js | 20 +++----
 .../syntax/ArrayDestructuring.test.js         |  4 +-
 .../src/features/syntax/ArraySpread.js        | 20 +++----
 .../src/features/syntax/ArraySpread.test.js   |  4 +-
 .../src/features/syntax/AsyncAwait.js         | 20 +++----
 .../src/features/syntax/AsyncAwait.test.js    |  4 +-
 .../src/features/syntax/ClassProperties.js    | 12 ++++-
 .../features/syntax/ClassProperties.test.js   |  4 +-
 .../src/features/syntax/ComputedProperties.js | 20 +++----
 .../syntax/ComputedProperties.test.js         |  4 +-
 .../features/syntax/CustomInterpolation.js    | 20 +++----
 .../syntax/CustomInterpolation.test.js        |  4 +-
 .../src/features/syntax/DefaultParameters.js  | 20 +++----
 .../features/syntax/DefaultParameters.test.js |  4 +-
 .../features/syntax/DestructuringAndAwait.js  | 20 +++----
 .../syntax/DestructuringAndAwait.test.js      |  4 +-
 .../src/features/syntax/Generators.js         | 20 +++----
 .../src/features/syntax/Generators.test.js    |  4 +-
 .../features/syntax/ObjectDestructuring.js    | 20 +++----
 .../syntax/ObjectDestructuring.test.js        |  4 +-
 .../src/features/syntax/ObjectSpread.js       | 20 +++----
 .../src/features/syntax/ObjectSpread.test.js  |  4 +-
 .../src/features/syntax/Promises.js           | 20 +++----
 .../src/features/syntax/Promises.test.js      |  4 +-
 .../src/features/syntax/RestAndDefault.js     | 20 +++----
 .../features/syntax/RestAndDefault.test.js    |  4 +-
 .../src/features/syntax/RestParameters.js     | 20 +++----
 .../features/syntax/RestParameters.test.js    |  4 +-
 .../features/syntax/TemplateInterpolation.js  | 20 +++----
 .../syntax/TemplateInterpolation.test.js      |  4 +-
 tasks/e2e-kitchensink.sh                      | 14 ++++-
 37 files changed, 269 insertions(+), 185 deletions(-)
 delete mode 100644 packages/react-scripts/fixtures/kitchensink/src/App.test.js

diff --git a/packages/react-scripts/fixtures/kitchensink/.babelrc b/packages/react-scripts/fixtures/kitchensink/.babelrc
index 5686105b9..c14b2828d 100644
--- a/packages/react-scripts/fixtures/kitchensink/.babelrc
+++ b/packages/react-scripts/fixtures/kitchensink/.babelrc
@@ -1,3 +1,3 @@
 {
-  "presets": ["latest"]
+  "presets": ["react-app"]
 }
diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
index 62e0d34a3..50511b3d1 100644
--- a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
+++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
@@ -1,6 +1,5 @@
 {
   "dependencies": {
-    "babel-preset-latest": "6.16.0",
     "babel-register": "6.22.0",
     "babel-polyfill": "6.20.0",
     "chai": "3.5.0",
diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js
index cf93b4c13..fa297133f 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/App.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/App.js
@@ -1,28 +1,40 @@
-import React from 'react';
+import React, { Component, PropTypes, createElement } from 'react';
 
-class BuiltEmitter extends React.Component {
-  constructor(props) {
-    super(props)
-
-    this.callWhenDone = done => done();
+class BuiltEmitter extends Component {
+  static propTypes = {
+    feature: PropTypes.func.isRequired
   }
 
   componentDidMount() {
-    this.callWhenDone(() => document.dispatchEvent(new Event('ReactFeatureDidMount')));
+    const { feature } = this.props
+
+    // Class components must call this.props.onReady when they're ready for the test.
+    // We will assume functional components are ready immediately after mounting.
+    if (!Component.isPrototypeOf(feature)) {
+      this.handleReady();
+    }
   }
 
-  render() {
-    const feature = React.cloneElement(React.Children.only(this.props.children), {
-      setCallWhenDone: done => {
-        this.callWhenDone = done;
-      }
-    });
+  handleReady() {
+    document.dispatchEvent(new Event('ReactFeatureDidMount'));
+  }
 
-    return <div>{feature}</div>;
+  render() {
+    const {
+      props: { feature },
+      handleReady
+    } = this;
+    return (
+      <div>
+        {createElement(feature, {
+          onReady: handleReady
+        })}
+      </div>
+    );
   }
 }
 
-class App extends React.Component {
+class App extends Component {
   constructor(props) {
     super(props);
 
@@ -105,9 +117,7 @@ class App extends React.Component {
       case 'unknown-ext-inclusion':
         require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default));
         break;
-      default:
-        this.setFeature(null);
-        break;
+      default: throw new Error('Unknown feature!');
     }
   }
 
@@ -116,8 +126,11 @@ class App extends React.Component {
   }
 
   render() {
-    const Feature = this.state.feature;
-    return Feature ? <BuiltEmitter><Feature /></BuiltEmitter> : null;
+    const { feature } = this.state;
+    if (feature !== null) {
+      return <BuiltEmitter feature={feature} />;
+    }
+    return null;
   }
 }
 
diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.test.js b/packages/react-scripts/fixtures/kitchensink/src/App.test.js
deleted file mode 100644
index b84af98d7..000000000
--- a/packages/react-scripts/fixtures/kitchensink/src/App.test.js
+++ /dev/null
@@ -1,8 +0,0 @@
-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/features/env/NodePath.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
index 1644b49ca..deef80d20 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js
@@ -1,21 +1,23 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 import load from 'absoluteLoad'
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 05b981853..81487d559 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js
@@ -5,6 +5,8 @@ import NodePath from './NodePath';
 describe('NODE_PATH', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<NodePath />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<NodePath onReady={resolve} />, 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
index 1ee751af7..d07271ed6 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load() {
   return [
@@ -9,21 +9,23 @@ function load() {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 617df2a6c..05d14263d 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js
@@ -5,6 +5,8 @@ import ArrayDestructuring from './ArrayDestructuring';
 describe('array destructuring', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ArrayDestructuring />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ArrayDestructuring onReady={resolve} />, 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
index be6311980..6cb2f6162 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load(users) {
   return [
@@ -9,21 +9,23 @@ function load(users) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load([{ id: 42, name: '42' }]);
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 85fade6e3..a2191cf1c 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js
@@ -5,6 +5,8 @@ import ArraySpread from './ArraySpread';
 describe('array spread', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ArraySpread />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ArraySpread onReady={resolve} />, 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
index 84dd42e0a..9fe920514 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 async function load() {
   return [
@@ -9,21 +9,23 @@ async function load() {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = await load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 072f16fff..bc60c5b58 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js
@@ -5,6 +5,8 @@ import AsyncAwait from './AsyncAwait';
 describe('async/await', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<AsyncAwait />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<AsyncAwait onReady={resolve} />, 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
index 65e500d64..726eba152 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js
@@ -1,6 +1,10 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
+
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
 
-export default class extends React.Component {
   users = [
     { id: 1, name: '1' },
     { id: 2, name: '2' },
@@ -8,6 +12,10 @@ export default class extends React.Component {
     { id: 4, name: '4' }
   ];
 
+  componentDidMount() {
+    this.props.onReady()
+  }
+
   render() {
     return (
       <div id="feature-class-properties">
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
index 71d851dd2..898916b2f 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js
@@ -5,6 +5,8 @@ import ClassProperties from './ClassProperties';
 describe('class properties', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ClassProperties />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ClassProperties onReady={resolve} />, 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
index b6111a149..04dc7ba10 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load(prefix) {
   return [
@@ -9,21 +9,23 @@ function load(prefix) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load('user_');
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 4e9aaf17a..0aa3d4d7e 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js
@@ -5,6 +5,8 @@ import ComputedProperties from './ComputedProperties';
 describe('computed properties', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ComputedProperties />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ComputedProperties onReady={resolve} />, 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
index 8184d3bd4..c80f14dc8 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 const styled = ([style]) => style.trim()
   .split(/\s*;\s*/)
@@ -14,21 +14,23 @@ function load() {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 10b1df278..79af8dc5f 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js
@@ -5,6 +5,8 @@ import CustomInterpolation from './CustomInterpolation';
 describe('custom interpolation', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<CustomInterpolation />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<CustomInterpolation onReady={resolve} />, 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
index 637a239d7..0d7b77d87 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load(id = 0) {
   return [
@@ -9,21 +9,23 @@ function load(id = 0) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index b5ece2446..c4a336563 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js
@@ -5,6 +5,8 @@ import DefaultParameters from './DefaultParameters';
 describe('default parameters', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<DefaultParameters />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<DefaultParameters onReady={resolve} />, 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
index e28e0bb36..40f517542 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 async function load() {
   return { users: [
@@ -9,21 +9,23 @@ async function load() {
   ] };
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const { users } = await load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 14521e307..96b361c74 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js
@@ -5,6 +5,8 @@ import DestructuringAndAwait from './DestructuringAndAwait';
 describe('destructuring and await', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<DestructuringAndAwait />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<DestructuringAndAwait onReady={resolve} />, 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
index a20fc19b7..6bb64f910 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function * load(limit) {
   let i = 1;
@@ -8,15 +8,13 @@ function * load(limit) {
   }
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
@@ -25,7 +23,11 @@ export default class extends React.Component {
     for (let user of load(4)) {
       users.push(user);
     }
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 1fd36cdbe..4e6b8d1d9 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js
@@ -5,6 +5,8 @@ import Generators from './Generators';
 describe('generators', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<Generators />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<Generators onReady={resolve} />, 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
index db377cee5..3235a9910 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load() {
   return [
@@ -9,21 +9,23 @@ function load() {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 7ed28147d..0a21b291b 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js
@@ -5,6 +5,8 @@ import ObjectDestructuring from './ObjectDestructuring';
 describe('object destructuring', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ObjectDestructuring />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ObjectDestructuring onReady={resolve} />, 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
index 72356fb94..fb43bf867 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load(baseUser) {
   return [
@@ -9,21 +9,23 @@ function load(baseUser) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load({ age: 42 });
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 9de96c264..4ca5d2b9a 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js
@@ -5,6 +5,8 @@ import ObjectSpread from './ObjectSpread';
 describe('object spread', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<ObjectSpread />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<ObjectSpread onReady={resolve} />, 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
index 9eb8c20f7..63f38fed4 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load() {
   return Promise.resolve([
@@ -9,24 +9,26 @@ function load() {
   ]);
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   componentDidMount() {
     load().then(users => {
-      this.setState({ users }, () => this.done());
+      this.setState({ users });
     });
   }
 
+  componentDidUpdate() {
+    this.props.onReady();
+  }
+
   render() {
     return (
       <div id="feature-promises">
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
index 96b4d298d..36c5984a4 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
@@ -5,6 +5,8 @@ import Promises from './Promises';
 describe('promises', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<Promises />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<Promises onReady={resolve} />, 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
index 94b759808..7783868ab 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load({ id, ...rest } = { id: 0, user: { id: 42, name: '42' } }) {
   return [
@@ -9,21 +9,23 @@ function load({ id, ...rest } = { id: 0, user: { id: 42, name: '42' } }) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load();
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 95f4a19fa..22a91be08 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js
@@ -5,6 +5,8 @@ import RestAndDefault from './RestAndDefault';
 describe('rest + default', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<RestAndDefault />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<RestAndDefault onReady={resolve} />, 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
index c1cd63e88..cf216ce01 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load({ id = 0, ...rest }) {
   return [
@@ -9,21 +9,23 @@ function load({ id = 0, ...rest }) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load({ id: 0, user: { id: 42, name: '42' } });
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index 8e0977133..f1f8e35e3 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js
@@ -5,6 +5,8 @@ import RestParameters from './RestParameters';
 describe('rest parameters', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<RestParameters />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<RestParameters onReady={resolve} />, 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
index 33b004722..dd6bf49b1 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Component, PropTypes } from 'react'
 
 function load(name) {
   return [
@@ -9,21 +9,23 @@ function load(name) {
   ];
 }
 
-export default class extends React.Component {
+export default class extends Component {
+  static propTypes = {
+    onReady: PropTypes.func.isRequired
+  }
+
   constructor(props) {
     super(props);
-
-    this.done = () => {};
-    this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
-      this.done = done;
-    });
-
     this.state = { users: [] };
   }
 
   async componentDidMount() {
     const users = load('user_');
-    this.setState({ users }, () => this.done());
+    this.setState({ users });
+  }
+
+  componentDidUpdate() {
+    this.props.onReady();
   }
 
   render() {
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
index b49af029a..41a5fad2c 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js
@@ -5,6 +5,8 @@ import TemplateInterpolation from './TemplateInterpolation';
 describe('template interpolation', () => {
   it('renders without crashing', () => {
     const div = document.createElement('div');
-    ReactDOM.render(<TemplateInterpolation />, div);
+    return new Promise(resolve => {
+      ReactDOM.render(<TemplateInterpolation onReady={resolve} />, div);
+    });
   });
 });
diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh
index 892230ab7..c681580b0 100755
--- a/tasks/e2e-kitchensink.sh
+++ b/tasks/e2e-kitchensink.sh
@@ -110,6 +110,9 @@ create_react_app --scripts-version=$scripts_path --internal-testing-template=$ro
 # Enter the app directory
 cd test-kitchensink
 
+# Link to our preset
+npm link $root_path/packages/babel-preset-react-app
+
 # Test the build
 NODE_PATH=src REACT_APP_SHELL_ENV_MESSAGE=fromtheshell npm run build
 # Check for expected output
@@ -120,6 +123,7 @@ test -e build/static/js/main.*.js
 REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
   CI=true \
   NODE_PATH=src \
+  NODE_ENV=test \
   npm test -- --no-cache --testPathPattern="/src/"
 
 # Test "development" environment
@@ -132,18 +136,23 @@ 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_ENV=development \
   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_ENV=production \
   node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
 
 # ******************************************************************************
 # Finally, let's check that everything still works after ejecting.
 # ******************************************************************************
 
+# Unlink our preset
+npm unlink $root_path/packages/babel-preset-react-app
+
 # Eject...
 echo yes | npm run eject
 
@@ -153,7 +162,7 @@ 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
+# ...and we need to remove template's .babelrc
 rm .babelrc
 
 # Test the build
@@ -166,6 +175,7 @@ test -e build/static/js/main.*.js
 REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
   CI=true \
   NODE_PATH=src \
+  NODE_ENV=test \
   npm test -- --no-cache --testPathPattern="/src/"
 
 # Test "development" environment
@@ -178,7 +188,7 @@ 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_ENV=development \
   node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
 
 # Test "production" environment
-- 
GitLab