diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f3986cd5fe..e4a1e92d436a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[@jest/console]` Print to stderr when calling `console.error`, `console.warn` or `console.assert` using the `jest-runtime` CLI ([#8261](https://github.com/facebook/jest/pull/8261)) + ### Chore & Maintenance ### Performance diff --git a/packages/jest-console/src/CustomConsole.ts b/packages/jest-console/src/CustomConsole.ts index 13bcd0e7ec23..6118e48cb715 100644 --- a/packages/jest-console/src/CustomConsole.ts +++ b/packages/jest-console/src/CustomConsole.ts @@ -12,8 +12,8 @@ import chalk from 'chalk'; import {LogCounters, LogMessage, LogTimers, LogType} from './types'; // TODO: Copied from `jest-util`. Import from it in Jest 25 -function clearLine(stream: NodeJS.WritableStream) { - if (process.stdout.isTTY) { +function clearLine(stream: NodeJS.WritableStream & {isTTY?: boolean}) { + if (stream.isTTY) { stream.write('\x1b[999D\x1b[K'); } } @@ -22,6 +22,7 @@ type Formatter = (type: LogType, message: LogMessage) => string; export default class CustomConsole extends Console { private _stdout: NodeJS.WritableStream; + private _stderr: NodeJS.WritableStream; private _formatBuffer: Formatter; private _counters: LogCounters; private _timers: LogTimers; @@ -34,19 +35,23 @@ export default class CustomConsole extends Console { ) { super(stdout, stderr); this._stdout = stdout; + this._stderr = stderr; this._formatBuffer = formatBuffer; this._counters = {}; this._timers = {}; this._groupDepth = 0; } - private _logToParentConsole(message: string) { - super.log(message); - } - private _log(type: LogType, message: string) { clearLine(this._stdout); - this._logToParentConsole( + super.log( + this._formatBuffer(type, ' '.repeat(this._groupDepth) + message), + ); + } + + private _logError(type: LogType, message: string) { + clearLine(this._stderr); + super.error( this._formatBuffer(type, ' '.repeat(this._groupDepth) + message), ); } @@ -55,7 +60,7 @@ export default class CustomConsole extends Console { try { assert(value, message); } catch (error) { - this._log('assert', error.toString()); + this._logError('assert', error.toString()); } } @@ -84,7 +89,7 @@ export default class CustomConsole extends Console { } error(firstArg: any, ...args: Array) { - this._log('error', format(firstArg, ...args)); + this._logError('error', format(firstArg, ...args)); } group(title?: string, ...args: Array) { @@ -137,7 +142,7 @@ export default class CustomConsole extends Console { } warn(firstArg: any, ...args: Array) { - this._log('warn', format(firstArg, ...args)); + this._logError('warn', format(firstArg, ...args)); } getBuffer() { diff --git a/packages/jest-console/src/__tests__/console.test.ts b/packages/jest-console/src/__tests__/CustomConsole.test.ts similarity index 70% rename from packages/jest-console/src/__tests__/console.test.ts rename to packages/jest-console/src/__tests__/CustomConsole.test.ts index 4b994fc6e888..56c6964eab7b 100644 --- a/packages/jest-console/src/__tests__/console.test.ts +++ b/packages/jest-console/src/__tests__/CustomConsole.test.ts @@ -5,47 +5,88 @@ * LICENSE file in the root directory of this source tree. */ +import {Writable} from 'stream'; import chalk from 'chalk'; import CustomConsole from '../CustomConsole'; describe('CustomConsole', () => { let _console; - let _stdout = ''; + let _stdout; + let _stderr; beforeEach(() => { - _console = new CustomConsole(process.stdout, process.stderr); - jest.spyOn(_console, '_logToParentConsole').mockImplementation(message => { - _stdout += message + '\n'; + _stdout = ''; + _stderr = ''; + + const stdout = new Writable({ + write(chunk, encoding, callback) { + _stdout += chunk.toString(); + callback(); + }, }); - _stdout = ''; + const stderr = new Writable({ + write(chunk, encoding, callback) { + _stderr += chunk.toString(); + callback(); + }, + }); + + _console = new CustomConsole(stdout, stderr); + }); + + describe('log', () => { + test('should print to stdout', () => { + _console.log('Hello world!'); + + expect(_stdout).toBe('Hello world!\n'); + }); + }); + + describe('error', () => { + test('should print to stderr', () => { + _console.error('Found some error!'); + + expect(_stderr).toBe('Found some error!\n'); + }); + }); + + describe('warn', () => { + test('should print to stderr', () => { + _console.warn('Found some warning!'); + + expect(_stderr).toBe('Found some warning!\n'); + }); }); describe('assert', () => { test('do not log when the assertion is truthy', () => { _console.assert(true); - expect(_stdout).toMatch(''); + expect(_stderr).toMatch(''); }); test('do not log when the assertion is truthy and there is a message', () => { _console.assert(true, 'ok'); - expect(_stdout).toMatch(''); + expect(_stderr).toMatch(''); }); test('log the assertion error when the assertion is falsy', () => { _console.assert(false); - expect(_stdout).toMatch('AssertionError'); - expect(_stdout).toMatch('false == true'); + expect(_stderr).toMatch('AssertionError'); + expect(_stderr).toMatch( + // The message may differ across Node versions + /(false == true)|(The expression evaluated to a falsy value:)/, + ); }); test('log the assertion error when the assertion is falsy with another message argument', () => { - _console.assert(false, 'ok'); + _console.assert(false, 'this should not happen'); - expect(_stdout).toMatch('AssertionError'); - expect(_stdout).toMatch('ok'); + expect(_stderr).toMatch('AssertionError'); + expect(_stderr).toMatch('this should not happen'); }); }); diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 49bcb0646b5c..dadef559f159 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -135,13 +135,9 @@ async function runTestInternal( let testConsole; if (globalConfig.silent) { - testConsole = new NullConsole(consoleOut, process.stderr, consoleFormatter); + testConsole = new NullConsole(consoleOut, consoleOut, consoleFormatter); } else if (globalConfig.verbose) { - testConsole = new CustomConsole( - consoleOut, - process.stderr, - consoleFormatter, - ); + testConsole = new CustomConsole(consoleOut, consoleOut, consoleFormatter); } else { testConsole = new BufferedConsole(() => runtime && runtime.getSourceMaps()); }