diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index dfe916a629a1..211a9539cc3f 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -179,6 +179,7 @@ const eventHandler: Circus.EventHandler = ( case 'test_done': { event.test.duration = getTestDuration(event.test); event.test.status = 'done'; + state.parentProcess.emit('test_done', {test: event.test}); state.currentlyRunningTest = null; break; } diff --git a/packages/jest-core/src/ReporterDispatcher.ts b/packages/jest-core/src/ReporterDispatcher.ts index e9bd47dbb319..542bac06d810 100644 --- a/packages/jest-core/src/ReporterDispatcher.ts +++ b/packages/jest-core/src/ReporterDispatcher.ts @@ -9,6 +9,8 @@ import type {AggregatedResult, TestResult} from '@jest/test-result'; import type {Test} from 'jest-runner'; import type {Context} from 'jest-runtime'; import type {Reporter, ReporterOnStartOptions} from '@jest/reporters'; +import type {TestEntry} from '@jest/types/build/Circus'; + export default class ReporterDispatcher { private _reporters: Array; @@ -27,6 +29,13 @@ export default class ReporterDispatcher { ); } + async onIndividualTestResult(testEntry: TestEntry) { + for (const reporter of this._reporters) { + reporter.onIndividualTestResult && + (await reporter.onIndividualTestResult(testEntry)); + } + } + async onTestResult( test: Test, testResult: TestResult, diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index bf455a2e05c1..69fcea41223e 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -76,6 +76,9 @@ export default class TestScheduler { tests: Array, watcher: TestWatcher, ): Promise { + const onIndividualTestResult = this._dispatcher.onIndividualTestResult.bind( + this._dispatcher, + ); const onStart = this._dispatcher.onTestStart.bind(this._dispatcher); const timings: Array = []; const contexts = new Set(); @@ -199,6 +202,7 @@ export default class TestScheduler { onStart, onResult, onFailure, + onIndividualTestResult, { serial: runInBand || Boolean(testRunners[runner].isSerial), }, diff --git a/packages/jest-reporters/src/base_reporter.ts b/packages/jest-reporters/src/base_reporter.ts index e8ded25dd089..b1204b8d3a61 100644 --- a/packages/jest-reporters/src/base_reporter.ts +++ b/packages/jest-reporters/src/base_reporter.ts @@ -8,6 +8,7 @@ import type {AggregatedResult, TestResult} from '@jest/test-result'; import {preRunMessage} from 'jest-util'; import type {Context, Reporter, ReporterOnStartOptions, Test} from './types'; +import type {TestEntry} from '@jest/types/build/Circus'; const {remove: preRunMessageRemove} = preRunMessage; @@ -25,6 +26,10 @@ export default class BaseReporter implements Reporter { preRunMessageRemove(process.stderr); } + onIndividualTestResult( + _testEntry: TestEntry + ): void {} + onTestResult( _test?: Test, _testResult?: TestResult, diff --git a/packages/jest-runner/src/index.ts b/packages/jest-runner/src/index.ts index 0f1d069c6af0..705adbf443a6 100644 --- a/packages/jest-runner/src/index.ts +++ b/packages/jest-runner/src/index.ts @@ -61,16 +61,25 @@ class TestRunner { onResult: JestOnTestSuccess, onFailure: JestOnTestFailure, options: JestTestRunnerOptions, + onIndividualTestResult: any, ): Promise { return await (options.serial - ? this._createInBandTestRun(tests, watcher, onStart, onResult, onFailure) + ? this._createInBandTestRun( + tests, + watcher, + onStart, + onResult, + onFailure, + onIndividualTestResult, + ) : this._createParallelTestRun( tests, watcher, onStart, onResult, onFailure, - )); + onIndividualTestResult, + )); } private async _createInBandTestRun( @@ -79,7 +88,9 @@ class TestRunner { onStart: JestOnTestStart, onResult: JestOnTestSuccess, onFailure: JestOnTestFailure, + onIndividualTestResult: any, ) { + process.on('test_done', evt => onIndividualTestResult(evt.test)); process.env.JEST_WORKER_ID = '1'; const mutex = throat(1); return tests.reduce( @@ -113,6 +124,7 @@ class TestRunner { onStart: JestOnTestStart, onResult: JestOnTestSuccess, onFailure: JestOnTestFailure, + onIndividualTestResult: any, ) { const resolvers: Map = new Map(); for (const test of tests) { @@ -129,6 +141,7 @@ class TestRunner { forkOptions: {stdio: 'pipe'}, maxRetries: 3, numWorkers: this._globalConfig.maxWorkers, + onIndividualTestResult, setupArgs: [ { serializableResolvers: Array.from(resolvers.values()), diff --git a/packages/jest-worker/src/index.ts b/packages/jest-worker/src/index.ts index 5858f6c7f90e..de0e3070842f 100644 --- a/packages/jest-worker/src/index.ts +++ b/packages/jest-worker/src/index.ts @@ -79,6 +79,7 @@ export default class JestWorker { maxRetries: this._options.maxRetries || 3, numWorkers: this._options.numWorkers || Math.max(cpus().length - 1, 1), setupArgs: this._options.setupArgs || [], + onIndividualTestResult: options.onIndividualTestResult, }; if (this._options.WorkerPool) { diff --git a/packages/jest-worker/src/workers/processChild.ts b/packages/jest-worker/src/workers/processChild.ts index 96a298cf33e9..ed984fcf031c 100644 --- a/packages/jest-worker/src/workers/processChild.ts +++ b/packages/jest-worker/src/workers/processChild.ts @@ -58,6 +58,19 @@ const messageListener = (request: any) => { } }; process.on('message', messageListener); +process.on('test_done', ({test}) => { + process.send([ + 'test_done', + { + name: test.name, + duration: test.duration, + parent: test.parent && { + name: test.parent.name, + parent: test.parent.parent && test.parent.parent.name, + }, + }, + ]); +}); function reportSuccess(result: any) { if (!process || !process.send) { diff --git a/packages/jest-worker/src/workers/threadChild.ts b/packages/jest-worker/src/workers/threadChild.ts index fc9105ce99ce..883f1310e8fe 100644 --- a/packages/jest-worker/src/workers/threadChild.ts +++ b/packages/jest-worker/src/workers/threadChild.ts @@ -60,6 +60,19 @@ const messageListener = (request: any) => { } }; parentPort!.on('message', messageListener); +process.on('test_done', ({test}) => { + process.send([ + 'test_done', + { + name: test.name, + duration: test.duration, + parent: test.parent && { + name: test.parent.name, + parent: test.parent.parent && test.parent.parent.name, + }, + }, + ]); +}); function reportSuccess(result: any) { if (isMainThread) {