diff --git a/CHANGELOG.md b/CHANGELOG.md index 116c4eab7169..523ff385cd0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-test-sequencer, jest-core]` Exposes globalConfig & contexts to TestSequencer ([#14535](https://github.com/jestjs/jest/pull/14535)) + ### Fixes - `[jest-leak-detector]` Make leak-detector more aggressive when running GC ([#14526](https://github.com/jestjs/jest/pull/14526)) diff --git a/e2e/__tests__/customTestSequencers.test.ts b/e2e/__tests__/customTestSequencers.test.ts index 40f0c43fc3cf..575822f9b8ac 100644 --- a/e2e/__tests__/customTestSequencers.test.ts +++ b/e2e/__tests__/customTestSequencers.test.ts @@ -79,3 +79,55 @@ test('run failed tests async', () => { .split('\n'); expect(sequence).toEqual(['./c.test.js', './d.test.js']); }); + +test('run tests based on even seed', () => { + const result = runJest( + dir, + [ + '-i', + '--config', + JSON.stringify({ + testSequencer: '/testSequencerWithSeed.js', + }), + '--seed=2', + ], + {}, + ); + expect(result.exitCode).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', + ]); +}); + +test('run tests based on odd seed', () => { + const result = runJest( + dir, + [ + '-i', + '--config', + JSON.stringify({ + testSequencer: '/testSequencerWithSeed.js', + }), + '--seed=1', + ], + {}, + ); + expect(result.exitCode).toBe(0); + const sequence = extractSummary(result.stderr) + .rest.replace(/PASS /g, '') + .split('\n'); + expect(sequence).toEqual([ + './e.test.js', + './d.test.js', + './c.test.js', + './b.test.js', + './a.test.js', + ]); +}); diff --git a/e2e/custom-test-sequencer/testSequencerWithSeed.js b/e2e/custom-test-sequencer/testSequencerWithSeed.js new file mode 100644 index 000000000000..1ed6db6cffeb --- /dev/null +++ b/e2e/custom-test-sequencer/testSequencerWithSeed.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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 { + constructor(_options) { + super(_options); + this.globalConfig = _options.globalConfig; + } + + sort(tests) { + const copyTests = Array.from(tests); + const seed = this.globalConfig.seed; + const sortedTests = copyTests.sort((testA, testB) => + testA.path > testB.path ? 1 : -1, + ); + + if (seed % 2 === 0) { + return sortedTests; + } else { + return sortedTests.reverse(); + } + } +} + +module.exports = CustomSequencer; diff --git a/packages/jest-core/src/runJest.ts b/packages/jest-core/src/runJest.ts index 8aae665e6d23..ce8e332163c3 100644 --- a/packages/jest-core/src/runJest.ts +++ b/packages/jest-core/src/runJest.ts @@ -159,7 +159,7 @@ export default async function runJest({ const Sequencer: typeof TestSequencer = await requireOrImportModule( globalConfig.testSequencer, ); - const sequencer = new Sequencer(); + const sequencer = new Sequencer({contexts, globalConfig}); let allTests: Array = []; if (changedFilesPromise && globalConfig.watch) { diff --git a/packages/jest-test-sequencer/src/index.ts b/packages/jest-test-sequencer/src/index.ts index 0a8239e8ce63..a4f3a3700c2a 100644 --- a/packages/jest-test-sequencer/src/index.ts +++ b/packages/jest-test-sequencer/src/index.ts @@ -10,11 +10,17 @@ import * as path from 'path'; import * as fs from 'graceful-fs'; import slash = require('slash'); import type {AggregatedResult, Test, TestContext} from '@jest/test-result'; +import type {Config} from '@jest/types'; import HasteMap from 'jest-haste-map'; const FAIL = 0; const SUCCESS = 1; +export type TestSequencerOptions = { + contexts: ReadonlyArray; + globalConfig: Config.GlobalConfig; +}; + type Cache = { [key: string]: | [testStatus: typeof FAIL | typeof SUCCESS, testDuration: number] @@ -46,6 +52,9 @@ type ShardPositionOptions = ShardOptions & { export default class TestSequencer { private readonly _cache = new Map(); + // eslint-disable-next-line @typescript-eslint/no-empty-function + constructor(_options?: TestSequencerOptions) {} + _getCachePath(testContext: TestContext): string { const {config} = testContext; const HasteMapClass = HasteMap.getStatic(config);