diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
index 0a42b7d4cd14dc9505a90fc05101d774b989aa3c..e45cf8218e132094432dc7c13142f4a91e40fa4e 100644
--- a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
+++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json
@@ -1,12 +1,7 @@
 {
   "dependencies": {
-    "@babel/plugin-transform-modules-commonjs": "7.0.0-beta.46",
-    "@babel/polyfill": "7.0.0-beta.46",
-    "@babel/register": "7.0.0-beta.46",
-    "bootstrap": "4.1.0",
-    "chai": "3.5.0",
-    "jsdom": "9.8.3",
-    "mocha": "3.2.0",
+    "bootstrap": "4.1.1",
+    "jest": "23.6.0",
     "node-sass": "4.8.3",
     "normalize.css": "7.0.0",
     "prop-types": "15.5.6",
diff --git a/packages/react-scripts/fixtures/kitchensink/README.md b/packages/react-scripts/fixtures/kitchensink/README.md
index 4e7725ce15f3b4ac9b9a4a30cd2b6c55bf88aeeb..0e64a76aed38b5ba5fc41857bcaa0c5807539741 100644
--- a/packages/react-scripts/fixtures/kitchensink/README.md
+++ b/packages/react-scripts/fixtures/kitchensink/README.md
@@ -51,4 +51,3 @@ An usual flow for the test itself is something similar to:
 
  - since `initDOM` returns a `Document` element, the previous `id` attribute is used to target the feature's DOM and `expect` accordingly
 
-These tests are run by **mocha** (why not **jest**? See [this issue](https://github.com/facebook/jest/issues/2288)) and the environments used are both `development` and `production`.
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js
index b1f5f5d55441d4632e0f027f7505f2a4b599c56f..79de16706dc9214ada3a8c88908b85341c0e9675 100644
--- a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js
@@ -5,7 +5,6 @@
  * LICENSE file in the root directory of this source tree.
  */
 
-import { expect } from 'chai';
 import initDOM from './initDOM';
 
 describe('Integration', () => {
@@ -15,23 +14,23 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-file-env-original-1').textContent
-      ).to.equal('from-original-env-1');
+      ).toBe('from-original-env-1');
       expect(
         doc.getElementById('feature-file-env-original-2').textContent
-      ).to.equal('override-from-original-local-env-2');
+      ).toBe('override-from-original-local-env-2');
 
       if (process.env.NODE_ENV === 'production') {
-        expect(doc.getElementById('feature-file-env').textContent).to.equal(
+        expect(doc.getElementById('feature-file-env').textContent).toBe(
           'production'
         );
-        expect(doc.getElementById('feature-file-env-x').textContent).to.equal(
+        expect(doc.getElementById('feature-file-env-x').textContent).toBe(
           'x-from-production-env'
         );
       } else {
-        expect(doc.getElementById('feature-file-env').textContent).to.equal(
+        expect(doc.getElementById('feature-file-env').textContent).toBe(
           'development'
         );
-        expect(doc.getElementById('feature-file-env-x').textContent).to.equal(
+        expect(doc.getElementById('feature-file-env-x').textContent).toBe(
           'x-from-development-env'
         );
       }
@@ -41,9 +40,7 @@ describe('Integration', () => {
     it('NODE_PATH', async () => {
       const doc = await initDOM('node-path');
 
-      expect(
-        doc.getElementById('feature-node-path').childElementCount
-      ).to.equal(4);
+      expect(doc.getElementById('feature-node-path').childElementCount).toBe(4);
       doc.defaultView.close();
     });
 
@@ -54,12 +51,12 @@ describe('Integration', () => {
         process.env.NODE_ENV === 'development'
           ? ''
           : 'http://www.example.org/spa';
-      expect(doc.getElementById('feature-public-url').textContent).to.equal(
+      expect(doc.getElementById('feature-public-url').textContent).toBe(
         `${prefix}.`
       );
       expect(
         doc.querySelector('head link[rel="shortcut icon"]').getAttribute('href')
-      ).to.equal(`${prefix}/favicon.ico`);
+      ).toBe(`${prefix}/favicon.ico`);
       doc.defaultView.close();
     });
 
@@ -68,25 +65,25 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-shell-env-variables').textContent
-      ).to.equal('fromtheshell.');
+      ).toBe('fromtheshell.');
       doc.defaultView.close();
     });
 
     it('expand .env variables', async () => {
       const doc = await initDOM('expand-env-variables');
 
-      expect(doc.getElementById('feature-expand-env-1').textContent).to.equal(
+      expect(doc.getElementById('feature-expand-env-1').textContent).toBe(
         'basic'
       );
-      expect(doc.getElementById('feature-expand-env-2').textContent).to.equal(
+      expect(doc.getElementById('feature-expand-env-2').textContent).toBe(
         'basic'
       );
-      expect(doc.getElementById('feature-expand-env-3').textContent).to.equal(
+      expect(doc.getElementById('feature-expand-env-3').textContent).toBe(
         'basic'
       );
       expect(
         doc.getElementById('feature-expand-env-existing').textContent
-      ).to.equal('fromtheshell');
+      ).toBe('fromtheshell');
       doc.defaultView.close();
     });
   });
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
index 0ed5a9408911988a6f923be68acb38591f96281a..e807888d39722d5fef084888e94dde8daa846d4c 100644
--- a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
+++ b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
@@ -7,9 +7,8 @@
 
 const fs = require('fs');
 const http = require('http');
-const jsdom = require('jsdom');
+const jsdom = require('jsdom/lib/old-api.js');
 const path = require('path');
-const { expect } = require('chai');
 
 let getMarkup;
 export let resourceLoader;
@@ -50,7 +49,7 @@ if (process.env.E2E_FILE) {
   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.")
-    ).to.be.undefined();
+    ).toBeUndefined();
   });
 }
 
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
index 9e34b669d6e11919ba7a1c7f2964420175cb8f69..54920726fa5d07c5d55a6de04338610f60d12a45 100644
--- a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js
@@ -5,7 +5,6 @@
  * LICENSE file in the root directory of this source tree.
  */
 
-import { expect } from 'chai';
 import initDOM from './initDOM';
 
 describe('Integration', () => {
@@ -15,25 +14,25 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-array-destructuring').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
     it('array spread', async () => {
       const doc = await initDOM('array-spread');
 
-      expect(
-        doc.getElementById('feature-array-spread').childElementCount
-      ).to.equal(4);
+      expect(doc.getElementById('feature-array-spread').childElementCount).toBe(
+        4
+      );
       doc.defaultView.close();
     });
 
     it('async/await', async () => {
       const doc = await initDOM('async-await');
 
-      expect(
-        doc.getElementById('feature-async-await').childElementCount
-      ).to.equal(4);
+      expect(doc.getElementById('feature-async-await').childElementCount).toBe(
+        4
+      );
       doc.defaultView.close();
     });
 
@@ -42,7 +41,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-class-properties').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -51,7 +50,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-computed-properties').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -60,7 +59,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-custom-interpolation').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -69,7 +68,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-default-parameters').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -78,16 +77,16 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-destructuring-and-await').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
     it('generators', async () => {
       const doc = await initDOM('generators');
 
-      expect(
-        doc.getElementById('feature-generators').childElementCount
-      ).to.equal(4);
+      expect(doc.getElementById('feature-generators').childElementCount).toBe(
+        4
+      );
       doc.defaultView.close();
     });
 
@@ -96,7 +95,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-object-destructuring').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -105,16 +104,14 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-object-spread').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
     it('promises', async () => {
       const doc = await initDOM('promises');
 
-      expect(doc.getElementById('feature-promises').childElementCount).to.equal(
-        4
-      );
+      expect(doc.getElementById('feature-promises').childElementCount).toBe(4);
       doc.defaultView.close();
     });
 
@@ -123,7 +120,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-rest-and-default').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -132,7 +129,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-rest-parameters').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
 
@@ -141,7 +138,7 @@ describe('Integration', () => {
 
       expect(
         doc.getElementById('feature-template-interpolation').childElementCount
-      ).to.equal(4);
+      ).toBe(4);
       doc.defaultView.close();
     });
   });
diff --git a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
index 4aca73603dd56f47d22670a1896777355402d1df..ebd830dfaaa782970aeb89f1c8e4b8669d2e6121 100644
--- a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js
@@ -5,7 +5,6 @@
  * LICENSE file in the root directory of this source tree.
  */
 
-import { expect } from 'chai';
 import initDOM, { resourceLoader } from './initDOM';
 import url from 'url';
 
@@ -20,14 +19,14 @@ const matchCSS = (doc, regexes) => {
     }
     resourceLoader({ url: url.parse(href) }, (_, textContent) => {
       for (const regex of regexes) {
-        expect(textContent).to.match(regex);
+        expect(textContent).toMatch(regex);
       }
     });
   } else {
     for (let i = 0; i < regexes.length; ++i) {
       expect(
         doc.getElementsByTagName('style')[i].textContent.replace(/\s/g, '')
-      ).to.match(regexes[i]);
+      ).toMatch(regexes[i]);
     }
   }
 };
@@ -87,7 +86,7 @@ describe('Integration', () => {
       const children = doc.getElementById('graphql-inclusion').children;
 
       // .graphql
-      expect(children[0].textContent.replace(/\s/g, '')).to.equal(
+      expect(children[0].textContent.replace(/\s/g, '')).toBe(
         '{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"test"},"value":{"kind":"StringValue","value":"test","block":false}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[],"directives":[]}]}}]}}],"loc":{"start":0,"end":40,"source":{"body":"{\\ntest(test:\\"test\\"){\\ntest\\n}\\n}\\n","name":"GraphQLrequest","locationOffset":{"line":1,"column":1}}}}'
       );
       doc.defaultView.close();
@@ -96,7 +95,7 @@ describe('Integration', () => {
     it('image inclusion', async () => {
       const doc = await initDOM('image-inclusion');
 
-      expect(doc.getElementById('feature-image-inclusion').src).to.match(
+      expect(doc.getElementById('feature-image-inclusion').src).toMatch(
         /^data:image\/jpeg;base64.+==$/
       );
       doc.defaultView.close();
@@ -105,7 +104,7 @@ describe('Integration', () => {
     it('no ext inclusion', async () => {
       const doc = await initDOM('no-ext-inclusion');
 
-      expect(doc.getElementById('feature-no-ext-inclusion').href).to.match(
+      expect(doc.getElementById('feature-no-ext-inclusion').href).toMatch(
         /\/static\/media\/aFileWithoutExt\.[a-f0-9]{8}\.bin$/
       );
       doc.defaultView.close();
@@ -114,7 +113,7 @@ describe('Integration', () => {
     it('json inclusion', async () => {
       const doc = await initDOM('json-inclusion');
 
-      expect(doc.getElementById('feature-json-inclusion').textContent).to.equal(
+      expect(doc.getElementById('feature-json-inclusion').textContent).toBe(
         'This is an abstract.'
       );
       doc.defaultView.close();
@@ -123,7 +122,7 @@ describe('Integration', () => {
     it('linked modules', async () => {
       const doc = await initDOM('linked-modules');
 
-      expect(doc.getElementById('feature-linked-modules').textContent).to.equal(
+      expect(doc.getElementById('feature-linked-modules').textContent).toBe(
         '2.0.0'
       );
       doc.defaultView.close();
@@ -131,7 +130,7 @@ describe('Integration', () => {
 
     it('svg inclusion', async () => {
       const doc = await initDOM('svg-inclusion');
-      expect(doc.getElementById('feature-svg-inclusion').src).to.match(
+      expect(doc.getElementById('feature-svg-inclusion').src).toMatch(
         /\/static\/media\/logo\..+\.svg$/
       );
       doc.defaultView.close();
@@ -140,9 +139,7 @@ describe('Integration', () => {
     it('svg component', async () => {
       const doc = await initDOM('svg-component');
 
-      expect(doc.getElementById('feature-svg-component').textContent).to.equal(
-        ''
-      );
+      expect(doc.getElementById('feature-svg-component').textContent).toBe('');
       doc.defaultView.close();
     });
 
@@ -155,7 +152,7 @@ describe('Integration', () => {
     it('unknown ext inclusion', async () => {
       const doc = await initDOM('unknown-ext-inclusion');
 
-      expect(doc.getElementById('feature-unknown-ext-inclusion').href).to.match(
+      expect(doc.getElementById('feature-unknown-ext-inclusion').href).toMatch(
         /\/static\/media\/aFileWithExt\.[a-f0-9]{8}\.unknown$/
       );
       doc.defaultView.close();
diff --git a/packages/react-scripts/fixtures/kitchensink/jest.integration.config.js b/packages/react-scripts/fixtures/kitchensink/jest.integration.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..fc77889249362baeb9f7814d3ba0a73e3d5d979f
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/jest.integration.config.js
@@ -0,0 +1,4 @@
+module.exports = {
+  testEnvironment: 'node',
+  testMatch: ['**/integration/*.test.js'],
+};
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/scss-styles.scss b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/scss-styles.scss
index 8aac77fbf6eed71fd176a93edb5195714f16c019..5a764ed2022ffa0ba8ec8fc00f03b86033eca0ff 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/scss-styles.scss
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/scss-styles.scss
@@ -1,4 +1,12 @@
-@import "~bootstrap/scss/bootstrap";
+@import "~bootstrap/scss/functions";
+@import "~bootstrap/scss/variables";
+@import "~bootstrap/scss/mixins";
+
+@import "~bootstrap/scss/reboot";
+@import "~bootstrap/scss/type";
+@import "~bootstrap/scss/images";
+@import "~bootstrap/scss/code";
+@import "~bootstrap/scss/grid";
 
 #feature-scss-inclusion {
   background: ghostwhite;
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index 490eb5f5666a53d12988a1fbca48f52622874c42..47f3b417d17ed4ef10af537b26ff298be103938f 100644
--- a/packages/react-scripts/template/README.md
+++ b/packages/react-scripts/template/README.md
@@ -1475,7 +1475,7 @@ import App from './App';
 it('renders welcome message', () => {
   const wrapper = shallow(<App />);
   const welcome = <h2>Welcome to React</h2>;
-  // expect(wrapper.contains(welcome)).to.equal(true);
+  // expect(wrapper.contains(welcome)).toBe(true);
   expect(wrapper.contains(welcome)).toEqual(true);
 });
 ```
diff --git a/tasks/e2e-kitchensink-eject.sh b/tasks/e2e-kitchensink-eject.sh
index ef7a511dc232c1d6c88595dc8e5733233102086f..41ea1ab0d1087bbbb6d8b36e73cdfda4e9253939 100755
--- a/tasks/e2e-kitchensink-eject.sh
+++ b/tasks/e2e-kitchensink-eject.sh
@@ -145,7 +145,7 @@ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
   CI=true \
   NODE_PATH=src \
   NODE_ENV=test \
-  yarn test --no-cache --testPathPattern=src
+  yarn test --no-cache --runInBand --testPathPattern=src
 
 # Test "development" environment
 tmp_server_log=`mktemp`
@@ -159,7 +159,7 @@ E2E_URL="http://localhost:3002" \
   CI=true NODE_PATH=src \
   NODE_ENV=development \
   BABEL_ENV=test \
-  node_modules/.bin/mocha --timeout 30000 --compilers js:@babel/register --require @babel/polyfill integration/*.test.js
+  node_modules/.bin/jest --no-cache --runInBand --config='jest.integration.config.js'
 
 # Test "production" environment
 E2E_FILE=./build/index.html \
@@ -168,7 +168,7 @@ E2E_FILE=./build/index.html \
   BABEL_ENV=test \
   NODE_PATH=src \
   PUBLIC_URL=http://www.example.org/spa/ \
-  node_modules/.bin/mocha --timeout 30000 --compilers js:@babel/register --require @babel/polyfill integration/*.test.js
+  node_modules/.bin/jest --no-cache --runInBand --config='jest.integration.config.js'
 
 # Cleanup
 cleanup
diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh
index c6011997cf823a8fc21fa2184497a1beb435b02f..a4cb3438f26fdad55a10d925a13dfc947c1102e1 100755
--- a/tasks/e2e-kitchensink.sh
+++ b/tasks/e2e-kitchensink.sh
@@ -131,11 +131,12 @@ exists build/*.html
 exists build/static/js/main.*.js
 
 # Unit tests
+# https://facebook.github.io/jest/docs/en/troubleshooting.html#tests-are-extremely-slow-on-docker-and-or-continuous-integration-ci-server
 REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
   CI=true \
   NODE_PATH=src \
   NODE_ENV=test \
-  yarn test --no-cache --testPathPattern=src
+  yarn test --no-cache --runInBand --testPathPattern=src
 
 # Prepare "development" environment
 tmp_server_log=`mktemp`
@@ -155,7 +156,7 @@ E2E_URL="http://localhost:3001" \
   CI=true NODE_PATH=src \
   NODE_ENV=development \
   BABEL_ENV=test \
-  node_modules/.bin/mocha --timeout 30000 --compilers js:@babel/register --require @babel/polyfill integration/*.test.js
+  node_modules/.bin/jest --no-cache --runInBand --config='jest.integration.config.js'
 # Test "production" environment
 E2E_FILE=./build/index.html \
   CI=true \
@@ -163,11 +164,7 @@ E2E_FILE=./build/index.html \
   NODE_ENV=production \
   BABEL_ENV=test \
   PUBLIC_URL=http://www.example.org/spa/ \
-  node_modules/.bin/mocha --timeout 30000 --compilers js:@babel/register --require @babel/polyfill integration/*.test.js
-
-# Remove the config we just created for Mocha
-# TODO: this is very hacky and we should find some other solution
-rm .babelrc
+  node_modules/.bin/jest --no-cache --runInBand --config='jest.integration.config.js'
 
 # Cleanup
 cleanup