diff --git a/CHANGELOG.md b/CHANGELOG.md index 61244390d64b..52de836d914e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * `[jest-util]` Fix `runOnlyPendingTimers` for `setTimeout` inside `setImmediate` ([#4608](https://github.com/facebook/jest/pull/4608)) ### Features +* `[jest-environment-*]` [**BREAKING**] Add Async Test Environment APIs, dispose is now teardown ([#4506](https://github.com/facebook/jest/pull/4506)) * `[jest-cli]` Add an option to clear the cache ([#4430](https://github.com/facebook/jest/pull/4430)) * `[babel-plugin-jest-hoist]` Improve error message, that the second argument of `jest.mock` must be an inline function ([#4593](https://github.com/facebook/jest/pull/4593)) * `[jest-snapshot]` [**BREAKING**] Concatenate name of test and snapshot ([#4460](https://github.com/facebook/jest/pull/4460)) diff --git a/integration_tests/__tests__/test_environment_async.test.js b/integration_tests/__tests__/test_environment_async.test.js new file mode 100644 index 000000000000..7e3ddd921787 --- /dev/null +++ b/integration_tests/__tests__/test_environment_async.test.js @@ -0,0 +1,26 @@ +/** + * 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 runJest = require('../runJest'); +const {cleanup} = require('../utils'); + +const DIR = os.tmpdir() + '/jest'; + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +it('triggers setup/teardown hooks', () => { + const result = runJest('test-environment-async'); + expect(result.status).toBe(0); + const teardown = fs.readFileSync(DIR + '/teardown', 'utf8'); + expect(teardown).toBe('teardown'); +}); diff --git a/integration_tests/test-environment-async/__tests__/custom.test.js b/integration_tests/test-environment-async/__tests__/custom.test.js new file mode 100644 index 000000000000..2632c0350617 --- /dev/null +++ b/integration_tests/test-environment-async/__tests__/custom.test.js @@ -0,0 +1,13 @@ +/** + * 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'; +/* eslint-env browser*/ + +test('setup', () => { + expect(global.setup).toBe('setup'); +}); diff --git a/integration_tests/test-environment-async/package.json b/integration_tests/test-environment-async/package.json new file mode 100644 index 000000000000..a346d75a230a --- /dev/null +++ b/integration_tests/test-environment-async/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "./test-environment.js" + } +} diff --git a/integration_tests/test-environment-async/test-environment.js b/integration_tests/test-environment-async/test-environment.js new file mode 100644 index 000000000000..a650d8436da7 --- /dev/null +++ b/integration_tests/test-environment-async/test-environment.js @@ -0,0 +1,33 @@ +'use strict'; + +const fs = require('fs'); +const os = require('os'); +const mkdirp = require('mkdirp'); +const JSDOMEnvironment = require('jest-environment-jsdom'); + +const DIR = os.tmpdir() + '/jest'; + +class TestEnvironment extends JSDOMEnvironment { + constructor(config) { + super(config); + } + + setup() { + return super.setup().then(() => { + this.global.setup = 'setup'; + }); + } + + teardown() { + return super.teardown().then(() => { + mkdirp.sync(DIR); + fs.writeFileSync(DIR + '/teardown', 'teardown'); + }); + } + + runScript(script) { + return super.runScript(script); + } +} + +module.exports = TestEnvironment; diff --git a/package.json b/package.json index f377d947bc7e..0924215648d2 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "react-test-renderer": "15.4.2", "regenerator-runtime": "^0.11.0", "resolve": "^1.4.0", - "rimraf": "^2.5.4", + "rimraf": "^2.6.2", "rollup": "^0.50.0", "rollup-plugin-babel": "^3.0.2", "rollup-plugin-commonjs": "^8.2.1", diff --git a/packages/jest-environment-jsdom/src/index.js b/packages/jest-environment-jsdom/src/index.js index 05ae31ec7a2b..23a2a832d406 100644 --- a/packages/jest-environment-jsdom/src/index.js +++ b/packages/jest-environment-jsdom/src/index.js @@ -58,7 +58,11 @@ class JSDOMEnvironment { }); } - dispose(): void { + setup(): Promise { + return Promise.resolve(); + } + + teardown(): Promise { if (this.fakeTimers) { this.fakeTimers.dispose(); } @@ -68,6 +72,7 @@ class JSDOMEnvironment { this.global = null; this.document = null; this.fakeTimers = null; + return Promise.resolve(); } runScript(script: Script): ?any { diff --git a/packages/jest-environment-node/src/index.js b/packages/jest-environment-node/src/index.js index fe29d8044b0a..f74637905e48 100644 --- a/packages/jest-environment-node/src/index.js +++ b/packages/jest-environment-node/src/index.js @@ -65,12 +65,17 @@ class NodeEnvironment { }); } - dispose() { + setup(): Promise { + return Promise.resolve(); + } + + teardown(): Promise { if (this.fakeTimers) { this.fakeTimers.dispose(); } this.context = null; this.fakeTimers = null; + return Promise.resolve(); } // Disabling rule as return type depends on script's return type. diff --git a/packages/jest-runner/src/run_test.js b/packages/jest-runner/src/run_test.js index eb0a50fcd7fe..7f5a714b68f8 100644 --- a/packages/jest-runner/src/run_test.js +++ b/packages/jest-runner/src/run_test.js @@ -30,7 +30,7 @@ import * as docblock from 'jest-docblock'; // and required implicitly through the `testRunner` ProjectConfig option. jasmine2; -export default function runTest( +export default (async function runTest( path: Path, globalConfig: GlobalConfig, config: ProjectConfig, @@ -95,39 +95,33 @@ export default function runTest( mapCoverage: globalConfig.mapCoverage, }); const start = Date.now(); - return testFramework(globalConfig, config, environment, runtime, path) - .then((result: TestResult) => { - const testCount = - result.numPassingTests + - result.numFailingTests + - result.numPendingTests; - result.perfStats = {end: Date.now(), start}; - result.testFilePath = path; - result.coverage = runtime.getAllCoverageInfo(); - result.sourceMaps = runtime.getSourceMapInfo(); - result.console = testConsole.getBuffer(); - result.skipped = testCount === result.numPendingTests; - result.displayName = config.displayName; - return result; - }) - .then( - result => - Promise.resolve().then(() => { - environment.dispose(); - if (globalConfig.logHeapUsage) { - if (global.gc) { - global.gc(); - } - result.memoryUsage = process.memoryUsage().heapUsed; - } - - // Delay the resolution to allow log messages to be output. - return new Promise(resolve => setImmediate(() => resolve(result))); - }), - err => - Promise.resolve().then(() => { - environment.dispose(); - throw err; - }), + await environment.setup(); + try { + const result: TestResult = await testFramework( + globalConfig, + config, + environment, + runtime, + path, ); -} + const testCount = + result.numPassingTests + result.numFailingTests + result.numPendingTests; + result.perfStats = {end: Date.now(), start}; + result.testFilePath = path; + result.coverage = runtime.getAllCoverageInfo(); + result.sourceMaps = runtime.getSourceMapInfo(); + result.console = testConsole.getBuffer(); + result.skipped = testCount === result.numPendingTests; + result.displayName = config.displayName; + if (globalConfig.logHeapUsage) { + if (global.gc) { + global.gc(); + } + result.memoryUsage = process.memoryUsage().heapUsed; + } + // Delay the resolution to allow log messages to be output. + return new Promise(resolve => setImmediate(() => resolve(result))); + } finally { + await environment.teardown(); + } +}); diff --git a/types/Environment.js b/types/Environment.js index 19507e993638..ccba7eeafe22 100644 --- a/types/Environment.js +++ b/types/Environment.js @@ -14,7 +14,6 @@ import type {ModuleMocker} from 'jest-mock'; declare class $JestEnvironment { constructor(config: ProjectConfig): void, - dispose(): void, runScript(script: Script): any, global: Global, fakeTimers: { @@ -30,6 +29,8 @@ declare class $JestEnvironment { }, testFilePath: string, moduleMocker: ModuleMocker, + setup(): Promise, + teardown(): Promise, } export type Environment = $JestEnvironment; diff --git a/yarn.lock b/yarn.lock index d9c7d86c9d12..0e6ed826f959 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1868,7 +1868,7 @@ debug@2.6.8, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8: dependencies: ms "2.0.0" -debug@^3.0.1, debug@3.1.0: +debug@3.1.0, debug@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -5103,9 +5103,9 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5"