diff --git a/CHANGELOG.md b/CHANGELOG.md index 71bbb255f5d5..c788bc784ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `[jest-haste-map]` [**BREAKING**] Remove name from hash in `HasteMap.getCacheFilePath` ([#7218](https://github.com/facebook/jest/pull/7218)) - `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203)) - `[jest-haste-map]` [**BREAKING**] Expose relative paths when getting the file iterator ([#7321](https://github.com/facebook/jest/pull/7321)) +- `[jest-util]` Export `specialChars` containing Unicode characters and ANSI escapes for console output ([#7532](https://github.com/facebook/jest/pull/7532)) - `[jest-config]` Handle typescript (`ts` and `tsx`) by default ([#7533](https://github.com/facebook/jest/pull/7533)) - `[jest-validate]` Add support for comments in `package.json` using a `"//"` key ([#7295](https://github.com/facebook/jest/pull/7295)) - `[jest-config]` Add shorthand for watch plugins and runners ([#7213](https://github.com/facebook/jest/pull/7213)) @@ -48,6 +49,7 @@ - `[jest-haste-map]` [**BREAKING**] Recover files correctly after haste name collisions are fixed ([#7329](https://github.com/facebook/jest/pull/7329)) - `[pretty-format]` [**BREAKING**] Omit non-enumerable symbol properties ([#7448](https://github.com/facebook/jest/pull/7448)) - `[*]` [**BREAKING**] Upgrade to Babel 7, dropping support for Babel 6 ([#7016](https://github.com/facebook/jest/pull/7016)) +- `[jest-cli]` Avoid watch mode causing bad terminal behavior in some cases ([#7523](https://github.com/facebook/jest/pull/7523)) - `[jest-runner/jest-worker]` Fix missing console output in verbose mode ([#6871](https://github.com/facebook/jest/pull/6871)) - `[expect]` Standardize file naming in `expect` ([#7306](https://github.com/facebook/jest/pull/7306)) - `[jest-each]` Add empty array validation check ([#7249](https://github.com/facebook/jest/pull/7249)) diff --git a/packages/jest-cli/src/SnapshotInteractiveMode.js b/packages/jest-cli/src/SnapshotInteractiveMode.js index 2a9c40d67522..317451f81b06 100644 --- a/packages/jest-cli/src/SnapshotInteractiveMode.js +++ b/packages/jest-cli/src/SnapshotInteractiveMode.js @@ -15,7 +15,9 @@ import ansiEscapes from 'ansi-escapes'; import {KEYS} from 'jest-watcher'; import {pluralize} from './reporters/utils'; -import {ARROW} from './constants'; +import {specialChars} from 'jest-util'; + +const {ARROW, CLEAR} = specialChars; export default class SnapshotInteractiveMode { _pipe: stream$Writable | tty$WriteStream; @@ -90,7 +92,7 @@ export default class SnapshotInteractiveMode { } _drawUIDoneWithSkipped() { - this._pipe.write(ansiEscapes.clearScreen); + this._pipe.write(CLEAR); const numPass = this._countPaths - this._testAssertions.length; let stats = chalk.bold.dim( @@ -123,7 +125,7 @@ export default class SnapshotInteractiveMode { } _drawUIDone() { - this._pipe.write(ansiEscapes.clearScreen); + this._pipe.write(CLEAR); const numPass = this._countPaths - this._testAssertions.length; let stats = chalk.bold.dim( diff --git a/packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js b/packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js index 2363c4013b35..9e6a7fb8a6a0 100644 --- a/packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js +++ b/packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js @@ -9,15 +9,22 @@ import chalk from 'chalk'; import {KEYS} from 'jest-watcher'; import SnapshotInteractiveMode from '../SnapshotInteractiveMode'; -jest.mock('ansi-escapes', () => ({ - clearScreen: '[MOCK - eraseDown]', - cursorRestorePosition: '[MOCK - cursorRestorePosition]', - cursorSavePosition: '[MOCK - cursorSavePosition]', - cursorScrollDown: '[MOCK - cursorScrollDown]', - cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, - cursorUp: () => '[MOCK - cursorUp]', - eraseDown: '[MOCK - eraseDown]', -})); +jest + .mock('ansi-escapes', () => ({ + cursorRestorePosition: '[MOCK - cursorRestorePosition]', + cursorSavePosition: '[MOCK - cursorSavePosition]', + cursorScrollDown: '[MOCK - cursorScrollDown]', + cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, + cursorUp: () => '[MOCK - cursorUp]', + eraseDown: '[MOCK - eraseDown]', + })) + .mock('jest-util', () => { + const {specialChars, ...util} = jest.requireActual('jest-util'); + return { + ...util, + specialChars: {...specialChars, CLEAR: '[MOCK - clear]'}, + }; + }); jest.doMock('chalk', () => Object.assign(new chalk.constructor({enabled: false}), { diff --git a/packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap index e842ea64375f..7eb11da97da9 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap @@ -16,7 +16,7 @@ exports[`SnapshotInteractiveMode skip 1 test, then quit 1`] = ` `; exports[`SnapshotInteractiveMode skip 1 test, then quit 2`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 1 snapshot reviewed, 1 snapshot skipped @@ -43,7 +43,7 @@ exports[`SnapshotInteractiveMode skip 1 test, then restart 1`] = ` `; exports[`SnapshotInteractiveMode skip 1 test, then restart 2`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 1 snapshot reviewed, 1 snapshot skipped @@ -100,7 +100,7 @@ exports[`SnapshotInteractiveMode skip 1 test, update 1 test, then finish and res `; exports[`SnapshotInteractiveMode skip 1 test, update 1 test, then finish and restart 3`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 2 snapshots reviewed, 1 snapshot updated, 1 snapshot skipped @@ -157,7 +157,7 @@ exports[`SnapshotInteractiveMode skip 2 tests, then finish and restart 2`] = ` `; exports[`SnapshotInteractiveMode skip 2 tests, then finish and restart 3`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 2 snapshots reviewed, 2 snapshots skipped @@ -214,7 +214,7 @@ exports[`SnapshotInteractiveMode update 1 test, skip 1 test, then finish and res `; exports[`SnapshotInteractiveMode update 1 test, skip 1 test, then finish and restart 3`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 2 snapshots reviewed, 1 snapshot updated, 1 snapshot skipped @@ -256,7 +256,7 @@ exports[`SnapshotInteractiveMode update 1 test, then finish and return 1`] = ` `; exports[`SnapshotInteractiveMode update 1 test, then finish and return 2`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 1 snapshot reviewed, 1 snapshot updated @@ -297,7 +297,7 @@ exports[`SnapshotInteractiveMode update 2 tests, then finish and return 2`] = ` `; exports[`SnapshotInteractiveMode update 2 tests, then finish and return 3`] = ` -"[MOCK - eraseDown] +"[MOCK - clear] Interactive Snapshot Result › 2 snapshots reviewed, 2 snapshots updated diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap index f2b4187233e8..192bd10d4b91 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap @@ -92,7 +92,7 @@ Object { exports[`Watch mode flows Pressing "c" clears the filters 1`] = ` "[MOCK - cursorHide] -[MOCK - clearScreen] +[MOCK - clear] Pattern Mode Usage › Press Esc to exit pattern mode. diff --git a/packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js b/packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js index 1cdf8ed9e31a..279a4614dc7e 100644 --- a/packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js +++ b/packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js @@ -13,15 +13,22 @@ import {KEYS} from 'jest-watcher'; const runJestMock = jest.fn(); -jest.mock('ansi-escapes', () => ({ - clearScreen: '[MOCK - clearScreen]', - cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, - cursorHide: '[MOCK - cursorHide]', - cursorRestorePosition: '[MOCK - cursorRestorePosition]', - cursorSavePosition: '[MOCK - cursorSavePosition]', - cursorShow: '[MOCK - cursorShow]', - cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, -})); +jest + .mock('ansi-escapes', () => ({ + cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, + cursorHide: '[MOCK - cursorHide]', + cursorRestorePosition: '[MOCK - cursorRestorePosition]', + cursorSavePosition: '[MOCK - cursorSavePosition]', + cursorShow: '[MOCK - cursorShow]', + cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, + })) + .mock('jest-util', () => { + const {specialChars, ...util} = jest.requireActual('jest-util'); + return { + ...util, + specialChars: {...specialChars, CLEAR: '[MOCK - clear]'}, + }; + }); jest.mock( '../SearchSource', diff --git a/packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js b/packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js index a1d8913655da..6561654ae7e2 100644 --- a/packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js +++ b/packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js @@ -13,15 +13,22 @@ import {KEYS} from 'jest-watcher'; const runJestMock = jest.fn(); -jest.mock('ansi-escapes', () => ({ - clearScreen: '[MOCK - clearScreen]', - cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, - cursorHide: '[MOCK - cursorHide]', - cursorRestorePosition: '[MOCK - cursorRestorePosition]', - cursorSavePosition: '[MOCK - cursorSavePosition]', - cursorShow: '[MOCK - cursorShow]', - cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, -})); +jest + .mock('ansi-escapes', () => ({ + cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, + cursorHide: '[MOCK - cursorHide]', + cursorRestorePosition: '[MOCK - cursorRestorePosition]', + cursorSavePosition: '[MOCK - cursorSavePosition]', + cursorShow: '[MOCK - cursorShow]', + cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, + })) + .mock('jest-util', () => { + const {specialChars, ...util} = jest.requireActual('jest-util'); + return { + ...util, + specialChars: {...specialChars, CLEAR: '[MOCK - clear]'}, + }; + }); jest.mock( '../SearchSource', diff --git a/packages/jest-cli/src/constants.js b/packages/jest-cli/src/constants.js index fec5f008415e..d4a9460b84b9 100644 --- a/packages/jest-cli/src/constants.js +++ b/packages/jest-cli/src/constants.js @@ -7,15 +7,5 @@ * @flow */ -const isWindows = process.platform === 'win32'; - -export const CLEAR = isWindows ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'; -export const ARROW = ' \u203A '; -export const ICONS = { - failed: isWindows ? '\u00D7' : '\u2715', - pending: '\u25CB', - success: isWindows ? '\u221A' : '\u2713', - todo: '\u270E', -}; export const PACKAGE_JSON = 'package.json'; export const JEST_CONFIG = 'jest.config.js'; diff --git a/packages/jest-cli/src/reporters/verbose_reporter.js b/packages/jest-cli/src/reporters/verbose_reporter.js index ff82c3f573ea..7a80f46f3bc9 100644 --- a/packages/jest-cli/src/reporters/verbose_reporter.js +++ b/packages/jest-cli/src/reporters/verbose_reporter.js @@ -17,9 +17,11 @@ import type { import type {Test} from 'types/TestRunner'; import chalk from 'chalk'; -import {ICONS} from '../constants'; +import {specialChars} from 'jest-util'; import DefaultReporter from './default_reporter'; +const {ICONS} = specialChars; + export default class VerboseReporter extends DefaultReporter { _globalConfig: GlobalConfig; diff --git a/packages/jest-cli/src/watch.js b/packages/jest-cli/src/watch.js index ed558414f516..299025cb26e6 100644 --- a/packages/jest-cli/src/watch.js +++ b/packages/jest-cli/src/watch.js @@ -18,7 +18,7 @@ import getChangedFilesPromise from './getChangedFilesPromise'; import exit from 'exit'; import HasteMap from 'jest-haste-map'; import isValidPath from './lib/is_valid_path'; -import {isInteractive} from 'jest-util'; +import {isInteractive, specialChars} from 'jest-util'; import {print as preRunMessagePrint} from './preRunMessage'; import createContext from './lib/create_context'; import runJest from './runJest'; @@ -26,7 +26,6 @@ import updateGlobalConfig from './lib/update_global_config'; import SearchSource from './SearchSource'; import TestWatcher from './TestWatcher'; import FailedTestsCache from './FailedTestsCache'; -import {CLEAR} from './constants'; import {KEYS, JestHook} from 'jest-watcher'; import TestPathPatternPlugin from './plugins/test_path_pattern'; import TestNamePatternPlugin from './plugins/test_name_pattern'; @@ -236,7 +235,7 @@ export default function watch( } testWatcher = new TestWatcher({isWatchMode: true}); - isInteractive && outputStream.write(CLEAR); + isInteractive && outputStream.write(specialChars.CLEAR); preRunMessagePrint(outputStream); isRunning = true; const configs = contexts.map(context => context.config); @@ -395,7 +394,7 @@ export default function watch( const onCancelPatternPrompt = () => { outputStream.write(ansiEscapes.cursorHide); - outputStream.write(ansiEscapes.clearScreen); + outputStream.write(specialChars.CLEAR); outputStream.write(usage(globalConfig, watchPlugins)); outputStream.write(ansiEscapes.cursorShow); }; diff --git a/packages/jest-util/src/index.js b/packages/jest-util/src/index.js index 5cd88557a8ff..4ea6335a2d13 100644 --- a/packages/jest-util/src/index.js +++ b/packages/jest-util/src/index.js @@ -24,6 +24,7 @@ import getCallsite from './getCallsite'; import setGlobal from './setGlobal'; import deepCyclicCopy from './deepCyclicCopy'; import convertDescriptorToString from './convertDescriptorToString'; +import * as specialChars from './specialChars'; const createDirectory = (path: string) => { try { @@ -52,4 +53,5 @@ module.exports = { installCommonGlobals, isInteractive, setGlobal, + specialChars, }; diff --git a/packages/jest-util/src/specialChars.js b/packages/jest-util/src/specialChars.js new file mode 100644 index 000000000000..462843eef184 --- /dev/null +++ b/packages/jest-util/src/specialChars.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2018-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 + */ + +const isWindows = process.platform === 'win32'; + +export const ARROW = ' \u203A '; +export const ICONS = { + failed: isWindows ? '\u00D7' : '\u2715', + pending: '\u25CB', + success: isWindows ? '\u221A' : '\u2713', + todo: '\u270E', +}; + +export const CLEAR = isWindows ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'; diff --git a/packages/jest-watcher/package.json b/packages/jest-watcher/package.json index a2da8dd11379..3734874f02bc 100644 --- a/packages/jest-watcher/package.json +++ b/packages/jest-watcher/package.json @@ -6,6 +6,7 @@ "dependencies": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", + "jest-util": "^23.4.0", "string-length": "^2.0.0" }, "repository": { diff --git a/packages/jest-watcher/src/PatternPrompt.js b/packages/jest-watcher/src/PatternPrompt.js index a25f602ca6ad..f50f608ef7c3 100644 --- a/packages/jest-watcher/src/PatternPrompt.js +++ b/packages/jest-watcher/src/PatternPrompt.js @@ -13,8 +13,11 @@ import type {ScrollOptions} from 'types/Watch'; import chalk from 'chalk'; import ansiEscapes from 'ansi-escapes'; +import {specialChars} from 'jest-util'; import Prompt from './lib/Prompt'; +const {CLEAR} = specialChars; + const usage = (entity: string) => `\n${chalk.bold('Pattern Mode Usage')}\n` + ` ${chalk.dim('\u203A Press')} Esc ${chalk.dim('to exit pattern mode.')}\n` + @@ -38,7 +41,7 @@ export default class PatternPrompt { run(onSuccess: Function, onCancel: Function, options?: {header: string}) { this._pipe.write(ansiEscapes.cursorHide); - this._pipe.write(ansiEscapes.clearScreen); + this._pipe.write(CLEAR); if (options && options.header) { this._pipe.write(options.header + '\n');