Skip to content
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

Add shorthand for watch plugins and runners #7213

Merged
merged 9 commits into from
Oct 26, 2018
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- `[jest-config]` Add shorthand for watch plugins and runners ([#7213](https://github.com/facebook/jest/pull/7213))
- `[jest-jasmine2/jest-circus/jest-cli]` Add test.todo ([#6996](https://github.com/facebook/jest/pull/6996))
- `[pretty-format]` Option to not escape strings in diff messages ([#5661](https://github.com/facebook/jest/pull/5661))
- `[jest-haste-map]` Add `getFileIterator` to `HasteFS` for faster file iteration ([#7010](https://github.com/facebook/jest/pull/7010)).
Expand Down
18 changes: 18 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,8 @@ This option allows you to use a custom runner instead of Jest's default test run
- [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc)
- [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier)

_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._

To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature:

```ts
Expand Down Expand Up @@ -960,3 +962,19 @@ Default: `[]`
An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests.

These patterns match against the full path. Use the `<rootDir>` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["<rootDir>/node_modules/"]`.

### `watchPlugins` [array<string | [string, Object]>]

Default: `[]`

This option allows you to use a custom watch plugins. Read more about watch plugins [here](watch-plugins).

Examples of watch plugins include:

- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master)
- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects)
- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)
- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)
- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces)

_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ exports[`rootDir throws if the options is missing a rootDir property 1`] = `
<red></>"
`;

exports[`runner throw error when a runner is not found 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Jest Runner <bold>missing-runner</> cannot be found. Make sure the <bold>runner</> configuration option points to an existing node module.</>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;

exports[`testEnvironment throws on invalid environment names 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
Expand All @@ -70,3 +80,13 @@ exports[`testMatch throws if testRegex and testMatch are both specified 1`] = `
exports[`testPathPattern <regexForTestFiles> ignores invalid regular expressions and logs a warning 1`] = `"<red> Invalid testPattern a( supplied. Running all tests instead.</>"`;

exports[`testPathPattern --testPathPattern ignores invalid regular expressions and logs a warning 1`] = `"<red> Invalid testPattern a( supplied. Running all tests instead.</>"`;

exports[`watchPlugins throw error when a watch plugin is not found 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Watch plugin <bold>missing-plugin</> cannot be found. Make sure the <bold>watchPlugins</> configuration option points to an existing node module.</>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;
121 changes: 111 additions & 10 deletions packages/jest-config/src/__tests__/normalize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,8 @@ describe('testEnvironment', () => {
beforeEach(() => {
Resolver = require('jest-resolve');
Resolver.findNodeModule = jest.fn(name => {
if (name === 'jsdom') {
return 'node_modules/jsdom';
}
if (name === 'jest-environment-jsdom') {
return 'node_modules/jest-environment-jsdom';
if (['jsdom', 'jest-environment-jsdom'].includes(name)) {
return `node_modules/${name}`;
}
if (name.startsWith('/root')) {
return name;
Expand Down Expand Up @@ -1125,15 +1122,79 @@ describe('preset without setupFiles', () => {
});
});

describe('runner', () => {
let Resolver;
beforeEach(() => {
Resolver = require('jest-resolve');
Resolver.findNodeModule = jest.fn(name => {
if (['eslint', 'jest-runner-eslint', 'my-runner-foo'].includes(name)) {
return `node_modules/${name}`;
}
thymikee marked this conversation as resolved.
Show resolved Hide resolved
if (name.startsWith('/root')) {
return name;
}
return findNodeModule(name);
});
});

it('defaults to `jest-runner`', () => {
const {options} = normalize({rootDir: '/root'}, {});

expect(options.runner).toBe('jest-runner');
});

it('resolves to runners that do not have the prefix', () => {
const {options} = normalize(
{
rootDir: '/root/',
runner: 'my-runner-foo',
},
{},
);

expect(options.runner).toBe('node_modules/my-runner-foo');
});

it('resolves to runners and prefers jest-runner-`name`', () => {
const {options} = normalize(
{
rootDir: '/root/',
runner: 'eslint',
},
{},
);

expect(options.runner).toBe('node_modules/jest-runner-eslint');
});

it('throw error when a runner is not found', () => {
expect(() =>
normalize(
{
rootDir: '/root/',
runner: 'missing-runner',
},
{},
),
).toThrowErrorMatchingSnapshot();
});
});

describe('watchPlugins', () => {
let Resolver;
beforeEach(() => {
Resolver = require('jest-resolve');
Resolver.findNodeModule = jest.fn(name => {
if (name.startsWith(path.sep)) {
if (
['typeahead', 'jest-watch-typeahead', 'my-watch-plugin'].includes(name)
) {
return `node_modules/${name}`;
}

if (name.startsWith('/root')) {
return name;
}
return path.sep + 'node_modules' + path.sep + name;
return findNodeModule(name);
});
});

Expand All @@ -1143,20 +1204,60 @@ describe('watchPlugins', () => {
expect(options.watchPlugins).toEqual(undefined);
});

it('normalizes watchPlugins', () => {
it('resolves to watch plugins and prefers jest-watch-`name`', () => {
const {options} = normalize(
{
rootDir: '/root/',
watchPlugins: ['my-watch-plugin', '<rootDir>/path/to/plugin'],
watchPlugins: ['typeahead'],
},
{},
);

expect(options.watchPlugins).toEqual([
{config: {}, path: '/node_modules/my-watch-plugin'},
{config: {}, path: 'node_modules/jest-watch-typeahead'},
]);
});

it('resolves watch plugins that do not have the prefix', () => {
const {options} = normalize(
{
rootDir: '/root/',
watchPlugins: ['my-watch-plugin'],
},
{},
);

expect(options.watchPlugins).toEqual([
{config: {}, path: 'node_modules/my-watch-plugin'},
]);
});

it('normalizes multiple watchPlugins', () => {
const {options} = normalize(
{
rootDir: '/root/',
watchPlugins: ['jest-watch-typeahead', '<rootDir>/path/to/plugin'],
},
{},
);

expect(options.watchPlugins).toEqual([
{config: {}, path: 'node_modules/jest-watch-typeahead'},
{config: {}, path: '/root/path/to/plugin'},
]);
});

it('throw error when a watch plugin is not found', () => {
expect(() =>
normalize(
{
rootDir: '/root/',
watchPlugins: ['missing-plugin'],
},
{},
),
).toThrowErrorMatchingSnapshot();
});
});

describe('testPathPattern', () => {
Expand Down
22 changes: 16 additions & 6 deletions packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
_replaceRootDirTags,
escapeGlobCharacters,
getTestEnvironment,
getRunner,
getWatchPlugin,
resolve,
} from './utils';
import {DEFAULT_JS_PATTERN, DEFAULT_REPORTER_LABEL} from './constants';
Expand Down Expand Up @@ -372,7 +374,10 @@ export default function normalize(options: InitialOptions, argv: Argv) {
}

if (options.testEnvironment) {
options.testEnvironment = getTestEnvironment(options);
options.testEnvironment = getTestEnvironment({
rootDir: options.rootDir,
testEnvironment: options.testEnvironment,
});
}

if (!options.roots && options.testPathDirs) {
Expand Down Expand Up @@ -452,7 +457,6 @@ export default function normalize(options: InitialOptions, argv: Argv) {
case 'globalSetup':
case 'globalTeardown':
case 'moduleLoader':
case 'runner':
case 'setupTestFrameworkScriptFile':
case 'snapshotResolver':
case 'testResultsProcessor':
Expand All @@ -466,6 +470,14 @@ export default function normalize(options: InitialOptions, argv: Argv) {
rootDir: options.rootDir,
});
break;
case 'runner':
value =
options[key] &&
getRunner(newOptions.resolver, {
filePath: options[key],
rootDir: options.rootDir,
});
break;
case 'prettierPath':
// We only want this to throw if "prettierPath" is explicitly passed
// from config or CLI, and the requested path isn't found. Otherwise we
Expand Down Expand Up @@ -634,18 +646,16 @@ export default function normalize(options: InitialOptions, argv: Argv) {
if (typeof watchPlugin === 'string') {
return {
config: {},
path: resolve(newOptions.resolver, {
path: getWatchPlugin(newOptions.resolver, {
filePath: watchPlugin,
key,
rootDir: options.rootDir,
}),
};
} else {
return {
config: watchPlugin[1] || {},
path: resolve(newOptions.resolver, {
path: getWatchPlugin(newOptions.resolver, {
filePath: watchPlugin[0],
key,
rootDir: options.rootDir,
}),
};
Expand Down
Loading