diff --git a/CHANGELOG.md b/CHANGELOG.md index d84b3bce67c9..2b14d26488af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-message-util]` Check for common errors when using the wrong test environment ([#8245](https://github.com/facebook/jest/pull/8245)) + ### Fixes - `[jest-circus]` Fix type ellision of jest-runtime imports ([#9717](https://github.com/facebook/jest/pull/9717)) diff --git a/e2e/__tests__/__snapshots__/wrongEnv.test.ts.snap b/e2e/__tests__/__snapshots__/wrongEnv.test.ts.snap new file mode 100644 index 000000000000..d551b668ba8c --- /dev/null +++ b/e2e/__tests__/__snapshots__/wrongEnv.test.ts.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Wrong globals for environment print useful error for document 1`] = ` +FAIL __tests__/node.js + ✕ use document + ○ skipped use window + ○ skipped use navigator + + ● use document + + The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. + Consider using the "jsdom" test environment. + + ReferenceError: document is not defined + + 14 | + 15 | test('use document', () => { + > 16 | const div = document.createElement('div'); + | ^ + 17 | + 18 | console.log(div); + 19 | + + at Object.document (__tests__/node.js:16:15) +`; + +exports[`Wrong globals for environment print useful error for navigator 1`] = ` +FAIL __tests__/node.js + ✕ use navigator + ○ skipped use document + ○ skipped use window + + ● use navigator + + The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. + Consider using the "jsdom" test environment. + + ReferenceError: navigator is not defined + + 30 | + 31 | test('use navigator', () => { + > 32 | const userAgent = navigator.userAgent; + | ^ + 33 | + 34 | console.log(userAgent); + 35 | + + at Object.navigator (__tests__/node.js:32:21) +`; + +exports[`Wrong globals for environment print useful error for unref 1`] = ` +FAIL __tests__/jsdom.js + ✕ use unref + + ● use unref + + The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. + Consider using the "node" test environment. + + TypeError: setTimeout(...).unref is not a function + + 11 | + 12 | test('use unref', () => { + > 13 | setTimeout(() => {}, 0).unref(); + | ^ + 14 | + 15 | expect(1).toBe(1); + 16 | }); + + at Object.unref (__tests__/jsdom.js:13:27) +`; + +exports[`Wrong globals for environment print useful error for window 1`] = ` +FAIL __tests__/node.js + ✕ use window + ○ skipped use document + ○ skipped use navigator + + ● use window + + The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. + Consider using the "jsdom" test environment. + + ReferenceError: window is not defined + + 22 | + 23 | test('use window', () => { + > 24 | const location = window.location; + | ^ + 25 | + 26 | console.log(location); + 27 | + + at Object.window (__tests__/node.js:24:20) +`; + +exports[`Wrong globals for environment print useful error when it explodes during evaluation 1`] = ` +FAIL __tests__/beforeTest.js + ● Test suite failed to run + + The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. + Consider using the "jsdom" test environment. + + ReferenceError: document is not defined + + 11 | /* eslint-env browser */ + 12 | + > 13 | const div = document.createElement('div'); + | ^ + 14 | + 15 | console.log(div); + 16 | + + at Object.document (__tests__/beforeTest.js:13:13) +`; diff --git a/e2e/__tests__/wrongEnv.test.ts b/e2e/__tests__/wrongEnv.test.ts new file mode 100644 index 000000000000..2cb5f255ed92 --- /dev/null +++ b/e2e/__tests__/wrongEnv.test.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import wrap from 'jest-snapshot-serializer-raw'; +import runJest from '../runJest'; +import {extractSummary} from '../Utils'; + +function assertFailuresAndSnapshot(args: Array) { + const result = runJest('wrong-env', args); + expect(result.exitCode).toBe(1); + expect(wrap(extractSummary(result.stderr).rest)).toMatchSnapshot(); +} + +describe('Wrong globals for environment', () => { + it('print useful error for window', () => { + assertFailuresAndSnapshot(['node', '-t=window']); + }); + + it('print useful error for document', () => { + assertFailuresAndSnapshot(['node', '-t=document']); + }); + + it('print useful error for navigator', () => { + assertFailuresAndSnapshot(['node', '-t=navigator']); + }); + + it('print useful error for unref', () => { + assertFailuresAndSnapshot(['jsdom', '-t=unref']); + }); + + it('print useful error when it explodes during evaluation', () => { + assertFailuresAndSnapshot(['beforeTest']); + }); +}); diff --git a/e2e/wrong-env/__tests__/beforeTest.js b/e2e/wrong-env/__tests__/beforeTest.js new file mode 100644 index 000000000000..95afb6a35872 --- /dev/null +++ b/e2e/wrong-env/__tests__/beforeTest.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + * @jest-environment node + * + */ + +/* eslint-env browser */ + +const div = document.createElement('div'); + +console.log(div); + +test('stub', () => expect(1).toBe(1)); diff --git a/e2e/wrong-env/__tests__/jsdom.js b/e2e/wrong-env/__tests__/jsdom.js new file mode 100644 index 000000000000..f030fbb2b60c --- /dev/null +++ b/e2e/wrong-env/__tests__/jsdom.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + * @jest-environment jsdom + */ + +'use strict'; + +test('use unref', () => { + setTimeout(() => {}, 0).unref(); + + expect(1).toBe(1); +}); diff --git a/e2e/wrong-env/__tests__/node.js b/e2e/wrong-env/__tests__/node.js new file mode 100644 index 000000000000..ec43dca816f5 --- /dev/null +++ b/e2e/wrong-env/__tests__/node.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + * @jest-environment node + * + */ + +/* eslint-env browser */ + +'use strict'; + +test('use document', () => { + const div = document.createElement('div'); + + console.log(div); + + expect(1).toBe(1); +}); + +test('use window', () => { + const location = window.location; + + console.log(location); + + expect(1).toBe(1); +}); + +test('use navigator', () => { + const userAgent = navigator.userAgent; + + console.log(userAgent); + + expect(1).toBe(1); +}); diff --git a/e2e/wrong-env/package.json b/e2e/wrong-env/package.json new file mode 100644 index 000000000000..586d4ca6b75c --- /dev/null +++ b/e2e/wrong-env/package.json @@ -0,0 +1,3 @@ +{ + "jest": {} +} diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 0b2248daab44..d46f75ffd0ac 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -92,6 +92,30 @@ const getRenderedCallsite = ( const blankStringRegexp = /^\s*$/; +function checkForCommonEnvironmentErrors(error: string) { + if ( + error.includes('ReferenceError: document is not defined') || + error.includes('ReferenceError: window is not defined') || + error.includes('ReferenceError: navigator is not defined') + ) { + return warnAboutWrongTestEnvironment(error, 'jsdom'); + } else if (error.includes('.unref is not a function')) { + return warnAboutWrongTestEnvironment(error, 'node'); + } + + return error; +} + +function warnAboutWrongTestEnvironment(error: string, env: 'jsdom' | 'node') { + return ( + chalk.bold.red( + `The error below may be caused by using the wrong test environment, see ${chalk.dim.underline( + 'https://jestjs.io/docs/en/configuration#testenvironment-string', + )}.\nConsider using the "${env}" test environment.\n\n`, + ) + error + ); +} + // ExecError is an error thrown outside of the test suite (not inside an `it` or // `before/after each` hooks). If it's thrown, none of the tests in the file // are executed. @@ -126,6 +150,8 @@ export const formatExecError = ( message = separated.message; } + message = checkForCommonEnvironmentErrors(message); + message = indentAllLines(message, MESSAGE_INDENT); stack = @@ -285,19 +311,21 @@ export const formatStackTrace = ( return `${renderedCallsite}\n${stacktrace}`; }; +type FailedResults = Array<{ + content: string; + result: AssertionResult; +}>; + export const formatResultsErrors = ( testResults: Array, config: StackTraceConfig, options: StackTraceOptions, testPath?: Path, ): string | null => { - type FailedResults = Array<{ - content: string; - result: AssertionResult; - }>; - const failedResults: FailedResults = testResults.reduce((errors, result) => { - result.failureMessages.forEach(content => errors.push({content, result})); + result.failureMessages + .map(checkForCommonEnvironmentErrors) + .forEach(content => errors.push({content, result})); return errors; }, [] as FailedResults);