Skip to content

Commit

Permalink
Add option testSequencer allow user use custom sequencer. (#8223)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeiAnAn authored and SimenB committed Apr 2, 2019
1 parent ac9bbed commit 2bc6280
Show file tree
Hide file tree
Showing 29 changed files with 207 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,9 @@

### Features

- `[@jest/core, @jest/test-sequencer]` Move `testSequencer` to individual package `@jest/test-sequencer` ([#8223](https://github.com/facebook/jest/pull/8223))
- `[@jest/core, jest-cli, jest-config]` Add option `testSequencer` allow user use custom sequencer. ([#8223](https://github.com/facebook/jest/pull/8223))

### Fixes

### Chore & Maintenance
Expand Down
4 changes: 4 additions & 0 deletions docs/CLI.md
Expand Up @@ -297,6 +297,10 @@ An array of regexp pattern strings that is tested against all tests paths before

Lets you specify a custom test runner.

### `--testSequencer=<path>`

Lets you specify a custom test sequencer. Please refer to the documentation of the corresponding configuration property for details.

### `--updateSnapshot`

Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots.
Expand Down
25 changes: 25 additions & 0 deletions docs/Configuration.md
Expand Up @@ -1018,6 +1018,31 @@ function testRunner(

An example of such function can be found in our default [jasmine2 test runner package](https://github.com/facebook/jest/blob/master/packages/jest-jasmine2/src/index.js).

### `testSequencer` [string]

Default: `@jest/test-sequencer`

This option allows you to use a custom sequencer instead of Jest's default.

Example:

Sort test path alphabetically

```js
const Sequencer = require('@jest/test-sequencer').default;

class CustomSequencer extends Sequencer {
sort(tests) {
// Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}

module.exports = CustomSequencer;
```

### `testURL` [string]

Default: `http://localhost`
Expand Down
1 change: 1 addition & 0 deletions e2e/__tests__/__snapshots__/showConfig.test.ts.snap
Expand Up @@ -117,6 +117,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
"testFailureExitCode": 1,
"testPathPattern": "",
"testResultsProcessor": null,
"testSequencer": "@jest/test-sequencer",
"updateSnapshot": "all",
"useStderr": false,
"verbose": null,
Expand Down
26 changes: 26 additions & 0 deletions e2e/__tests__/customTestSequencers.test.ts
@@ -0,0 +1,26 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/

import path from 'path';
import runJest from '../runJest';
import {extractSummary} from '../Utils';
const dir = path.resolve(__dirname, '../custom-test-sequencer');

test('run prioritySequence first', () => {
const result = runJest(dir, ['-i']);
expect(result.status).toBe(0);
const sequence = extractSummary(result.stderr)
.rest.replace(/PASS /g, '')
.split('\n');
expect(sequence).toEqual([
'./a.test.js',
'./b.test.js',
'./c.test.js',
'./d.test.js',
'./e.test.js',
]);
});
7 changes: 7 additions & 0 deletions e2e/custom-test-sequencer/a.test.js
@@ -0,0 +1,7 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/
test('a', () => {});
7 changes: 7 additions & 0 deletions e2e/custom-test-sequencer/b.test.js
@@ -0,0 +1,7 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/
test('b', () => {});
7 changes: 7 additions & 0 deletions e2e/custom-test-sequencer/c.test.js
@@ -0,0 +1,7 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/
test('c', () => {});
7 changes: 7 additions & 0 deletions e2e/custom-test-sequencer/d.test.js
@@ -0,0 +1,7 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/
test('d', () => {});
7 changes: 7 additions & 0 deletions e2e/custom-test-sequencer/e.test.js
@@ -0,0 +1,7 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/
test('e', () => {});
6 changes: 6 additions & 0 deletions e2e/custom-test-sequencer/package.json
@@ -0,0 +1,6 @@
{
"jest": {
"testEnvironment": "node",
"testSequencer": "<rootDir>/testSequencer.js"
}
}
17 changes: 17 additions & 0 deletions e2e/custom-test-sequencer/testSequencer.js
@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. 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.
*/

const Sequencer = require('@jest/test-sequencer').default;

class CustomSequencer extends Sequencer {
sort(tests) {
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}

module.exports = CustomSequencer;
7 changes: 7 additions & 0 deletions packages/jest-cli/src/cli/args.ts
Expand Up @@ -615,6 +615,13 @@ export const options = {
'`<rootDir>/path/to/testRunner.js`.',
type: 'string' as 'string',
},
testSequencer: {
description:
'Allows to specify a custom test sequencer. The default is ' +
'`@jest/test-sequencer`. A path to a custom test sequencer can be ' +
'provided: `<rootDir>/path/to/testSequencer.js`',
type: 'string' as 'string',
},
testURL: {
description: 'This option sets the URL for the jsdom environment.',
type: 'string' as 'string',
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/package.json
Expand Up @@ -11,6 +11,7 @@
"types": "build/index.d.ts",
"dependencies": {
"@babel/core": "^7.1.0",
"@jest/test-sequencer": "^24.6.0",
"@jest/types": "^24.6.0",
"babel-jest": "^24.6.0",
"chalk": "^2.0.1",
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/Defaults.ts
Expand Up @@ -71,6 +71,7 @@ const defaultOptions: Config.DefaultOptions = {
testRegex: [],
testResultsProcessor: null,
testRunner: 'jasmine2',
testSequencer: '@jest/test-sequencer',
testURL: 'http://localhost',
timers: 'real',
transform: null,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/ValidConfig.ts
Expand Up @@ -114,6 +114,7 @@ const initialOptions: Config.InitialOptions = {
),
testResultsProcessor: 'processor-node-module',
testRunner: 'jasmine2',
testSequencer: '@jest/test-sequencer',
testURL: 'http://localhost',
timers: 'real',
transform: {
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/index.ts
Expand Up @@ -148,6 +148,7 @@ const groupOptions = (
testNamePattern: options.testNamePattern,
testPathPattern: options.testPathPattern,
testResultsProcessor: options.testResultsProcessor,
testSequencer: options.testSequencer,
updateSnapshot: options.updateSnapshot,
useStderr: options.useStderr,
verbose: options.verbose,
Expand Down
12 changes: 12 additions & 0 deletions packages/jest-config/src/normalize.ts
Expand Up @@ -29,6 +29,7 @@ import {
getRunner,
getWatchPlugin,
resolve,
getSequencer,
} from './utils';
import {DEFAULT_JS_PATTERN, DEFAULT_REPORTER_LABEL} from './constants';
import {validateReporters} from './ReporterValidationErrors';
Expand Down Expand Up @@ -588,6 +589,17 @@ export default function normalize(
});
}
break;
case 'testSequencer':
{
const option = oldOptions[key];
value =
option &&
getSequencer(newOptions.resolver, {
filePath: option,
rootDir: options.rootDir,
});
}
break;
case 'prettierPath':
{
// We only want this to throw if "prettierPath" is explicitly passed
Expand Down
12 changes: 12 additions & 0 deletions packages/jest-config/src/utils.ts
Expand Up @@ -223,3 +223,15 @@ export const isJSONString = (text?: JSONString | string): text is JSONString =>
typeof text === 'string' &&
text.startsWith('{') &&
text.endsWith('}');

export const getSequencer = (
resolver: string | undefined | null,
{filePath, rootDir}: {filePath: string; rootDir: Config.Path},
) =>
resolveWithPrefix(resolver, {
filePath,
humanOptionName: 'Jest Sequencer',
optionName: 'testSequencer',
prefix: 'jest-sequencer-',
rootDir,
});
1 change: 1 addition & 0 deletions packages/jest-core/package.json
Expand Up @@ -34,6 +34,7 @@
"strip-ansi": "^5.0.0"
},
"devDependencies": {
"@jest/test-sequencer": "^24.6.0",
"@types/ansi-escapes": "^3.0.1",
"@types/exit": "^0.1.30",
"@types/graceful-fs": "^4.1.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-core/src/__tests__/run_jest.test.js
Expand Up @@ -16,7 +16,7 @@ describe('runJest', () => {
await runJest({
changedFilesPromise: Promise.resolve({repos: {git: {size: 0}}}),
contexts: [],
globalConfig: {watch: true},
globalConfig: {testSequencer: '@jest/test-sequencer', watch: true},
onComplete: () => null,
outputStream: {},
startRun: {},
Expand Down
9 changes: 6 additions & 3 deletions packages/jest-core/src/runJest.ts
Expand Up @@ -9,7 +9,7 @@ import path from 'path';
import chalk from 'chalk';
import {sync as realpath} from 'realpath-native';
import {CustomConsole} from '@jest/console';
import {formatTestResults} from 'jest-util';
import {formatTestResults, interopRequireDefault} from 'jest-util';
import exit from 'exit';
import fs from 'graceful-fs';
import {JestHook, JestHookEmitter} from 'jest-watcher';
Expand All @@ -20,12 +20,12 @@ import {
AggregatedResult,
makeEmptyAggregatedTestResult,
} from '@jest/test-result';
import TestSequencer from '@jest/test-sequencer'; // eslint-disable-line import/no-extraneous-dependencies
import {ChangedFiles, ChangedFilesPromise} from 'jest-changed-files';
import getNoTestsFoundMessage from './getNoTestsFoundMessage';
import runGlobalHook from './runGlobalHook';
import SearchSource from './SearchSource';
import TestScheduler, {TestSchedulerContext} from './TestScheduler';
import TestSequencer from './TestSequencer';
import FailedTestsCache from './FailedTestsCache';
import collectNodeHandles from './collectHandles';
import TestWatcher from './TestWatcher';
Expand Down Expand Up @@ -143,7 +143,10 @@ export default (async function runJest({
failedTestsCache?: FailedTestsCache;
filter?: Filter;
}) {
const sequencer = new TestSequencer();
const Sequencer: typeof TestSequencer = interopRequireDefault(
require(globalConfig.testSequencer),
).default;
const sequencer = new Sequencer();
let allTests: Array<Test> = [];

if (changedFilesPromise && globalConfig.watch) {
Expand Down
1 change: 1 addition & 0 deletions packages/jest-core/tsconfig.json
Expand Up @@ -17,6 +17,7 @@
{"path": "../jest-runtime"},
{"path": "../jest-snapshot"},
{"path": "../jest-test-result"},
{"path": "../jest-test-sequencer"},
{"path": "../jest-types"},
{"path": "../jest-transform"},
{"path": "../jest-util"},
Expand Down
3 changes: 3 additions & 0 deletions packages/jest-test-sequencer/.npmignore
@@ -0,0 +1,3 @@
**/__mocks__/**
**/__tests__/**
src
25 changes: 25 additions & 0 deletions packages/jest-test-sequencer/package.json
@@ -0,0 +1,25 @@
{
"name": "@jest/test-sequencer",
"version": "24.6.0",
"repository": {
"type": "git",
"url": "https://github.com/facebook/jest.git",
"directory": "packages/test-sequencer"
},
"license": "MIT",
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@jest/test-result": "^24.6.0",
"jest-haste-map": "^24.6.0",
"jest-runner": "^24.6.0",
"jest-runtime": "^24.6.0"
},
"engines": {
"node": ">= 6"
},
"publishConfig": {
"access": "public"
},
"gitHead": "800533020f5b2f153615c821ed7cb12fd868fa6f"
}
Expand Up @@ -6,7 +6,7 @@
*/
'use strict';

import TestSequencer from '../TestSequencer';
import TestSequencer from '../index';

jest.mock('fs');

Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions packages/jest-test-sequencer/tsconfig.json
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "build"
},
"references": [
{"path": "../jest-haste-map"},
{"path": "../jest-runner"},
{"path": "../jest-test-result"},
{"path": "../jest-types"}
]
}
4 changes: 4 additions & 0 deletions packages/jest-types/src/Config.ts
Expand Up @@ -88,6 +88,7 @@ export type DefaultOptions = {
testRegex: Array<string>;
testResultsProcessor: string | null | undefined;
testRunner: string | null | undefined;
testSequencer: string;
testURL: string;
timers: 'real' | 'fake';
transform:
Expand Down Expand Up @@ -203,6 +204,7 @@ export type InitialOptions = {
testRegex?: string | Array<string>;
testResultsProcessor?: string | null | undefined;
testRunner?: string;
testSequencer?: string;
testURL?: string;
timers?: 'real' | 'fake';
transform?: {
Expand Down Expand Up @@ -340,6 +342,7 @@ export type GlobalConfig = {
testNamePattern: string;
testPathPattern: string;
testResultsProcessor: string | null | undefined;
testSequencer: string;
updateSnapshot: SnapshotUpdateState;
useStderr: boolean;
verbose: boolean | null | undefined;
Expand Down Expand Up @@ -483,6 +486,7 @@ export type Argv = Arguments<
testRegex: string | Array<string>;
testResultsProcessor: string | null | undefined;
testRunner: string;
testSequencer: string;
testURL: string;
timers: string;
transform: string;
Expand Down

0 comments on commit 2bc6280

Please sign in to comment.