Skip to content

Commit

Permalink
Feature: broader uses for watch plugins (#6473)
Browse files Browse the repository at this point in the history
* Allow WatchPlugins access to a broader list of global config options

Refs #6467

* Tests for whitelisting config options in WatchPlugins

* Docs on allowed config options in WatchPlugins

* chore: Changelog

* tests: leverage it.each to clean up code

* refactor: centralize config update logic

* Update plugin test to use new signature for configs

* Refactor watch extensions as per PR review (#6473)
  • Loading branch information
tdd authored and SimenB committed Jul 12, 2018
1 parent 7d9544b commit 6c61baa
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 49 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## master

### Features

- `[jest-cli]` Watch plugins now have access to a broader range of global configuration options in their `updateConfigAndRun` callbacks, so they can provide a wider set of extra features ([#6473](https://github.com/facebook/jest/pull/6473))

## 23.4.0

### Features
Expand Down
21 changes: 21 additions & 0 deletions docs/WatchPlugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,27 @@ class MyWatchPlugin {
}
```

**Note**: If you do call `updateConfigAndRun`, your `run` method should not resolve to a truthy value, as that would trigger a double-run.

#### Authorized configuration keys

For stability and safety reasons, only part of the global configuration keys can be updated with `updateConfigAndRun`. The current white list is as follows:

- [`bail`](configuration.html#bail-boolean)
- [`collectCoverage`](configuration.html#collectcoverage-boolean)
- [`collectCoverageFrom`](configuration.html#collectcoveragefrom-array)
- [`collectCoverageOnlyFrom`](configuration.html#collectcoverageonlyfrom-array)
- [`coverageDirectory`](configuration.html#coveragedirectory-string)
- [`coverageReporters`](configuration.html#coveragereporters-array)
- [`notify`](configuration.html#notify-boolean)
- [`notifyMode`](configuration.html#notifymode-string)
- [`onlyFailures`](configuration.html#onlyfailures-boolean)
- [`reporters`](configuration.html#reporters-array-modulename-modulename-options)
- [`testNamePattern`](cli.html#testnamepattern-regex)
- [`testPathPattern`](cli.html#testpathpattern-regex)
- [`updateSnapshot`](cli.html#updatesnapshot)
- [`verbose`](configuration.html#verbose-boolean)

## Customization

Plugins can be customized via your Jest configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 8`] = `
[MOCK - cursorRestorePosition]"
`;

exports[`Watch mode flows Pressing "P" enters pattern mode 9`] = `
Object {
"onlyChanged": false,
"passWithNoTests": true,
"testPathPattern": "p.*3",
"watch": true,
"watchAll": false,
}
`;

exports[`Watch mode flows Pressing "c" clears the filters 1`] = `
"[MOCK - cursorHide]
[MOCK - clearScreen]
Expand Down
102 changes: 100 additions & 2 deletions packages/jest-cli/src/__tests__/watch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ jest.doMock(
{virtual: true},
);

const regularUpdateGlobalConfig = require('../lib/update_global_config')
.default;
const updateGlobalConfig = jest.fn(regularUpdateGlobalConfig);
jest.doMock('../lib/update_global_config', () => updateGlobalConfig);

const watch = require('../watch').default;

const nextTick = () => new Promise(res => process.nextTick(res));
Expand Down Expand Up @@ -435,6 +440,101 @@ describe('Watch mode flows', () => {
});
});

it.each`
ok | option
✔︎ | bail
✖︎ | changedFilesWithAncestor
✖︎ | changedSince
✔︎ | collectCoverage
✔︎ | collectCoverageFrom
✔︎ | collectCoverageOnlyFrom
✔︎ | coverageDirectory
✔︎ | coverageReporters
✖︎ | coverageThreshold
✖︎ | detectLeaks
✖︎ | detectOpenHandles
✖︎ | enabledTestsMap
✖︎ | errorOnDeprecated
✖︎ | expand
✖︎ | filter
✖︎ | findRelatedTests
✖︎ | forceExit
✖︎ | globalSetup
✖︎ | globalTeardown
✖︎ | json
✖︎ | lastCommit
✖︎ | listTests
✖︎ | logHeapUsage
✖︎ | maxWorkers
✖︎ | nonFlagArgs
✖︎ | noSCM
✖︎ | noStackTrace
✔︎ | notify
✔︎ | notifyMode
✖︎ | onlyChanged
✔︎ | onlyFailures
✖︎ | outputFile
✖︎ | passWithNoTests
✖︎ | projects
✖︎ | replname
✔︎ | reporters
✖︎ | rootDir
✖︎ | runTestsByPath
✖︎ | silent
✖︎ | skipFilter
✖︎ | testFailureExitCode
✔︎ | testNamePattern
✔︎ | testPathPattern
✖︎ | testResultsProcessor
✔︎ | updateSnapshot
✖︎ | useStderr
✔︎ | verbose
✖︎ | watch
✖︎ | watchAll
✖︎ | watchman
✖︎ | watchPlugins
`(
'allows WatchPlugins to modify only white-listed global config keys',
async ({ok, option}) => {
const pluginPath = `${__dirname}/__fixtures__/plugin_path_config_updater`;
const config = Object.assign({}, globalConfig, {
rootDir: __dirname,
watchPlugins: [{config: {}, path: pluginPath}],
});

jest.doMock(
pluginPath,
() =>
class WatchPlugin {
getUsageInfo() {
return {key: 'x', prompt: 'test option white-listing'};
}

run(globalConfig, updateConfigAndRun) {
updateConfigAndRun({[option]: '__JUST_TRYING__'});
return Promise.resolve();
}
},
{virtual: true},
);

watch(config, contexts, pipe, hasteMapInstances, stdin);
await nextTick();

stdin.emit('x');
await nextTick();

const lastCall = updateGlobalConfig.mock.calls.slice(-1)[0];
let expector = expect(lastCall[0]);
if (!ok) {
expector = expector.not;
}
expector.toMatchObject({
[option]: '__JUST_TRYING__',
});
},
);

it('triggers enter on a WatchPlugin when its key is pressed', async () => {
const run = jest.fn(() => Promise.resolve());
const pluginPath = `${__dirname}/__fixtures__/plugin_path`;
Expand Down Expand Up @@ -588,7 +688,6 @@ describe('Watch mode flows', () => {

expect(runJestMock.mock.calls[0][0].globalConfig).toMatchObject({
testNamePattern: 'test',
testPathPattern: '',
watch: true,
watchAll: false,
});
Expand All @@ -606,7 +705,6 @@ describe('Watch mode flows', () => {
await nextTick();

expect(runJestMock.mock.calls[0][0].globalConfig).toMatchObject({
testNamePattern: '',
testPathPattern: 'file',
watch: true,
watchAll: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,7 @@ describe('Watch mode flows', () => {
expect(runJestMock).toBeCalled();

// globalConfig is updated with the current pattern
expect(runJestMock.mock.calls[0][0].globalConfig).toEqual({
onlyChanged: false,
passWithNoTests: true,
testNamePattern: '',
testPathPattern: 'p.*3',
watch: true,
watchAll: false,
});
expect(runJestMock.mock.calls[0][0].globalConfig).toMatchSnapshot();
});

it('Pressing "c" clears the filters', async () => {
Expand Down
94 changes: 75 additions & 19 deletions packages/jest-cli/src/lib/update_global_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,31 @@
* @flow
*/

import type {GlobalConfig, SnapshotUpdateState} from 'types/Config';
import {replacePathSepForRegex} from 'jest-regex-util';

type Options = {
testNamePattern?: string,
testPathPattern?: string,
noSCM?: boolean,
updateSnapshot?: SnapshotUpdateState,
import type {GlobalConfig} from 'types/Config';

export type Options = {
bail?: $PropertyType<GlobalConfig, 'bail'>,
collectCoverage?: $PropertyType<GlobalConfig, 'collectCoverage'>,
collectCoverageFrom?: $PropertyType<GlobalConfig, 'collectCoverageFrom'>,
collectCoverageOnlyFrom?: $PropertyType<
GlobalConfig,
'collectCoverageOnlyFrom',
>,
coverageDirectory?: $PropertyType<GlobalConfig, 'coverageDirectory'>,
coverageReporters?: $PropertyType<GlobalConfig, 'coverageReporters'>,
mode?: 'watch' | 'watchAll',
passWithNoTests?: boolean,
onlyFailures?: boolean,
noSCM?: $PropertyType<GlobalConfig, 'noSCM'>,
notify?: $PropertyType<GlobalConfig, 'notify'>,
notifyMode?: $PropertyType<GlobalConfig, 'notifyMode'>,
onlyFailures?: $PropertyType<GlobalConfig, 'onlyFailures'>,
passWithNoTests?: $PropertyType<GlobalConfig, 'passWithNoTests'>,
reporters?: $PropertyType<GlobalConfig, 'reporters'>,
testNamePattern?: $PropertyType<GlobalConfig, 'testNamePattern'>,
testPathPattern?: $PropertyType<GlobalConfig, 'testPathPattern'>,
updateSnapshot?: $PropertyType<GlobalConfig, 'updateSnapshot'>,
verbose?: $PropertyType<GlobalConfig, 'verbose'>,
};

export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
Expand All @@ -27,10 +42,6 @@ export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
options = {};
}

if (options.updateSnapshot) {
newConfig.updateSnapshot = options.updateSnapshot;
}

if (options.mode === 'watch') {
newConfig.watch = true;
newConfig.watchAll = false;
Expand All @@ -39,12 +50,13 @@ export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
newConfig.watchAll = true;
}

if ('testPathPattern' in options) {
newConfig.testPathPattern = options.testPathPattern || '';
if (options.testNamePattern !== undefined) {
newConfig.testNamePattern = options.testNamePattern || '';
}

if ('testNamePattern' in options) {
newConfig.testNamePattern = options.testNamePattern || '';
if (options.testPathPattern !== undefined) {
newConfig.testPathPattern =
replacePathSepForRegex(options.testPathPattern) || '';
}

newConfig.onlyChanged = false;
Expand All @@ -53,17 +65,61 @@ export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
!newConfig.testNamePattern &&
!newConfig.testPathPattern;

if (options.bail !== undefined) {
newConfig.bail = options.bail || false;
}

if (options.collectCoverage !== undefined) {
newConfig.collectCoverage = options.collectCoverage || false;
}

if (options.collectCoverageFrom !== undefined) {
newConfig.collectCoverageFrom = options.collectCoverageFrom;
}

if (options.collectCoverageOnlyFrom !== undefined) {
newConfig.collectCoverageOnlyFrom = options.collectCoverageOnlyFrom;
}

if (options.coverageDirectory !== undefined) {
newConfig.coverageDirectory = options.coverageDirectory;
}

if (options.coverageReporters !== undefined) {
newConfig.coverageReporters = options.coverageReporters;
}

if (options.noSCM) {
newConfig.noSCM = true;
}

if (options.passWithNoTests) {
newConfig.passWithNoTests = true;
if (options.notify !== undefined) {
newConfig.notify = options.notify || false;
}

if (options.notifyMode !== undefined) {
newConfig.notifyMode = options.notifyMode;
}

if ('onlyFailures' in options) {
if (options.onlyFailures !== undefined) {
newConfig.onlyFailures = options.onlyFailures || false;
}

if (options.passWithNoTests !== undefined) {
newConfig.passWithNoTests = true;
}

if (options.reporters !== undefined) {
newConfig.reporters = options.reporters;
}

if (options.updateSnapshot !== undefined) {
newConfig.updateSnapshot = options.updateSnapshot;
}

if (options.verbose !== undefined) {
newConfig.verbose = options.verbose || false;
}

return Object.freeze(newConfig);
};
Loading

0 comments on commit 6c61baa

Please sign in to comment.