From f099621054afecbe41d3d8932721b05a06602132 Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Wed, 28 Mar 2018 01:25:31 -0700 Subject: [PATCH] Allow plugin keys to be overriden by other plugins (#5878) --- .../__snapshots__/watch.test.js.snap | 14 +++++ packages/jest-cli/src/__tests__/watch.test.js | 41 +++++++++++++++ .../jest-cli/src/lib/watch_plugins_helpers.js | 51 +++++++++++++++++++ packages/jest-cli/src/plugins/quit.js | 10 ++++ .../jest-cli/src/plugins/test_name_pattern.js | 2 + .../jest-cli/src/plugins/test_path_pattern.js | 2 + .../jest-cli/src/plugins/update_snapshots.js | 10 ++++ .../plugins/update_snapshots_interactive.js | 3 ++ packages/jest-cli/src/types.js | 1 + packages/jest-cli/src/watch.js | 27 +++------- 10 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 packages/jest-cli/src/lib/watch_plugins_helpers.js diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap index d1619091990f..38f4a2d573ad 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap @@ -21,6 +21,20 @@ Watch Usage ] `; +exports[`Watch mode flows allows WatchPlugins to override internal plugins 1`] = ` +Array [ + " +Watch Usage + › Press a to run all tests. + › Press f to run only failed tests. + › Press t to filter by a test name regex pattern. + › Press q to quit watch mode. + › Press p to custom \\"P\\" plugin. + › Press Enter to trigger a test run. +", +] +`; + exports[`Watch mode flows shows prompts for WatchPlugins in alphabetical order 1`] = ` Array [ Array [ diff --git a/packages/jest-cli/src/__tests__/watch.test.js b/packages/jest-cli/src/__tests__/watch.test.js index 51ff4c97774a..21f993934d01 100644 --- a/packages/jest-cli/src/__tests__/watch.test.js +++ b/packages/jest-cli/src/__tests__/watch.test.js @@ -317,6 +317,47 @@ describe('Watch mode flows', () => { expect(apply).toHaveBeenCalled(); }); + it('allows WatchPlugins to override internal plugins', async () => { + const run = jest.fn(() => Promise.resolve()); + const pluginPath = `${__dirname}/__fixtures__/plugin_path_override`; + jest.doMock( + pluginPath, + () => + class WatchPlugin { + constructor() { + this.run = run; + } + getUsageInfo() { + return { + key: 'p'.codePointAt(0), + prompt: 'custom "P" plugin', + }; + } + }, + {virtual: true}, + ); + + watch( + Object.assign({}, globalConfig, { + rootDir: __dirname, + watchPlugins: [pluginPath], + }), + contexts, + pipe, + hasteMapInstances, + stdin, + ); + + await nextTick(); + + expect(pipe.write.mock.calls.reverse()[0]).toMatchSnapshot(); + + stdin.emit(toHex('p')); + await nextTick(); + + expect(run).toHaveBeenCalled(); + }); + it('allows WatchPlugins to hook into file system changes', async () => { const fileChange = jest.fn(); const pluginPath = `${__dirname}/__fixtures__/plugin_path_fs_change`; diff --git a/packages/jest-cli/src/lib/watch_plugins_helpers.js b/packages/jest-cli/src/lib/watch_plugins_helpers.js new file mode 100644 index 000000000000..08a7bc21f4b8 --- /dev/null +++ b/packages/jest-cli/src/lib/watch_plugins_helpers.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2014-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 + */ +import type {GlobalConfig} from 'types/Config'; +import type {WatchPlugin, UsageData} from '../types'; + +export const filterInteractivePlugins = ( + watchPlugins: Array, + globalConfig: GlobalConfig, +): Array => { + const usageInfos = watchPlugins.map( + p => p.getUsageInfo && p.getUsageInfo(globalConfig), + ); + + return watchPlugins.filter((plugin, i, array) => { + if (usageInfos[i]) { + const {key} = usageInfos[i]; + return !usageInfos.slice(i + 1).some(u => u && key === u.key); + } + + return false; + }); +}; + +export const getSortedUsageRows = ( + watchPlugins: Array, + globalConfig: GlobalConfig, +): Array => { + return filterInteractivePlugins(watchPlugins, globalConfig) + .sort((a: WatchPlugin, b: WatchPlugin) => { + if (a.isInternal) { + return -1; + } + + const usageInfoA = a.getUsageInfo && a.getUsageInfo(globalConfig); + const usageInfoB = b.getUsageInfo && b.getUsageInfo(globalConfig); + + if (usageInfoA && usageInfoB) { + return usageInfoA.key - usageInfoB.key; + } + + return 0; + }) + .map(p => p.getUsageInfo && p.getUsageInfo(globalConfig)) + .filter(Boolean); +}; diff --git a/packages/jest-cli/src/plugins/quit.js b/packages/jest-cli/src/plugins/quit.js index ae4e33499977..e9bcc781e7e5 100644 --- a/packages/jest-cli/src/plugins/quit.js +++ b/packages/jest-cli/src/plugins/quit.js @@ -9,6 +9,16 @@ import BaseWatchPlugin from '../base_watch_plugin'; class QuitPlugin extends BaseWatchPlugin { + isInternal: true; + + constructor(options: { + stdin: stream$Readable | tty$ReadStream, + stdout: stream$Writable | tty$WriteStream, + }) { + super(options); + this.isInternal = true; + } + async run() { if (typeof this._stdin.setRawMode === 'function') { this._stdin.setRawMode(false); diff --git a/packages/jest-cli/src/plugins/test_name_pattern.js b/packages/jest-cli/src/plugins/test_name_pattern.js index 1d28c3fee001..a293c5235d6c 100644 --- a/packages/jest-cli/src/plugins/test_name_pattern.js +++ b/packages/jest-cli/src/plugins/test_name_pattern.js @@ -14,6 +14,7 @@ import Prompt from '../lib/Prompt'; class TestNamePatternPlugin extends BaseWatchPlugin { _prompt: Prompt; + isInternal: true; constructor(options: { stdin: stream$Readable | tty$ReadStream, @@ -21,6 +22,7 @@ class TestNamePatternPlugin extends BaseWatchPlugin { }) { super(options); this._prompt = new Prompt(); + this.isInternal = true; } getUsageInfo() { diff --git a/packages/jest-cli/src/plugins/test_path_pattern.js b/packages/jest-cli/src/plugins/test_path_pattern.js index 5512938341e9..2dbb48dc1e31 100644 --- a/packages/jest-cli/src/plugins/test_path_pattern.js +++ b/packages/jest-cli/src/plugins/test_path_pattern.js @@ -15,6 +15,7 @@ import Prompt from '../lib/Prompt'; class TestPathPatternPlugin extends BaseWatchPlugin { _prompt: Prompt; + isInternal: true; constructor(options: { stdin: stream$Readable | tty$ReadStream, @@ -22,6 +23,7 @@ class TestPathPatternPlugin extends BaseWatchPlugin { }) { super(options); this._prompt = new Prompt(); + this.isInternal = true; } getUsageInfo() { diff --git a/packages/jest-cli/src/plugins/update_snapshots.js b/packages/jest-cli/src/plugins/update_snapshots.js index c63ff6a77e9a..00ff3b045a27 100644 --- a/packages/jest-cli/src/plugins/update_snapshots.js +++ b/packages/jest-cli/src/plugins/update_snapshots.js @@ -12,6 +12,16 @@ import type {JestHookSubscriber} from '../jest_hooks'; class UpdateSnapshotsPlugin extends BaseWatchPlugin { _hasSnapshotFailure: boolean; + isInternal: true; + + constructor(options: { + stdin: stream$Readable | tty$ReadStream, + stdout: stream$Writable | tty$WriteStream, + }) { + super(options); + this.isInternal = true; + } + run( globalConfig: GlobalConfig, updateConfigAndRun: Function, diff --git a/packages/jest-cli/src/plugins/update_snapshots_interactive.js b/packages/jest-cli/src/plugins/update_snapshots_interactive.js index 97f995d9852d..59f8d2312987 100644 --- a/packages/jest-cli/src/plugins/update_snapshots_interactive.js +++ b/packages/jest-cli/src/plugins/update_snapshots_interactive.js @@ -14,7 +14,9 @@ import SnapshotInteractiveMode from '../snapshot_interactive_mode'; class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { _snapshotInteractiveMode: SnapshotInteractiveMode; + _failedSnapshotTestPaths: Array<*>; _failedSnapshotTestAssertions: Array; + isInternal: true; constructor(options: { stdin: stream$Readable | tty$ReadStream, @@ -23,6 +25,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { super(options); this._failedSnapshotTestAssertions = []; this._snapshotInteractiveMode = new SnapshotInteractiveMode(this._stdout); + this.isInternal = true; } getFailedSnapshotTestAssertions( diff --git a/packages/jest-cli/src/types.js b/packages/jest-cli/src/types.js index f6a90eb63324..d145c68135ff 100644 --- a/packages/jest-cli/src/types.js +++ b/packages/jest-cli/src/types.js @@ -19,6 +19,7 @@ export type JestHooks = { }; export interface WatchPlugin { + +isInternal?: boolean; +apply?: (hooks: JestHookSubscriber) => void; +getUsageInfo?: (globalConfig: GlobalConfig) => ?UsageData; +onKey?: (value: string) => void; diff --git a/packages/jest-cli/src/watch.js b/packages/jest-cli/src/watch.js index d89b226179cf..502789cb1343 100644 --- a/packages/jest-cli/src/watch.js +++ b/packages/jest-cli/src/watch.js @@ -33,6 +33,10 @@ import TestNamePatternPlugin from './plugins/test_name_pattern'; import UpdateSnapshotsPlugin from './plugins/update_snapshots'; import UpdateSnapshotsInteractivePlugin from './plugins/update_snapshots_interactive'; import QuitPlugin from './plugins/quit'; +import { + getSortedUsageRows, + filterInteractivePlugins, +} from './lib/watch_plugins_helpers'; import activeFilters from './lib/active_filters_message'; let hasExitListener = false; @@ -45,24 +49,6 @@ const INTERNAL_PLUGINS = [ QuitPlugin, ]; -const getSortedUsageRows = ( - watchPlugins: Array, - globalConfig: GlobalConfig, -) => { - const internalPlugins = watchPlugins - .slice(0, INTERNAL_PLUGINS.length) - .map(p => p.getUsageInfo && p.getUsageInfo(globalConfig)) - .filter(Boolean); - - const thirdPartyPlugins = watchPlugins - .slice(INTERNAL_PLUGINS.length) - .map(p => p.getUsageInfo && p.getUsageInfo(globalConfig)) - .filter(Boolean) - .sort((a, b) => a.key - b.key); - - return internalPlugins.concat(thirdPartyPlugins); -}; - export default function watch( initialGlobalConfig: GlobalConfig, contexts: Array, @@ -285,7 +271,10 @@ export default function watch( return; } - const matchingWatchPlugin = watchPlugins.find(plugin => { + const matchingWatchPlugin = filterInteractivePlugins( + watchPlugins, + globalConfig, + ).find(plugin => { const usageData = (plugin.getUsageInfo && plugin.getUsageInfo(globalConfig)) || {}; return usageData.key === parseInt(key, 16);