-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
[Proposal] Watch plugins API #5399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
396cd18
c04edc7
1f74aa1
03b53b1
1aeafb8
b40f604
6e97a6e
ef14ade
8b999ed
2604965
ffc85f1
053d9e4
2036be9
40c4265
44c82ba
7767b71
2b084f8
58f1717
13f08e5
d4ecb92
8f6513e
401e44b
9ac7d94
25e0c4b
4a68617
3dc8c06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /** | ||
| * 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 {WatchPlugin} from '../types'; | ||
| import {getFailedSnapshotTests} from 'jest-util'; | ||
| import SnapshotInteractiveMode from '../snapshot_interactive_mode'; | ||
|
|
||
| const PLUGIN_NAME = 'update-snapshots-interactive'; | ||
| const updateSnapshotsInteractivePlugin: WatchPlugin = { | ||
| apply: (jestHooks, {stdin, stdout}) => { | ||
| let failedSnapshotTestPaths = []; | ||
| const snapshotInteractiveMode = new SnapshotInteractiveMode(stdout); | ||
|
|
||
| jestHooks.testRunComplete.tap(PLUGIN_NAME, results => { | ||
| failedSnapshotTestPaths = getFailedSnapshotTests(results); | ||
| if (snapshotInteractiveMode.isActive()) { | ||
| snapshotInteractiveMode.updateWithResults(results); | ||
| } | ||
| }); | ||
|
|
||
| stdin.on('data', key => { | ||
| if (snapshotInteractiveMode.isActive()) { | ||
| snapshotInteractiveMode.put(key); | ||
| } | ||
| }); | ||
|
|
||
| jestHooks.showPrompt.tapPromise( | ||
| PLUGIN_NAME, | ||
| (globalConfig, updateConfigAndRun) => { | ||
| if (failedSnapshotTestPaths.length) { | ||
| return new Promise(res => { | ||
| snapshotInteractiveMode.run( | ||
| failedSnapshotTestPaths, | ||
| (path: string, shouldUpdateSnapshot: boolean) => { | ||
| updateConfigAndRun({ | ||
| testNamePattern: '', | ||
| testPathPattern: path, | ||
| updateSnapshot: shouldUpdateSnapshot ? 'all' : 'none', | ||
| }); | ||
| if (!snapshotInteractiveMode.isActive()) { | ||
| res(); | ||
| } | ||
| }, | ||
| ); | ||
| }); | ||
| } else { | ||
| return Promise.resolve(); | ||
| } | ||
| }, | ||
| ); | ||
| }, | ||
| key: 'i'.codePointAt(0), | ||
| name: PLUGIN_NAME, | ||
| prompt: 'update failing snapshots interactively', | ||
| shouldShowUsage: (globalConfig, hasSnapshotFailures) => hasSnapshotFailures, | ||
| }; | ||
|
|
||
| export default updateSnapshotsInteractivePlugin; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,26 +18,26 @@ import exit from 'exit'; | |
| import {replacePathSepForRegex} from 'jest-regex-util'; | ||
| import HasteMap from 'jest-haste-map'; | ||
| import isValidPath from './lib/is_valid_path'; | ||
| import {getFailedSnapshotTests, isInteractive} from 'jest-util'; | ||
| import {isInteractive} from 'jest-util'; | ||
| import {print as preRunMessagePrint} from './pre_run_message'; | ||
| import createContext from './lib/create_context'; | ||
| import runJest from './run_jest'; | ||
| import updateGlobalConfig from './lib/update_global_config'; | ||
| import SearchSource from './search_source'; | ||
| import SnapshotInteractiveMode from './snapshot_interactive_mode'; | ||
| import TestWatcher from './test_watcher'; | ||
| import Prompt from './lib/Prompt'; | ||
| import FailedTestsCache from './failed_tests_cache'; | ||
| import WatchPluginRegistry from './lib/watch_plugin_registry'; | ||
| import {KEYS, CLEAR} from './constants'; | ||
| import {AsyncSeriesWaterfallHook} from 'tapable'; | ||
| import {AsyncSeriesWaterfallHook, SyncHook} from 'tapable'; | ||
|
|
||
| let hasExitListener = false; | ||
|
|
||
| const internalPlugins = [ | ||
| require.resolve('./plugins/test_path_pattern'), | ||
| require.resolve('./plugins/test_name_pattern'), | ||
| require.resolve('./plugins/update_snapshots'), | ||
| require.resolve('./plugins/update_snapshots_interactive'), | ||
| require.resolve('./plugins/quit'), | ||
| ]; | ||
|
|
||
|
|
@@ -59,7 +59,11 @@ export default function watch( | |
| }); | ||
|
|
||
| const hooks = { | ||
| showPrompt: new AsyncSeriesWaterfallHook(['globalConfig']), | ||
| showPrompt: new AsyncSeriesWaterfallHook([ | ||
| 'globalConfig', | ||
| 'updateConfigAndRun', | ||
| ]), | ||
| testRunComplete: new SyncHook(['results']), | ||
| }; | ||
|
|
||
| hooks.showPrompt.intercept({ | ||
|
|
@@ -76,6 +80,27 @@ export default function watch( | |
| }, | ||
| }); | ||
|
|
||
| const updateConfigAndRun = ({ | ||
| testNamePattern, | ||
| testPathPattern, | ||
| updateSnapshot, | ||
| } = {}) => { | ||
| const previousUpdateSnapshot = globalConfig.updateSnapshot; | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| mode: 'watch', | ||
| testNamePattern, | ||
| testPathPattern: replacePathSepForRegex(testPathPattern || ''), | ||
| updateSnapshot: updateSnapshot || globalConfig.updateSnapshot, | ||
| }); | ||
|
|
||
| startRun(globalConfig); | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| // updateSnapshot is not sticky after a run. | ||
| updateSnapshot: | ||
| previousUpdateSnapshot === 'all' ? 'none' : previousUpdateSnapshot, | ||
| }); | ||
| }; | ||
|
|
||
| const watchPlugins = new WatchPluginRegistry(globalConfig.rootDir); | ||
|
|
||
| internalPlugins.forEach(pluginPath => { | ||
|
|
@@ -97,8 +122,6 @@ export default function watch( | |
|
|
||
| const failedTestsCache = new FailedTestsCache(); | ||
| const prompt = new Prompt(); | ||
| const snapshotInteractiveMode = new SnapshotInteractiveMode(outputStream); | ||
| let failedSnapshotTestPaths = []; | ||
| let searchSources = contexts.map(context => ({ | ||
| context, | ||
| searchSource: new SearchSource(context), | ||
|
|
@@ -163,7 +186,7 @@ export default function watch( | |
| onComplete: results => { | ||
| isRunning = false; | ||
| hasSnapshotFailure = !!results.snapshot.failure; | ||
| failedSnapshotTestPaths = getFailedSnapshotTests(results); | ||
| hooks.testRunComplete.call(results); | ||
|
|
||
| // Create a new testWatcher instance so that re-runs won't be blocked. | ||
| // The old instance that was passed to Jest will still be interrupted | ||
|
|
@@ -173,10 +196,6 @@ export default function watch( | |
| // Do not show any Watch Usage related stuff when running in a | ||
| // non-interactive environment | ||
| if (isInteractive) { | ||
| if (snapshotInteractiveMode.isActive()) { | ||
| snapshotInteractiveMode.updateWithResults(results); | ||
| return; | ||
| } | ||
| if (shouldDisplayWatchUsage) { | ||
| outputStream.write( | ||
| usage(globalConfig, watchPlugins, hasSnapshotFailure), | ||
|
|
@@ -217,11 +236,6 @@ export default function watch( | |
| return; | ||
| } | ||
|
|
||
| if (snapshotInteractiveMode.isActive()) { | ||
| snapshotInteractiveMode.put(key); | ||
| return; | ||
| } | ||
|
|
||
| // Abort test run | ||
| const pluginKeys = watchPlugins | ||
| .getPluginsOrderedByKey() | ||
|
|
@@ -245,25 +259,10 @@ export default function watch( | |
| // "activate" the plugin, which has jest ignore keystrokes so the plugin | ||
| // can handle them | ||
| activePlugin = matchingWatchPlugin; | ||
| hooks.showPrompt.promise(globalConfig).then( | ||
| ({testNamePattern, testPathPattern, updateSnapshot} = {}) => { | ||
| hooks.showPrompt.promise(globalConfig, updateConfigAndRun).then( | ||
| () => { | ||
| activePlugin = null; | ||
| const previousUpdateSnapshot = globalConfig.updateSnapshot; | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| mode: 'watch', | ||
| testNamePattern, | ||
| testPathPattern: replacePathSepForRegex(testPathPattern || ''), | ||
| updateSnapshot: updateSnapshot || globalConfig.updateSnapshot, | ||
| }); | ||
|
|
||
| startRun(globalConfig); | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| // updateSnapshot is not sticky after a run. | ||
| updateSnapshot: | ||
| previousUpdateSnapshot === 'all' | ||
| ? 'none' | ||
| : previousUpdateSnapshot, | ||
| }); | ||
| updateConfigAndRun(); | ||
| }, | ||
| () => { | ||
| activePlugin = null; | ||
|
|
@@ -276,26 +275,6 @@ export default function watch( | |
| case KEYS.ENTER: | ||
| startRun(globalConfig); | ||
| break; | ||
| case KEYS.I: | ||
| if (hasSnapshotFailure) { | ||
| snapshotInteractiveMode.run( | ||
| failedSnapshotTestPaths, | ||
| (path: string, shouldUpdateSnapshot: boolean) => { | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| mode: 'watch', | ||
| testNamePattern: '', | ||
| testPathPattern: replacePathSepForRegex(path), | ||
| updateSnapshot: shouldUpdateSnapshot ? 'all' : 'none', | ||
| }); | ||
| startRun(globalConfig); | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| // updateSnapshot is not sticky after a run. | ||
| updateSnapshot: 'none', | ||
| }); | ||
| }, | ||
| ); | ||
| } | ||
| break; | ||
| case KEYS.A: | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| mode: 'watchAll', | ||
|
|
@@ -305,12 +284,10 @@ export default function watch( | |
| startRun(globalConfig); | ||
| break; | ||
| case KEYS.C: | ||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
| mode: 'watch', | ||
| updateConfigAndRun({ | ||
| testNamePattern: '', | ||
| testPathPattern: '', | ||
| }); | ||
| startRun(globalConfig); | ||
| break; | ||
| case KEYS.F: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be a plugin as well?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, this one should be a plugin... I can do it in this PR of as follow up PRs
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also want to wait for it because it will require some refactor |
||
| globalConfig = updateGlobalConfig(globalConfig, { | ||
|
|
@@ -415,12 +392,6 @@ const usage = ( | |
| chalk.dim(' to only run tests related to changed files.') | ||
| : null, | ||
|
|
||
| snapshotFailure | ||
| ? chalk.dim(' \u203A Press ') + | ||
| 'i' + | ||
| chalk.dim(' to update failing snapshots interactively.') | ||
| : null, | ||
|
|
||
| ...watchPlugins | ||
| .getPluginsOrderedByKey() | ||
| .filter(plugin => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will get cleaned up