diff --git a/CHANGELOG.md b/CHANGELOG.md index ca627f1d0cbe..67253e1f4e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,9 @@ ### Fixes +* `[jest-runtime]` Throw a more useful error when trying to require modules + after the test environment is torn down + ([#5888](https://github.com/facebook/jest/pull/5888)) * `[jest-mock]` [**BREAKING**] Replace timestamps with `invocationCallOrder` ([#5867](https://github.com/facebook/jest/pull/5867)) * `[jest-jasmine2]` Install `sourcemap-support` into normal runtime to catch @@ -71,8 +74,6 @@ mode. ([#5861](https://github.com/facebook/jest/pull/5861)) * `[jest-mock]` Extend .toHaveBeenCalled return message with outcome ([#5951](https://github.com/facebook/jest/pull/5951)) -* `[jest-message-util]` Include column in stack frames - ([#5889](https://github.com/facebook/jest/pull/5889)) * `[jest-runner]` Assign `process.env.JEST_WORKER_ID="1"` when in runInBand mode ([#5860](https://github.com/facebook/jest/pull/5860)) * `[jest-cli]` Add descriptive error message when trying to use diff --git a/integration-tests/__tests__/__snapshots__/require_after_teardown.test.js.snap b/integration-tests/__tests__/__snapshots__/require_after_teardown.test.js.snap new file mode 100644 index 000000000000..e200d37782aa --- /dev/null +++ b/integration-tests/__tests__/__snapshots__/require_after_teardown.test.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`prints useful error for requires after test is done 1`] = ` +"ReferenceError: You are trying to \`import\` a file after the Jest environment has been torn down. + + 9 | test('require after done', () => { + 10 | setTimeout(() => { + > 11 | const double = require('../'); + 12 | + 13 | expect(double(5)).toBe(10); + 14 | }, 0);" +`; diff --git a/integration-tests/__tests__/require_after_teardown.test.js b/integration-tests/__tests__/require_after_teardown.test.js new file mode 100644 index 000000000000..1a13b9ac5d38 --- /dev/null +++ b/integration-tests/__tests__/require_after_teardown.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 runJest = require('../runJest'); + +test('prints useful error for requires after test is done', () => { + const {stderr} = runJest('require-after-teardown'); + + const interestingLines = stderr + .split('\n') + .slice(9, 17) + .join('\n'); + + expect(interestingLines).toMatchSnapshot(); + expect(stderr.split('\n')[18]).toMatch( + new RegExp('(__tests__/late-require.test.js:11:20)'), + ); +}); diff --git a/integration-tests/require-after-teardown/__tests__/late-require.test.js b/integration-tests/require-after-teardown/__tests__/late-require.test.js new file mode 100644 index 000000000000..116a9ddde81e --- /dev/null +++ b/integration-tests/require-after-teardown/__tests__/late-require.test.js @@ -0,0 +1,15 @@ +/** + * 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'; + +test('require after done', () => { + setTimeout(() => { + const double = require('../'); + + expect(double(5)).toBe(10); + }, 0); +}); diff --git a/integration-tests/require-after-teardown/index.js b/integration-tests/require-after-teardown/index.js new file mode 100644 index 000000000000..39261f1e45e9 --- /dev/null +++ b/integration-tests/require-after-teardown/index.js @@ -0,0 +1,10 @@ +/** + * 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. + */ + +module.exports = function double(input) { + return input * 2; +}; diff --git a/integration-tests/require-after-teardown/package.json b/integration-tests/require-after-teardown/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/integration-tests/require-after-teardown/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index c4cb00c48713..0469662a3163 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -16,6 +16,7 @@ "graceful-fs": "^4.1.11", "jest-config": "^22.4.2", "jest-haste-map": "^22.4.2", + "jest-message-util": "^22.4.0", "jest-regex-util": "^22.1.0", "jest-resolve": "^22.4.2", "jest-snapshot": "^22.4.0", diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index d179cdfbb81b..b21208815648 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -19,6 +19,7 @@ import type {SourceMapRegistry} from 'types/SourceMaps'; import path from 'path'; import HasteMap from 'jest-haste-map'; +import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import Resolver from 'jest-resolve'; import {createDirectory, deepCyclicCopy} from 'jest-util'; import {escapePathForRegex} from 'jest-regex-util'; @@ -551,9 +552,28 @@ class Runtime { } } - const wrapper = this._environment.runScript(transformedFile.script)[ - ScriptTransformer.EVAL_RESULT_VARIABLE - ]; + const runScript = this._environment.runScript(transformedFile.script); + + if (runScript === null) { + const originalStack = new ReferenceError( + 'You are trying to `import` a file after the Jest environment has been torn down.', + ).stack + .split('\n') + // Remove this file from the stack (jest-message-utils will keep one line) + .filter(line => line.indexOf(__filename) === -1) + .join('\n'); + + const {message, stack} = separateMessageFromStack(originalStack); + + console.error( + `\n${message}\n` + + formatStackTrace(stack, this._config, {noStackTrace: false}), + ); + process.exitCode = 1; + return; + } + + const wrapper = runScript[ScriptTransformer.EVAL_RESULT_VARIABLE]; wrapper.call( localModule.exports, // module context localModule, // module object