From 02de10563c2d08c3bcb20e17a243a7b5fe11b4ea Mon Sep 17 00:00:00 2001 From: Conrad Buck Date: Thu, 4 Feb 2021 15:29:58 -0500 Subject: [PATCH] Ensure verbose mode prints output synchronously Fixes #8208 --- .../consoleDebugging.test.ts.snap | 16 +++++++++++++ e2e/__tests__/consoleDebugging.test.ts | 23 ++++++++++++++++++ .../__tests__/console-debugging.test.js | 23 ++++++++++++++++++ e2e/console-debugging/package.json | 9 +++++++ e2e/console-debugging/stdout-spy.js | 10 ++++++++ .../jest-reporters/src/DefaultReporter.ts | 24 ++++++++++--------- .../jest-reporters/src/VerboseReporter.ts | 15 ++++++++++++ 7 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/consoleDebugging.test.ts.snap create mode 100644 e2e/__tests__/consoleDebugging.test.ts create mode 100644 e2e/console-debugging/__tests__/console-debugging.test.js create mode 100644 e2e/console-debugging/package.json create mode 100644 e2e/console-debugging/stdout-spy.js diff --git a/e2e/__tests__/__snapshots__/consoleDebugging.test.ts.snap b/e2e/__tests__/__snapshots__/consoleDebugging.test.ts.snap new file mode 100644 index 000000000000..f5bfd36dfb92 --- /dev/null +++ b/e2e/__tests__/__snapshots__/consoleDebugging.test.ts.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`console debugging with --verbose 1`] = ``; + +exports[`console debugging with --verbose 2`] = ` +PASS __tests__/console-debugging.test.js + ✓ verbose mode prints console output synchronously +`; + +exports[`console debugging with --verbose 3`] = ` +Test Suites: 1 passed, 1 total +Tests: 1 passed, 1 total +Snapshots: 1 passed, 1 total +Time: <> +Ran all test suites. +`; diff --git a/e2e/__tests__/consoleDebugging.test.ts b/e2e/__tests__/consoleDebugging.test.ts new file mode 100644 index 000000000000..4aca32566208 --- /dev/null +++ b/e2e/__tests__/consoleDebugging.test.ts @@ -0,0 +1,23 @@ +/** + * 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 {extractSummary} from '../Utils'; +import runJest from '../runJest'; + +test('console debugging with --verbose', () => { + const {stderr, stdout, exitCode} = runJest('console-debugging', [ + '--noStackTrace', + '--no-cache', + ]); + const {summary, rest} = extractSummary(stderr); + + expect(exitCode).toBe(0); + expect(wrap(stdout)).toMatchSnapshot(); + expect(wrap(rest)).toMatchSnapshot(); + expect(wrap(summary)).toMatchSnapshot(); +}); diff --git a/e2e/console-debugging/__tests__/console-debugging.test.js b/e2e/console-debugging/__tests__/console-debugging.test.js new file mode 100644 index 000000000000..3647e1620c73 --- /dev/null +++ b/e2e/console-debugging/__tests__/console-debugging.test.js @@ -0,0 +1,23 @@ +/** + * 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'; + +const stdoutWrite = require('../stdout-spy'); + +process.stdout.write = jest.fn(process.stdout.write); + +test('verbose mode prints console output synchronously', () => { + console.log('test'); + expect(stdoutWrite.text).toMatchInlineSnapshot(` +" console.log + test + + at Object.log (__tests__/console-debugging.test.js:14:11) + +" +`); +}); diff --git a/e2e/console-debugging/package.json b/e2e/console-debugging/package.json new file mode 100644 index 000000000000..77cd799d4bb3 --- /dev/null +++ b/e2e/console-debugging/package.json @@ -0,0 +1,9 @@ +{ + "jest": { + "testEnvironment": "node", + "verbose": true, + "setupFiles": [ + "./stdout-spy.js" + ] + } +} diff --git a/e2e/console-debugging/stdout-spy.js b/e2e/console-debugging/stdout-spy.js new file mode 100644 index 000000000000..be853459ffa5 --- /dev/null +++ b/e2e/console-debugging/stdout-spy.js @@ -0,0 +1,10 @@ +const originalStdoutWrite = process.stdout.write; + +const stdoutWrite = (...args) => { + stdoutWrite.text = args[0]; + originalStdoutWrite(...args); +}; + +process.stdout.write = stdoutWrite; + +module.exports = stdoutWrite; diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index 77b1fbe94b46..d9aeee5b210a 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -43,16 +43,18 @@ export default class DefaultReporter extends BaseReporter { this._err = process.stderr.write.bind(process.stderr); this._status = new Status(); this._bufferedOutput = new Set(); - this._wrapStdio(process.stdout); - this._wrapStdio(process.stderr); + this.__wrapStdio(process.stdout); + this.__wrapStdio(process.stderr); this._status.onChange(() => { - this._clearStatus(); - this._printStatus(); + this.__clearStatus(); + this.__printStatus(); }); } - private _wrapStdio(stream: NodeJS.WritableStream | NodeJS.WriteStream) { - const originalWrite = stream.write; + protected __wrapStdio( + stream: NodeJS.WritableStream | NodeJS.WriteStream, + ): void { + const write = stream.write.bind(stream); let buffer: Array = []; let timeout: NodeJS.Timeout | null = null; @@ -62,11 +64,11 @@ export default class DefaultReporter extends BaseReporter { buffer = []; // This is to avoid conflicts between random output and status text - this._clearStatus(); + this.__clearStatus(); if (string) { - originalWrite.call(stream, string); + write(string); } - this._printStatus(); + this.__printStatus(); this._bufferedOutput.delete(flushBufferedOutput); }; @@ -103,7 +105,7 @@ export default class DefaultReporter extends BaseReporter { } } - private _clearStatus() { + protected __clearStatus(): void { if (isInteractive) { if (this._globalConfig.useStderr) { this._err(this._clear); @@ -113,7 +115,7 @@ export default class DefaultReporter extends BaseReporter { } } - private _printStatus() { + protected __printStatus(): void { const {content, clear} = this._status.get(); this._clear = clear; if (isInteractive) { diff --git a/packages/jest-reporters/src/VerboseReporter.ts b/packages/jest-reporters/src/VerboseReporter.ts index 5d7de322f330..dfe5901ef7bd 100644 --- a/packages/jest-reporters/src/VerboseReporter.ts +++ b/packages/jest-reporters/src/VerboseReporter.ts @@ -29,6 +29,21 @@ export default class VerboseReporter extends DefaultReporter { this._globalConfig = globalConfig; } + // Verbose mode is for debugging. Buffering of output is undesirable. + // See https://github.com/facebook/jest/issues/8208 + protected __wrapStdio( + stream: NodeJS.WritableStream | NodeJS.WriteStream, + ): void { + const write = stream.write.bind(stream); + + stream.write = (chunk: string) => { + this.__clearStatus(); + write(chunk); + this.__printStatus(); + return true; + }; + } + static filterTestResults( testResults: Array, ): Array {