From 0633a61c2b7767ec915e40865ca329c10023c046 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 29 Jan 2019 17:11:29 +0100 Subject: [PATCH] fix: print log entries if logging happens after teardown (#7731) --- .flowconfig | 1 + CHANGELOG.md | 1 + .../consoleAfterTeardown.test.js.snap | 20 ++++++++++++ e2e/__tests__/consoleAfterTeardown.test.js | 20 ++++++++++++ e2e/__tests__/forceExit.test.js | 15 ++++++--- .../__tests__/console.test.js | 15 +++++++++ e2e/console-after-teardown/package.json | 6 ++++ packages/jest-runner/package.json | 1 + packages/jest-runner/src/runTest.js | 32 +++++++++++++++++++ 9 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap create mode 100644 e2e/__tests__/consoleAfterTeardown.test.js create mode 100644 e2e/console-after-teardown/__tests__/console.test.js create mode 100644 e2e/console-after-teardown/package.json diff --git a/.flowconfig b/.flowconfig index 21c130eb2c80..e7de44ffb18f 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,6 +1,7 @@ [ignore] .*/examples/.* .*/node_modules/metro-bundler/.* +.*/node_modules/metro-config/.* .*/node_modules/metro/.* .*/node_modules/module-deps/.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4d3ef543ec..f27a8f2ebba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - `[jest-config]` Extract setupFilesAfterEnv from preset ([#7724](https://github.com/facebook/jest/pull/7724)) - `[jest-cli]` Do not execute any `globalSetup` or `globalTeardown` if there are no tests to execute ([#7745](https://github.com/facebook/jest/pull/7745)) - `[jest-runtime]` Lock down version of `write-file-atomic` ([#7725](https://github.com/facebook/jest/pull/7725)) +- `[jest-cli]` Print log entries when logging happens after test environment is torn down ([#7731](https://github.com/facebook/jest/pull/7731)) ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap b/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap new file mode 100644 index 000000000000..635de99d73cd --- /dev/null +++ b/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`console printing 1`] = ` +PASS __tests__/console.test.js + + + ● Cannot log after tests are done. Did you forget to wait for something async in your test? + Attempted to log "hello!". + + 11 | // eslint-disable-next-line prefer-arrow-callback + 12 | new Promise(resolve => setTimeout(resolve, 500)).then(function log() { + > 13 | console.log('hello!'); + | ^ + 14 | }); + 15 | }); + 16 | + + at BufferedConsole.log (../../packages/jest-util/build/BufferedConsole.js:169:10) + at log (__tests__/console.test.js:13:13) +`; diff --git a/e2e/__tests__/consoleAfterTeardown.test.js b/e2e/__tests__/consoleAfterTeardown.test.js new file mode 100644 index 000000000000..0c12d77720b4 --- /dev/null +++ b/e2e/__tests__/consoleAfterTeardown.test.js @@ -0,0 +1,20 @@ +/** + * 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. + * + * @flow + */ + +import {extractSummary} from '../Utils'; +import runJest from '../runJest'; +import {wrap} from 'jest-snapshot-serializer-raw'; + +test('console printing', () => { + const {stderr, status} = runJest('console-after-teardown'); + const {rest} = extractSummary(stderr); + + expect(status).toBe(0); + expect(wrap(rest)).toMatchSnapshot(); +}); diff --git a/e2e/__tests__/forceExit.test.js b/e2e/__tests__/forceExit.test.js index 34cb1f03682d..de0e03ebac1d 100644 --- a/e2e/__tests__/forceExit.test.js +++ b/e2e/__tests__/forceExit.test.js @@ -28,11 +28,15 @@ test('exits the process after test are done but before timers complete', () => { 'package.json': JSON.stringify({jest: {testEnvironment: 'node'}}), }); + let output; let stdout; let stderr; ({stdout, stderr} = runJest(DIR)); - expect(stderr).toMatch(/PASS.*test\.test\.js/); - expect(stdout).toMatch(/TIMER_DONE/); + + output = `${stdout}\n${stderr}`; + + expect(output).toMatch(/PASS.*test\.test\.js/); + expect(output).toMatch(/TIMER_DONE/); writeFiles(DIR, { 'package.json': JSON.stringify({ jest: {forceExit: true, testEnvironment: 'node'}, @@ -40,6 +44,9 @@ test('exits the process after test are done but before timers complete', () => { }); ({stdout, stderr} = runJest(DIR)); - expect(stderr).toMatch(/PASS.*test\.test\.js/); - expect(stdout).not.toMatch(/TIMER_DONE/); + + output = `${stdout}\n${stderr}`; + + expect(output).toMatch(/PASS.*test\.test\.js/); + expect(output).not.toMatch(/TIMER_DONE/); }); diff --git a/e2e/console-after-teardown/__tests__/console.test.js b/e2e/console-after-teardown/__tests__/console.test.js new file mode 100644 index 000000000000..6e565ec4ee2d --- /dev/null +++ b/e2e/console-after-teardown/__tests__/console.test.js @@ -0,0 +1,15 @@ +/** + * 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. + */ +'use strict'; + +test('throws error', () => { + // To have the function be named the same in jasmine and circus + // eslint-disable-next-line prefer-arrow-callback + new Promise(resolve => setTimeout(resolve, 500)).then(function log() { + console.log('hello!'); + }); +}); diff --git a/e2e/console-after-teardown/package.json b/e2e/console-after-teardown/package.json new file mode 100644 index 000000000000..33eac18834b4 --- /dev/null +++ b/e2e/console-after-teardown/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "verbose": false + } +} diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index edf15b0711ec..3323e7aeae7f 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -9,6 +9,7 @@ "license": "MIT", "main": "build/index.js", "dependencies": { + "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", "jest-config": "^24.0.0", diff --git a/packages/jest-runner/src/runTest.js b/packages/jest-runner/src/runTest.js index 14d34483955a..2ff916f45abd 100644 --- a/packages/jest-runner/src/runTest.js +++ b/packages/jest-runner/src/runTest.js @@ -28,12 +28,42 @@ import {getTestEnvironment} from 'jest-config'; import * as docblock from 'jest-docblock'; import {formatExecError} from 'jest-message-util'; import sourcemapSupport from 'source-map-support'; +import chalk from 'chalk'; type RunTestInternalResult = { leakDetector: ?LeakDetector, result: TestResult, }; +function freezeConsole( + testConsole: BufferedConsole | Console | NullConsole, + config: ProjectConfig, +) { + // $FlowFixMe: overwrite it for pretty errors + testConsole._log = function fakeConsolePush(_type, message) { + const error = new ErrorWithStack( + `${chalk.red( + `${chalk.bold( + 'Cannot log after tests are done.', + )} Did you forget to wait for something async in your test?`, + )}\nAttempted to log "${message}".`, + fakeConsolePush, + ); + + const formattedError = formatExecError( + error, + config, + {noStackTrace: false}, + undefined, + true, + ); + + process.stderr.write('\n' + formattedError + '\n'); + // TODO: set exit code in Jest 25 + // process.exitCode = 1; + }; +} + // Keeping the core of "runTest" as a separate function (as "runTestInternal") // is key to be able to detect memory leaks. Since all variables are local to // the function, when "runTestInternal" finishes its execution, they can all be @@ -197,6 +227,8 @@ async function runTestInternal( throw err; } + freezeConsole(testConsole, config); + const testCount = result.numPassingTests + result.numFailingTests +