From d0ed84507b9bc36e9751ddc6f10e3402c049146f Mon Sep 17 00:00:00 2001 From: Jack Zhao <jzhao@fb.com> Date: Fri, 21 Sep 2018 02:17:45 -0700 Subject: [PATCH] Convert test suite to Jest (#4550) * convert mocha tests to jest * jest 23 * add jest configs * use material css * fix windows * forceExit jest test * force exit eject * test * test * retrigger test * remove appveyor comment * try to remove pretendToBeVisual option * use jsdom env * test environment * no cache * test no close * bring back raf * test revert all broken changes * add back jsdom * remove jsdom * node test environment * use latest change * runInBand * runInBand * comment test run * try different jest option * standardize jest test options * increase heap size * remove heap size config * support scoped packages for cra --scripts-version option * upgrade jest version * fix windows * fix windows again * jest 23.4.1 * babel-jest * babel-jest * split out kitchhensink * travis node 6 * travis node 6 config * node 6 travis eject * cache yarn * only cache yarn * remove unrelated changes * typo --- .../kitchensink/.template.dependencies.json | 9 +--- .../fixtures/kitchensink/README.md | 1 - .../kitchensink/integration/env.test.js | 31 ++++++------- .../kitchensink/integration/initDOM.js | 5 +-- .../kitchensink/integration/syntax.test.js | 45 +++++++++---------- .../kitchensink/integration/webpack.test.js | 23 +++++----- .../kitchensink/jest.integration.config.js | 4 ++ .../features/webpack/assets/scss-styles.scss | 10 ++++- packages/react-scripts/template/README.md | 2 +- tasks/e2e-kitchensink-eject.sh | 6 +-- tasks/e2e-kitchensink.sh | 11 ++--- 11 files changed, 70 insertions(+), 77 deletions(-) create mode 100644 packages/react-scripts/fixtures/kitchensink/jest.integration.config.js diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json index 0a42b7d4c..e45cf8218 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 4e7725ce1..0e64a76ae 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 b1f5f5d55..79de16706 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 0ed5a9408..e807888d3 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 9e34b669d..54920726f 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 4aca73603..ebd830dfa 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 000000000..fc7788924 --- /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 8aac77fbf..5a764ed20 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 490eb5f56..47f3b417d 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 ef7a511dc..41ea1ab0d 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 c6011997c..a4cb3438f 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 -- GitLab