From 5b75b69689dc95d7346c2226532c6390c9f53fdd Mon Sep 17 00:00:00 2001 From: Fumihiro Xue Date: Wed, 18 Oct 2017 03:11:14 +0800 Subject: [PATCH] Add Global Setup/Teardown APIs --- CHANGELOG.md | 2 + docs/Configuration.md | 10 +++++ .../__snapshots__/show_config.test.js.snap | 2 + .../__tests__/global_setup.test.js | 30 +++++++++++++++ .../__tests__/global_teardown.test.js | 37 +++++++++++++++++++ .../global_setup/__tests__/setup1.test.js | 20 ++++++++++ .../global_setup/__tests__/setup2.test.js | 20 ++++++++++ .../global_setup/__tests__/setup3.test.js | 20 ++++++++++ integration_tests/global_setup/package.json | 5 +++ integration_tests/global_setup/setup.js | 22 +++++++++++ .../__tests__/teardown1.test.js | 18 +++++++++ .../__tests__/teardown2.test.js | 18 +++++++++ .../__tests__/teardown3.test.js | 18 +++++++++ .../global_teardown/package.json | 5 +++ integration_tests/global_teardown/teardown.js | 22 +++++++++++ packages/jest-cli/src/cli/args.js | 8 ++++ packages/jest-cli/src/run_jest.js | 8 ++++ packages/jest-config/src/defaults.js | 2 + packages/jest-config/src/index.js | 2 + packages/jest-config/src/normalize.js | 2 + test_utils.js | 2 + types/Argv.js | 2 + types/Config.js | 6 +++ 23 files changed, 281 insertions(+) create mode 100644 integration_tests/__tests__/global_setup.test.js create mode 100644 integration_tests/__tests__/global_teardown.test.js create mode 100644 integration_tests/global_setup/__tests__/setup1.test.js create mode 100644 integration_tests/global_setup/__tests__/setup2.test.js create mode 100644 integration_tests/global_setup/__tests__/setup3.test.js create mode 100644 integration_tests/global_setup/package.json create mode 100644 integration_tests/global_setup/setup.js create mode 100644 integration_tests/global_teardown/__tests__/teardown1.test.js create mode 100644 integration_tests/global_teardown/__tests__/teardown2.test.js create mode 100644 integration_tests/global_teardown/__tests__/teardown3.test.js create mode 100644 integration_tests/global_teardown/package.json create mode 100644 integration_tests/global_teardown/teardown.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 674bc7498e93..a4065c478427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,8 @@ ### Features +* `[jest-config]` Add Global Setup/Teardown options + ([#4716](https://github.com/facebook/jest/pull/4716)) * `[jest-config]` Add `testEnvironmentOptions` to apply to jsdom options or node context. ([#5003](https://github.com/facebook/jest/pull/5003)) * `[jest-jasmine2]` Update Timeout error message to `jest.timeout` and display diff --git a/docs/Configuration.md b/docs/Configuration.md index d6ac586199eb..ed2bd92478b7 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -261,6 +261,16 @@ Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will _not_ be persisted across test runs for other test files. +### `globalSetup` [string] +Default: `undefined` + +This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. + +### `globalTeardown` [string] +Default: `undefined` + +This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. + ### `mapCoverage` [boolean] ##### available in Jest **20.0.0+** diff --git a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap index bc52f635f121..ad5f154fb672 100644 --- a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap @@ -70,6 +70,8 @@ exports[`--showConfig outputs config info and exits 1`] = ` ], \\"detectLeaks\\": false, \\"expand\\": false, + \\"globalSetup\\": null, + \\"globalTeardown\\": null, \\"listTests\\": false, \\"mapCoverage\\": false, \\"maxWorkers\\": \\"[maxWorkers]\\", diff --git a/integration_tests/__tests__/global_setup.test.js b/integration_tests/__tests__/global_setup.test.js new file mode 100644 index 000000000000..df85c3d4e40c --- /dev/null +++ b/integration_tests/__tests__/global_setup.test.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @flow + */ +'use strict'; + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const runJest = require('../runJest'); +const {cleanup} = require('../utils'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +test('globalSetup is triggered once before all test suites', () => { + const path = require('path'); + const setupPath = path.resolve(__dirname, '../global_setup/setup.js'); + const result = runJest.json('global_setup', [`--globalSetup=${setupPath}`]); + expect(result.status).toBe(0); + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(1); + const setup = fs.readFileSync(path.join(DIR, '/', files[0]), 'utf8'); + expect(setup).toBe('setup'); +}); diff --git a/integration_tests/__tests__/global_teardown.test.js b/integration_tests/__tests__/global_teardown.test.js new file mode 100644 index 000000000000..ab2c8ee17665 --- /dev/null +++ b/integration_tests/__tests__/global_teardown.test.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @flow + */ +'use strict'; + +const fs = require('fs'); +const mkdirp = require('mkdirp'); +const os = require('os'); +const path = require('path'); +const runJest = require('../runJest'); +const {cleanup} = require('../utils'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +test('globalTeardown is triggered once after all test suites', () => { + mkdirp.sync(DIR); + const path = require('path'); + const teardownPath = path.resolve( + __dirname, + '../global_teardown/teardown.js', + ); + const result = runJest.json('global_teardown', [ + `--globalTeardown=${teardownPath}`, + ]); + expect(result.status).toBe(0); + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(1); + const teardown = fs.readFileSync(path.join(DIR, '/', files[0]), 'utf8'); + expect(teardown).toBe('teardown'); +}); diff --git a/integration_tests/global_setup/__tests__/setup1.test.js b/integration_tests/global_setup/__tests__/setup1.test.js new file mode 100644 index 000000000000..347b5b09c193 --- /dev/null +++ b/integration_tests/global_setup/__tests__/setup1.test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should exist setup file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(1); + const setup = fs.readFileSync(path.join(DIR, '/', files[0]), 'utf8'); + expect(setup).toBe('setup'); +}); diff --git a/integration_tests/global_setup/__tests__/setup2.test.js b/integration_tests/global_setup/__tests__/setup2.test.js new file mode 100644 index 000000000000..347b5b09c193 --- /dev/null +++ b/integration_tests/global_setup/__tests__/setup2.test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should exist setup file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(1); + const setup = fs.readFileSync(path.join(DIR, '/', files[0]), 'utf8'); + expect(setup).toBe('setup'); +}); diff --git a/integration_tests/global_setup/__tests__/setup3.test.js b/integration_tests/global_setup/__tests__/setup3.test.js new file mode 100644 index 000000000000..347b5b09c193 --- /dev/null +++ b/integration_tests/global_setup/__tests__/setup3.test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should exist setup file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(1); + const setup = fs.readFileSync(path.join(DIR, '/', files[0]), 'utf8'); + expect(setup).toBe('setup'); +}); diff --git a/integration_tests/global_setup/package.json b/integration_tests/global_setup/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/integration_tests/global_setup/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/integration_tests/global_setup/setup.js b/integration_tests/global_setup/setup.js new file mode 100644 index 000000000000..4041e3b59d1d --- /dev/null +++ b/integration_tests/global_setup/setup.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +const crypto = require('crypto'); +const fs = require('fs'); +const mkdirp = require('mkdirp'); +const os = require('os'); +const path = require('path'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +module.exports = function() { + return new Promise((resolve, reject) => { + mkdirp.sync(DIR); + const fileId = crypto.randomBytes(20).toString('hex'); + fs.writeFileSync(path.join(DIR, '/', fileId), 'setup'); + resolve(); + }); +}; diff --git a/integration_tests/global_teardown/__tests__/teardown1.test.js b/integration_tests/global_teardown/__tests__/teardown1.test.js new file mode 100644 index 000000000000..4ff9f33bc707 --- /dev/null +++ b/integration_tests/global_teardown/__tests__/teardown1.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should not exist teardown file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(0); +}); diff --git a/integration_tests/global_teardown/__tests__/teardown2.test.js b/integration_tests/global_teardown/__tests__/teardown2.test.js new file mode 100644 index 000000000000..4ff9f33bc707 --- /dev/null +++ b/integration_tests/global_teardown/__tests__/teardown2.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should not exist teardown file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(0); +}); diff --git a/integration_tests/global_teardown/__tests__/teardown3.test.js b/integration_tests/global_teardown/__tests__/teardown3.test.js new file mode 100644 index 000000000000..4ff9f33bc707 --- /dev/null +++ b/integration_tests/global_teardown/__tests__/teardown3.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +test('should not exist teardown file', () => { + const files = fs.readdirSync(DIR); + expect(files).toHaveLength(0); +}); diff --git a/integration_tests/global_teardown/package.json b/integration_tests/global_teardown/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/integration_tests/global_teardown/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/integration_tests/global_teardown/teardown.js b/integration_tests/global_teardown/teardown.js new file mode 100644 index 000000000000..d2c5e4487e60 --- /dev/null +++ b/integration_tests/global_teardown/teardown.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +const crypto = require('crypto'); +const fs = require('fs'); +const mkdirp = require('mkdirp'); +const os = require('os'); +const path = require('path'); + +const DIR = path.join(os.tmpdir(), '/jest'); + +module.exports = function() { + return new Promise((resolve, reject) => { + mkdirp.sync(DIR); + const fileId = crypto.randomBytes(20).toString('hex'); + fs.writeFileSync(path.join(DIR, '/', fileId), 'teardown'); + resolve(); + }); +}; diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index 4c5bbb06cd28..ec367a9d9a8b 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -239,6 +239,14 @@ export const options = { 'adequately cleaned up.', type: 'boolean', }, + globalSetup: { + description: 'The path to a module that runs before All Tests.', + type: 'string', + }, + globalTeardown: { + description: 'The path to a module that runs after All Tests.', + type: 'string', + }, globals: { description: 'A JSON string with map of global variables that need ' + diff --git a/packages/jest-cli/src/run_jest.js b/packages/jest-cli/src/run_jest.js index 0776c82cf845..dc97d640e3c3 100644 --- a/packages/jest-cli/src/run_jest.js +++ b/packages/jest-cli/src/run_jest.js @@ -91,6 +91,10 @@ export default (async function runJest({ changedFilesPromise: ?ChangedFilesPromise, onComplete: (testResults: AggregatedResult) => any, }) { + if (globalConfig.globalSetup) { + // $FlowFixMe + await require(globalConfig.globalSetup)(); + } const sequencer = new TestSequencer(); let allTests = []; @@ -172,6 +176,10 @@ export default (async function runJest({ sequencer.cacheResults(allTests, results); + if (globalConfig.globalTeardown) { + // $FlowFixMe + await require(globalConfig.globalTeardown)(); + } return processResults(results, { isJSON: globalConfig.json, onComplete, diff --git a/packages/jest-config/src/defaults.js b/packages/jest-config/src/defaults.js index 404a75b90445..ce68eab6a649 100644 --- a/packages/jest-config/src/defaults.js +++ b/packages/jest-config/src/defaults.js @@ -38,6 +38,8 @@ export default ({ coverageReporters: ['json', 'text', 'lcov', 'clover'], detectLeaks: false, expand: false, + globalSetup: null, + globalTeardown: null, globals: {}, haste: { providesModuleNodeModules: [], diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index e14dd07b560d..221a558d0a37 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -85,6 +85,8 @@ const getConfigs = ( expand: options.expand, findRelatedTests: options.findRelatedTests, forceExit: options.forceExit, + globalSetup: options.globalSetup, + globalTeardown: options.globalTeardown, json: options.json, lastCommit: options.lastCommit, listTests: options.listTests, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 27e6f8909aac..9da76fe2daab 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -455,6 +455,8 @@ export default function normalize(options: InitialOptions, argv: Argv) { case 'detectLeaks': case 'displayName': case 'expand': + case 'globalSetup': + case 'globalTeardown': case 'globals': case 'findRelatedTests': case 'forceExit': diff --git a/test_utils.js b/test_utils.js index 49c4e5136de0..401d346eabe9 100644 --- a/test_utils.js +++ b/test_utils.js @@ -24,6 +24,8 @@ const DEFAULT_GLOBAL_CONFIG: GlobalConfig = { expand: false, findRelatedTests: false, forceExit: false, + globalSetup: null, + globalTeardown: null, json: false, lastCommit: false, listTests: false, diff --git a/types/Argv.js b/types/Argv.js index 186b8db2ebde..0f1b7943405a 100644 --- a/types/Argv.js +++ b/types/Argv.js @@ -33,6 +33,8 @@ export type Argv = {| expand: boolean, findRelatedTests: boolean, forceExit: boolean, + globalSetup: ?string, + globalTeardown: ?string, globals: string, h: boolean, haste: string, diff --git a/types/Config.js b/types/Config.js index 067987a76ebb..7898ec19a4e5 100644 --- a/types/Config.js +++ b/types/Config.js @@ -32,6 +32,8 @@ export type DefaultOptions = {| coverageReporters: Array, expand: boolean, globals: ConfigGlobals, + globalSetup: ?string, + globalTeardown: ?string, haste: HasteConfig, detectLeaks: boolean, mapCoverage: boolean, @@ -87,6 +89,8 @@ export type InitialOptions = { forceExit?: boolean, json?: boolean, globals?: ConfigGlobals, + globalSetup?: ?string, + globalTeardown?: ?string, haste?: HasteConfig, reporters?: Array, logHeapUsage?: boolean, @@ -163,6 +167,8 @@ export type GlobalConfig = {| findRelatedTests: boolean, forceExit: boolean, json: boolean, + globalSetup: ?string, + globalTeardown: ?string, lastCommit: boolean, logHeapUsage: boolean, listTests: boolean,