diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d947982a870..281d8a568266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[jest-cli]` Include type definitions to generated config files ([#14078](https://github.com/facebook/jest/pull/14078)) - `[jest-snapshot]` Support arrays as property matchers ([#14025](https://github.com/facebook/jest/pull/14025)) +- `[jest-core, jest-circus, jest-reporter, jest-runner]` Added support for reporting about start individual test cases using jest-circus ([#14174](https://github.com/jestjs/jest/pull/14174)) ### Fixes diff --git a/e2e/__tests__/__snapshots__/customReportersOnCircus.test.ts.snap b/e2e/__tests__/__snapshots__/customReportersOnCircus.test.ts.snap index 0f5823c60467..e52b96c3dbb6 100644 --- a/e2e/__tests__/__snapshots__/customReportersOnCircus.test.ts.snap +++ b/e2e/__tests__/__snapshots__/customReportersOnCircus.test.ts.snap @@ -14,3 +14,12 @@ exports[`Custom Reporters Integration on jest-circus push test case results for "onTestCaseResult: sample, status: todo, numExpectations: 0 onTestFileResult testCaseResult 0: sample, status: todo, numExpectations: 0" `; + +exports[`Custom Reporters Integration on jest-circus push test case start 1`] = ` +"onTestCaseStart: test 1, mode: undefined, ancestorTitles: Custom Reporters +onTestCaseStart: test 2, mode: undefined, ancestorTitles: Custom Reporters" +`; + +exports[`Custom Reporters Integration on jest-circus doesn't push test case start for skip tests 1`] = `""`; + +exports[`Custom Reporters Integration on jest-circus doesn't push test case start for todo tests 1`] = `""`; diff --git a/e2e/__tests__/customReportersOnCircus.test.ts b/e2e/__tests__/customReportersOnCircus.test.ts index c6faf69467ae..2dbe0b248cae 100644 --- a/e2e/__tests__/customReportersOnCircus.test.ts +++ b/e2e/__tests__/customReportersOnCircus.test.ts @@ -54,4 +54,40 @@ describe('Custom Reporters Integration on jest-circus', () => { expect(stdout).toMatchSnapshot(); }); + + test('push test case start', () => { + const {stdout} = runJest('custom-reporters', [ + '--config', + JSON.stringify({ + reporters: ['default', '/reporters/TestCaseStartReporter.js'], + }), + 'just2Tests.test.js', + ]); + + expect(stdout).toMatchSnapshot(); + }); + + test("doesn't push test case start for todo tests", () => { + const {stdout} = runJest('custom-reporters', [ + '--config', + JSON.stringify({ + reporters: ['default', '/reporters/TestCaseStartReporter.js'], + }), + 'todo.test.js', + ]); + + expect(stdout).toMatchSnapshot(); + }); + + test("doesn't push test case start for skip tests", () => { + const {stdout} = runJest('custom-reporters', [ + '--config', + JSON.stringify({ + reporters: ['default', '/reporters/TestCaseStartReporter.js'], + }), + 'skip.test.js', + ]); + + expect(stdout).toMatchSnapshot(); + }); }); diff --git a/e2e/__tests__/testEnvironmentCircus.test.ts b/e2e/__tests__/testEnvironmentCircus.test.ts index aec50e7c2b10..1cfc2a8c8d49 100644 --- a/e2e/__tests__/testEnvironmentCircus.test.ts +++ b/e2e/__tests__/testEnvironmentCircus.test.ts @@ -23,6 +23,7 @@ it('calls testEnvironment handleTestEvent', () => { "run_start", "run_describe_start", "test_start: test name here", + "test_started: test name here", "hook_start", "hook_success: test name here", "hook_start", @@ -31,6 +32,7 @@ it('calls testEnvironment handleTestEvent', () => { "test_fn_success: test name here", "test_done: test name here", "test_start: second test name here", + "test_started: second test name here", "hook_start", "hook_success: second test name here", "hook_start", diff --git a/e2e/__tests__/testEnvironmentCircusAsync.test.ts b/e2e/__tests__/testEnvironmentCircusAsync.test.ts index 56692be0c21f..90a4311e95ee 100644 --- a/e2e/__tests__/testEnvironmentCircusAsync.test.ts +++ b/e2e/__tests__/testEnvironmentCircusAsync.test.ts @@ -36,6 +36,7 @@ it('calls asynchronous handleTestEvent in testEnvironment', () => { "run_describe_start", "run_describe_start", "test_start: passing test", + "test_started: passing test", "hook_start: beforeEach", "hook_success: beforeEach", "hook_start: beforeEach", @@ -46,6 +47,7 @@ it('calls asynchronous handleTestEvent in testEnvironment', () => { "hook_failure: afterEach", "test_done: passing test", "test_start: failing test", + "test_started: failing test", "hook_start: beforeEach", "hook_success: beforeEach", "hook_start: beforeEach", diff --git a/e2e/custom-reporters/__tests__/just2Tests.test.js b/e2e/custom-reporters/__tests__/just2Tests.test.js new file mode 100644 index 000000000000..ab8a346ac71f --- /dev/null +++ b/e2e/custom-reporters/__tests__/just2Tests.test.js @@ -0,0 +1,19 @@ +/** + * 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. + * + */ + +'use strict'; + +describe('Custom Reporters', () => { + it('test 1', () => { + expect(true).toBeTruthy(); + }); + + it('test 2', () => { + expect(true).toBeTruthy(); + }); +}); diff --git a/e2e/custom-reporters/__tests__/skip.test.js b/e2e/custom-reporters/__tests__/skip.test.js new file mode 100644 index 000000000000..a0a6088a8f20 --- /dev/null +++ b/e2e/custom-reporters/__tests__/skip.test.js @@ -0,0 +1,13 @@ +/** + * 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. + * + */ + +'use strict'; + +describe('Custom Reporters', () => { + it.skip('sample', () => {}); +}); diff --git a/e2e/custom-reporters/reporters/TestCaseStartReporter.js b/e2e/custom-reporters/reporters/TestCaseStartReporter.js new file mode 100644 index 000000000000..096957ddefae --- /dev/null +++ b/e2e/custom-reporters/reporters/TestCaseStartReporter.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +'use strict'; + +/** + * @class + * @implements {import('@jest/reporters').Reporter} + */ +class TestCaseStartReporter { + onTestCaseStart(test, testCaseStartInfo) { + const mode = + testCaseStartInfo.mode != null ? testCaseStartInfo.mode : 'undefined'; + console.log( + `onTestCaseStart: ${testCaseStartInfo.title}, ` + + `mode: ${mode}, ` + + `ancestorTitles: ${testCaseStartInfo.ancestorTitles.join('.')}`, + ); + } +} + +module.exports = TestCaseStartReporter; diff --git a/packages/jest-circus/src/__mocks__/testEventHandler.ts b/packages/jest-circus/src/__mocks__/testEventHandler.ts index cfdaef275c79..b3c20200ac64 100644 --- a/packages/jest-circus/src/__mocks__/testEventHandler.ts +++ b/packages/jest-circus/src/__mocks__/testEventHandler.ts @@ -20,6 +20,7 @@ const testEventHandler: Circus.EventHandler = (event, state) => { break; } case 'test_start': + case 'test_started': case 'test_retry': case 'test_done': { console.log(`${event.name}:`, event.test.name); diff --git a/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap b/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap index 8ae3e7150ae1..0f4a157cbb31 100644 --- a/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap +++ b/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap @@ -54,6 +54,7 @@ hook_start: beforeAll hook_success: beforeAll run_describe_start: child describe test_start: my test +test_started: my test hook_start: beforeEach > beforeEach hook_success: beforeEach @@ -132,24 +133,29 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one test_fn_start: one test_fn_success: one test_done: one test_start: two +test_started: two test_fn_start: two test_fn_success: two test_done: two run_describe_start: 2nd level describe test_start: 2nd level test +test_started: 2nd level test test_fn_start: 2nd level test test_fn_success: 2nd level test test_done: 2nd level test run_describe_start: 3rd level describe test_start: 3rd level test +test_started: 3rd level test test_fn_start: 3rd level test test_fn_success: 3rd level test test_done: 3rd level test test_start: 3rd level test#2 +test_started: 3rd level test#2 test_fn_start: 3rd level test#2 test_fn_success: 3rd level test#2 test_done: 3rd level test#2 @@ -162,6 +168,7 @@ hook_success: afterAll run_describe_finish: describe run_describe_start: 2nd describe test_start: 2nd describe test +test_started: 2nd describe test test_fn_start: 2nd describe test test_fn_success: 2nd describe test test_done: 2nd describe test diff --git a/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap b/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap index f44411f9ac58..531ff0e13f1f 100644 --- a/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap +++ b/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap @@ -11,6 +11,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one hook_start: beforeEach hook_success: beforeEach test_fn_start: one @@ -19,6 +20,7 @@ hook_start: afterEach hook_failure: afterEach test_done: one test_start: two +test_started: two hook_start: beforeEach hook_success: beforeEach test_fn_start: two @@ -41,6 +43,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describer test_start: One +test_started: One test_fn_start: One test_fn_success: One test_done: One @@ -62,6 +65,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one hook_start: beforeEach hook_success: beforeEach test_fn_start: one @@ -70,6 +74,7 @@ hook_start: afterEach hook_success: afterEach test_done: one test_start: two +test_started: two hook_start: beforeEach hook_success: beforeEach test_fn_start: two diff --git a/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap b/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap index 465511f31286..277e3c17e2f7 100644 --- a/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap +++ b/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap @@ -17,6 +17,7 @@ hook_start: beforeAll > beforeAll 1 hook_success: beforeAll test_start: test 1 +test_started: test 1 test_fn_start: test 1 > test 1 test_fn_success: test 1 @@ -26,11 +27,13 @@ hook_start: beforeAll > beforeAll 2 hook_success: beforeAll test_start: test 2 +test_started: test 2 test_fn_start: test 2 > test 2 test_fn_success: test 2 test_done: test 2 test_start: test 3 +test_started: test 3 test_fn_start: test 3 > test 3 test_fn_success: test 3 @@ -65,6 +68,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one hook_start: beforeEach > describe beforeEach hook_success: beforeEach @@ -72,6 +76,7 @@ test_fn_start: one test_fn_success: one test_done: one test_start: two +test_started: two hook_start: beforeEach > describe beforeEach hook_success: beforeEach @@ -80,6 +85,7 @@ test_fn_success: two test_done: two run_describe_start: 2nd level describe test_start: 2nd level test +test_started: 2nd level test hook_start: beforeEach > describe beforeEach hook_success: beforeEach @@ -91,6 +97,7 @@ test_fn_success: 2nd level test test_done: 2nd level test run_describe_start: 3rd level describe test_start: 3rd level test +test_started: 3rd level test hook_start: beforeEach > describe beforeEach hook_success: beforeEach @@ -101,6 +108,7 @@ test_fn_start: 3rd level test test_fn_success: 3rd level test test_done: 3rd level test test_start: 3rd level test#2 +test_started: 3rd level test#2 hook_start: beforeEach > describe beforeEach hook_success: beforeEach @@ -115,6 +123,7 @@ run_describe_finish: 2nd level describe run_describe_finish: describe run_describe_start: 2nd describe test_start: 2nd describe test +test_started: 2nd describe test hook_start: beforeEach > 2nd describe beforeEach that throws hook_failure: beforeEach @@ -140,6 +149,7 @@ run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe 1 run_describe_start: 2nd level describe test_start: test +test_started: test hook_start: beforeEach before each 1 hook_success: beforeEach diff --git a/packages/jest-circus/src/__tests__/__snapshots__/randomizeTest.test.ts.snap b/packages/jest-circus/src/__tests__/__snapshots__/randomizeTest.test.ts.snap index f44411f9ac58..531ff0e13f1f 100644 --- a/packages/jest-circus/src/__tests__/__snapshots__/randomizeTest.test.ts.snap +++ b/packages/jest-circus/src/__tests__/__snapshots__/randomizeTest.test.ts.snap @@ -11,6 +11,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one hook_start: beforeEach hook_success: beforeEach test_fn_start: one @@ -19,6 +20,7 @@ hook_start: afterEach hook_failure: afterEach test_done: one test_start: two +test_started: two hook_start: beforeEach hook_success: beforeEach test_fn_start: two @@ -41,6 +43,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describer test_start: One +test_started: One test_fn_start: One test_fn_success: One test_done: One @@ -62,6 +65,7 @@ run_start run_describe_start: ROOT_DESCRIBE_BLOCK run_describe_start: describe test_start: one +test_started: one hook_start: beforeEach hook_success: beforeEach test_fn_start: one @@ -70,6 +74,7 @@ hook_start: afterEach hook_success: afterEach test_done: one test_start: two +test_started: two hook_start: beforeEach hook_success: beforeEach test_fn_start: two diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index 7ccaf2d2e187..ec07b7d13dfd 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -166,6 +166,8 @@ const _runTest = async ( return; } + await dispatch({name: 'test_started', test}); + const {afterEach, beforeEach} = getEachHooksForTest(test); for (const hook of beforeEach) { diff --git a/packages/jest-circus/src/testCaseReportHandler.ts b/packages/jest-circus/src/testCaseReportHandler.ts index f7633db2622e..c1d987225518 100644 --- a/packages/jest-circus/src/testCaseReportHandler.ts +++ b/packages/jest-circus/src/testCaseReportHandler.ts @@ -7,12 +7,21 @@ import type {TestFileEvent} from '@jest/test-result'; import type {Circus} from '@jest/types'; -import {makeSingleTestResult, parseSingleTestResult} from './utils'; +import { + createTestCaseStartInfo, + makeSingleTestResult, + parseSingleTestResult, +} from './utils'; const testCaseReportHandler = (testPath: string, sendMessageToJest: TestFileEvent) => (event: Circus.Event): void => { switch (event.name) { + case 'test_started': { + const testCaseStartInfo = createTestCaseStartInfo(event.test); + sendMessageToJest('test-case-start', [testPath, testCaseStartInfo]); + break; + } case 'test_todo': case 'test_done': { const testResult = makeSingleTestResult(event.test); diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 13cf3b321e05..ff8f3bd55022 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -328,19 +328,25 @@ export const makeRunResult = ( unhandledErrors: unhandledErrors.map(_getError).map(getErrorStack), }); +const getTestNamesPath = (test: Circus.TestEntry): Circus.TestNamesPath => { + const titles = []; + let parent: Circus.TestEntry | Circus.DescribeBlock | undefined = test; + do { + titles.unshift(parent.name); + } while ((parent = parent.parent)); + + return titles; +}; + export const makeSingleTestResult = ( test: Circus.TestEntry, ): Circus.TestResult => { const {includeTestLocationInResult} = getState(); - const testPath = []; - let parent: Circus.TestEntry | Circus.DescribeBlock | undefined = test; const {status} = test; invariant(status, 'Status should be present after tests are run.'); - do { - testPath.unshift(parent.name); - } while ((parent = parent.parent)); + const testPath = getTestNamesPath(test); let location = null; if (includeTestLocationInResult) { @@ -402,14 +408,9 @@ const makeTestResults = ( // Return a string that identifies the test (concat of parent describe block // names + test title) export const getTestID = (test: Circus.TestEntry): string => { - const titles = []; - let parent: Circus.TestEntry | Circus.DescribeBlock | undefined = test; - do { - titles.unshift(parent.name); - } while ((parent = parent.parent)); - - titles.shift(); // remove TOP_DESCRIBE_BLOCK_NAME - return titles.join(' '); + const testNamesPath = getTestNamesPath(test); + testNamesPath.shift(); // remove TOP_DESCRIBE_BLOCK_NAME + return testNamesPath.join(' '); }; const _getError = ( @@ -464,6 +465,29 @@ export function invariant( } } +type TestDescription = { + ancestorTitles: Array; + fullName: string; + title: string; +}; + +const resolveTestCaseStartInfo = ( + testNamesPath: Circus.TestNamesPath, +): TestDescription => { + const ancestorTitles = testNamesPath.filter( + name => name !== ROOT_DESCRIBE_BLOCK_NAME, + ); + const fullName = ancestorTitles.join(' '); + const title = testNamesPath[testNamesPath.length - 1]; + // remove title + ancestorTitles.pop(); + return { + ancestorTitles, + fullName, + title, + }; +}; + export const parseSingleTestResult = ( testResult: Circus.TestResult, ): AssertionResult => { @@ -478,24 +502,36 @@ export const parseSingleTestResult = ( status = 'passed'; } - const ancestorTitles = testResult.testPath.filter( - name => name !== ROOT_DESCRIBE_BLOCK_NAME, + const {ancestorTitles, fullName, title} = resolveTestCaseStartInfo( + testResult.testPath, ); - const title = ancestorTitles.pop(); return { ancestorTitles, duration: testResult.duration, failureDetails: testResult.errorsDetailed, failureMessages: Array.from(testResult.errors), - fullName: title - ? ancestorTitles.concat(title).join(' ') - : ancestorTitles.join(' '), + fullName, invocations: testResult.invocations, location: testResult.location, numPassingAsserts: testResult.numPassingAsserts, retryReasons: Array.from(testResult.retryReasons), status, - title: testResult.testPath[testResult.testPath.length - 1], + title, + }; +}; + +export const createTestCaseStartInfo = ( + test: Circus.TestEntry, +): Circus.TestCaseStartInfo => { + const testPath = getTestNamesPath(test); + const {ancestorTitles, fullName, title} = resolveTestCaseStartInfo(testPath); + + return { + ancestorTitles, + fullName, + mode: test.mode, + startedAt: test.startedAt, + title, }; }; diff --git a/packages/jest-core/src/ReporterDispatcher.ts b/packages/jest-core/src/ReporterDispatcher.ts index b3683bbffb4f..2ab01253315d 100644 --- a/packages/jest-core/src/ReporterDispatcher.ts +++ b/packages/jest-core/src/ReporterDispatcher.ts @@ -13,6 +13,7 @@ import type { TestContext, TestResult, } from '@jest/test-result'; +import type {Circus} from '@jest/types'; import type {ReporterConstructor} from './TestScheduler'; export default class ReporterDispatcher { @@ -69,6 +70,17 @@ export default class ReporterDispatcher { } } + async onTestCaseStart( + test: Test, + testCaseStartInfo: Circus.TestCaseStartInfo, + ): Promise { + for (const reporter of this._reporters) { + if (reporter.onTestCaseStart) { + await reporter.onTestCaseStart(test, testCaseStartInfo); + } + } + } + async onTestCaseResult( test: Test, testCaseResult: TestCaseResult, diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index 00ad2b053768..2c53db5a41fc 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -257,6 +257,13 @@ class TestScheduler { testRunner.on('test-file-failure', ([test, error]) => onFailure(test, error), ), + testRunner.on( + 'test-case-start', + ([testPath, testCaseStartInfo]) => { + const test: Test = {context, path: testPath}; + this._dispatcher.onTestCaseStart(test, testCaseStartInfo); + }, + ), testRunner.on( 'test-case-result', ([testPath, testCaseResult]) => { diff --git a/packages/jest-reporters/src/types.ts b/packages/jest-reporters/src/types.ts index dbf0361c7c1f..3f285ba9f285 100644 --- a/packages/jest-reporters/src/types.ts +++ b/packages/jest-reporters/src/types.ts @@ -12,7 +12,7 @@ import type { TestContext, TestResult, } from '@jest/test-result'; -import type {Config} from '@jest/types'; +import type {Circus, Config} from '@jest/types'; export type ReporterOnStartOptions = { estimatedTime: number; @@ -30,6 +30,14 @@ export interface Reporter { testResult: TestResult, aggregatedResult: AggregatedResult, ) => Promise | void; + /** + * Called before running a spec (prior to `before` hooks) + * Not called for `skipped` and `todo` specs + */ + readonly onTestCaseStart?: ( + test: Test, + testCaseStartInfo: Circus.TestCaseStartInfo, + ) => Promise | void; readonly onTestCaseResult?: ( test: Test, testCaseResult: TestCaseResult, diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index 9bff7590784c..acab4be765ad 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -8,7 +8,7 @@ import type {V8Coverage} from 'collect-v8-coverage'; import type {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage'; import type {ConsoleBuffer} from '@jest/console'; -import type {Config, TestResult, TransformTypes} from '@jest/types'; +import type {Circus, Config, TestResult, TransformTypes} from '@jest/types'; import type {IHasteFS, IModuleMap} from 'jest-haste-map'; import type Resolver from 'jest-resolve'; @@ -197,6 +197,7 @@ export type TestEvents = { 'test-file-start': [Test]; 'test-file-success': [Test, TestResult]; 'test-file-failure': [Test, SerializableError]; + 'test-case-start': [string, Circus.TestCaseStartInfo]; 'test-case-result': [string, AssertionResult]; }; diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index f7d1c0374923..2b27c314f541 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -142,6 +142,10 @@ export type AsyncEvent = name: 'test_todo'; test: TestEntry; } + | { + name: 'test_started'; + test: TestEntry; + } | { // test failure is defined by presence of errors in `test.errors`, // `test_done` indicates that the test and all its hooks were run, @@ -178,6 +182,17 @@ export type MatcherResults = { }; export type TestStatus = 'skip' | 'done' | 'todo'; + +export type TestNamesPath = Array; + +export type TestCaseStartInfo = { + ancestorTitles: Array; + fullName: string; + mode: TestMode; + title: string; + startedAt?: number | null; +}; + export type TestResult = { duration?: number | null; errors: Array; @@ -187,7 +202,7 @@ export type TestResult = { location?: {column: number; line: number} | null; numPassingAsserts: number; retryReasons: Array; - testPath: Array; + testPath: TestNamesPath; }; export type RunResult = {