Skip to content

Commit f099621

Browse files
rogeliogcpojer
authored andcommitted
Allow plugin keys to be overriden by other plugins (#5878)
1 parent a9d1c5f commit f099621

10 files changed

Lines changed: 142 additions & 19 deletions

File tree

packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ Watch Usage
2121
]
2222
`;
2323

24+
exports[`Watch mode flows allows WatchPlugins to override internal plugins 1`] = `
25+
Array [
26+
"
27+
Watch Usage
28+
› Press a to run all tests.
29+
› Press f to run only failed tests.
30+
› Press t to filter by a test name regex pattern.
31+
› Press q to quit watch mode.
32+
› Press p to custom \\"P\\" plugin.
33+
› Press Enter to trigger a test run.
34+
",
35+
]
36+
`;
37+
2438
exports[`Watch mode flows shows prompts for WatchPlugins in alphabetical order 1`] = `
2539
Array [
2640
Array [

packages/jest-cli/src/__tests__/watch.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,47 @@ describe('Watch mode flows', () => {
317317
expect(apply).toHaveBeenCalled();
318318
});
319319

320+
it('allows WatchPlugins to override internal plugins', async () => {
321+
const run = jest.fn(() => Promise.resolve());
322+
const pluginPath = `${__dirname}/__fixtures__/plugin_path_override`;
323+
jest.doMock(
324+
pluginPath,
325+
() =>
326+
class WatchPlugin {
327+
constructor() {
328+
this.run = run;
329+
}
330+
getUsageInfo() {
331+
return {
332+
key: 'p'.codePointAt(0),
333+
prompt: 'custom "P" plugin',
334+
};
335+
}
336+
},
337+
{virtual: true},
338+
);
339+
340+
watch(
341+
Object.assign({}, globalConfig, {
342+
rootDir: __dirname,
343+
watchPlugins: [pluginPath],
344+
}),
345+
contexts,
346+
pipe,
347+
hasteMapInstances,
348+
stdin,
349+
);
350+
351+
await nextTick();
352+
353+
expect(pipe.write.mock.calls.reverse()[0]).toMatchSnapshot();
354+
355+
stdin.emit(toHex('p'));
356+
await nextTick();
357+
358+
expect(run).toHaveBeenCalled();
359+
});
360+
320361
it('allows WatchPlugins to hook into file system changes', async () => {
321362
const fileChange = jest.fn();
322363
const pluginPath = `${__dirname}/__fixtures__/plugin_path_fs_change`;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
import type {GlobalConfig} from 'types/Config';
10+
import type {WatchPlugin, UsageData} from '../types';
11+
12+
export const filterInteractivePlugins = (
13+
watchPlugins: Array<WatchPlugin>,
14+
globalConfig: GlobalConfig,
15+
): Array<WatchPlugin> => {
16+
const usageInfos = watchPlugins.map(
17+
p => p.getUsageInfo && p.getUsageInfo(globalConfig),
18+
);
19+
20+
return watchPlugins.filter((plugin, i, array) => {
21+
if (usageInfos[i]) {
22+
const {key} = usageInfos[i];
23+
return !usageInfos.slice(i + 1).some(u => u && key === u.key);
24+
}
25+
26+
return false;
27+
});
28+
};
29+
30+
export const getSortedUsageRows = (
31+
watchPlugins: Array<WatchPlugin>,
32+
globalConfig: GlobalConfig,
33+
): Array<UsageData> => {
34+
return filterInteractivePlugins(watchPlugins, globalConfig)
35+
.sort((a: WatchPlugin, b: WatchPlugin) => {
36+
if (a.isInternal) {
37+
return -1;
38+
}
39+
40+
const usageInfoA = a.getUsageInfo && a.getUsageInfo(globalConfig);
41+
const usageInfoB = b.getUsageInfo && b.getUsageInfo(globalConfig);
42+
43+
if (usageInfoA && usageInfoB) {
44+
return usageInfoA.key - usageInfoB.key;
45+
}
46+
47+
return 0;
48+
})
49+
.map(p => p.getUsageInfo && p.getUsageInfo(globalConfig))
50+
.filter(Boolean);
51+
};

packages/jest-cli/src/plugins/quit.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
import BaseWatchPlugin from '../base_watch_plugin';
1010

1111
class QuitPlugin extends BaseWatchPlugin {
12+
isInternal: true;
13+
14+
constructor(options: {
15+
stdin: stream$Readable | tty$ReadStream,
16+
stdout: stream$Writable | tty$WriteStream,
17+
}) {
18+
super(options);
19+
this.isInternal = true;
20+
}
21+
1222
async run() {
1323
if (typeof this._stdin.setRawMode === 'function') {
1424
this._stdin.setRawMode(false);

packages/jest-cli/src/plugins/test_name_pattern.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import Prompt from '../lib/Prompt';
1414

1515
class TestNamePatternPlugin extends BaseWatchPlugin {
1616
_prompt: Prompt;
17+
isInternal: true;
1718

1819
constructor(options: {
1920
stdin: stream$Readable | tty$ReadStream,
2021
stdout: stream$Writable | tty$WriteStream,
2122
}) {
2223
super(options);
2324
this._prompt = new Prompt();
25+
this.isInternal = true;
2426
}
2527

2628
getUsageInfo() {

packages/jest-cli/src/plugins/test_path_pattern.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import Prompt from '../lib/Prompt';
1515

1616
class TestPathPatternPlugin extends BaseWatchPlugin {
1717
_prompt: Prompt;
18+
isInternal: true;
1819

1920
constructor(options: {
2021
stdin: stream$Readable | tty$ReadStream,
2122
stdout: stream$Writable | tty$WriteStream,
2223
}) {
2324
super(options);
2425
this._prompt = new Prompt();
26+
this.isInternal = true;
2527
}
2628

2729
getUsageInfo() {

packages/jest-cli/src/plugins/update_snapshots.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ import type {JestHookSubscriber} from '../jest_hooks';
1212

1313
class UpdateSnapshotsPlugin extends BaseWatchPlugin {
1414
_hasSnapshotFailure: boolean;
15+
isInternal: true;
16+
17+
constructor(options: {
18+
stdin: stream$Readable | tty$ReadStream,
19+
stdout: stream$Writable | tty$WriteStream,
20+
}) {
21+
super(options);
22+
this.isInternal = true;
23+
}
24+
1525
run(
1626
globalConfig: GlobalConfig,
1727
updateConfigAndRun: Function,

packages/jest-cli/src/plugins/update_snapshots_interactive.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import SnapshotInteractiveMode from '../snapshot_interactive_mode';
1414

1515
class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin {
1616
_snapshotInteractiveMode: SnapshotInteractiveMode;
17+
_failedSnapshotTestPaths: Array<*>;
1718
_failedSnapshotTestAssertions: Array<AssertionLocation>;
19+
isInternal: true;
1820

1921
constructor(options: {
2022
stdin: stream$Readable | tty$ReadStream,
@@ -23,6 +25,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin {
2325
super(options);
2426
this._failedSnapshotTestAssertions = [];
2527
this._snapshotInteractiveMode = new SnapshotInteractiveMode(this._stdout);
28+
this.isInternal = true;
2629
}
2730

2831
getFailedSnapshotTestAssertions(

packages/jest-cli/src/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type JestHooks = {
1919
};
2020

2121
export interface WatchPlugin {
22+
+isInternal?: boolean;
2223
+apply?: (hooks: JestHookSubscriber) => void;
2324
+getUsageInfo?: (globalConfig: GlobalConfig) => ?UsageData;
2425
+onKey?: (value: string) => void;

packages/jest-cli/src/watch.js

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ import TestNamePatternPlugin from './plugins/test_name_pattern';
3333
import UpdateSnapshotsPlugin from './plugins/update_snapshots';
3434
import UpdateSnapshotsInteractivePlugin from './plugins/update_snapshots_interactive';
3535
import QuitPlugin from './plugins/quit';
36+
import {
37+
getSortedUsageRows,
38+
filterInteractivePlugins,
39+
} from './lib/watch_plugins_helpers';
3640
import activeFilters from './lib/active_filters_message';
3741

3842
let hasExitListener = false;
@@ -45,24 +49,6 @@ const INTERNAL_PLUGINS = [
4549
QuitPlugin,
4650
];
4751

48-
const getSortedUsageRows = (
49-
watchPlugins: Array<WatchPlugin>,
50-
globalConfig: GlobalConfig,
51-
) => {
52-
const internalPlugins = watchPlugins
53-
.slice(0, INTERNAL_PLUGINS.length)
54-
.map(p => p.getUsageInfo && p.getUsageInfo(globalConfig))
55-
.filter(Boolean);
56-
57-
const thirdPartyPlugins = watchPlugins
58-
.slice(INTERNAL_PLUGINS.length)
59-
.map(p => p.getUsageInfo && p.getUsageInfo(globalConfig))
60-
.filter(Boolean)
61-
.sort((a, b) => a.key - b.key);
62-
63-
return internalPlugins.concat(thirdPartyPlugins);
64-
};
65-
6652
export default function watch(
6753
initialGlobalConfig: GlobalConfig,
6854
contexts: Array<Context>,
@@ -285,7 +271,10 @@ export default function watch(
285271
return;
286272
}
287273

288-
const matchingWatchPlugin = watchPlugins.find(plugin => {
274+
const matchingWatchPlugin = filterInteractivePlugins(
275+
watchPlugins,
276+
globalConfig,
277+
).find(plugin => {
289278
const usageData =
290279
(plugin.getUsageInfo && plugin.getUsageInfo(globalConfig)) || {};
291280
return usageData.key === parseInt(key, 16);

0 commit comments

Comments
 (0)