Skip to content

Commit

Permalink
ISSUE-13112: Add onTestCaseStart hook
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitryMakhnev committed May 5, 2023
1 parent d85d542 commit 4f1cae7
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 24 deletions.
22 changes: 22 additions & 0 deletions e2e/__tests__/__snapshots__/customReportersOnCircus.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,25 @@ 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 results for skip tests 1`] = `
"onTestCaseResult: sample, status: pending, numExpectations: 0
onTestFileResult testCaseResult 0: sample, status: pending, numExpectations: 0"
`;

exports[`Custom Reporters Integration on jest-circus push test case start 1`] = `
"onTestCaseStart: test 1, mode: undefined, ancestorTitles: Custom Reporters
onTestCaseResult: test 1, status: passed
onTestCaseStart: test 2, mode: undefined, ancestorTitles: Custom Reporters
onTestCaseResult: test 2, status: passed"
`;

exports[`Custom Reporters Integration on jest-circus push test case start for todo tests 1`] = `
"onTestCaseStart: sample, mode: todo, ancestorTitles: Custom Reporters
onTestCaseResult: sample, status: todo"
`;

exports[`Custom Reporters Integration on jest-circus push test case start for skip tests 1`] = `
"onTestCaseStart: sample, mode: skip, ancestorTitles: Custom Reporters
onTestCaseResult: sample, status: pending"
`;
51 changes: 51 additions & 0 deletions e2e/__tests__/customReportersOnCircus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,55 @@ describe('Custom Reporters Integration on jest-circus', () => {

expect(stdout).toMatchSnapshot();
});

test('push test case results for skip tests', () => {
const {stdout} = runJest('custom-reporters', [
'--config',
JSON.stringify({
reporters: [
'default',
'<rootDir>/reporters/AssertionCountsReporter.js',
],
}),
'skip.test.js',
]);

expect(stdout).toMatchSnapshot();
});

test('push test case start', () => {
const {stdout} = runJest('custom-reporters', [
'--config',
JSON.stringify({
reporters: ['default', '<rootDir>/reporters/TestCaseStartReporter.js'],
}),
'just2Tests.test.js',
]);

expect(stdout).toMatchSnapshot();
});

test('push test case start for todo tests', () => {
const {stdout} = runJest('custom-reporters', [
'--config',
JSON.stringify({
reporters: ['default', '<rootDir>/reporters/TestCaseStartReporter.js'],
}),
'todo.test.js',
]);

expect(stdout).toMatchSnapshot();
});

test('push test case start for skip tests', () => {
const {stdout} = runJest('custom-reporters', [
'--config',
JSON.stringify({
reporters: ['default', '<rootDir>/reporters/TestCaseStartReporter.js'],
}),
'skip.test.js',
]);

expect(stdout).toMatchSnapshot();
});
});
19 changes: 19 additions & 0 deletions e2e/custom-reporters/__tests__/just2Tests.test.js
Original file line number Diff line number Diff line change
@@ -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();
});
});
13 changes: 13 additions & 0 deletions e2e/custom-reporters/__tests__/skip.test.js
Original file line number Diff line number Diff line change
@@ -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', () => {});
});
32 changes: 32 additions & 0 deletions e2e/custom-reporters/reporters/TestCaseStartReporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* 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('.')}`,
);
}
onTestCaseResult(test, testCaseResult) {
console.log(
`onTestCaseResult: ${testCaseResult.title}, ` +
`status: ${testCaseResult.status}`,
);
}
}

module.exports = TestCaseStartReporter;
12 changes: 11 additions & 1 deletion packages/jest-circus/src/testCaseReportHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@

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_start': {
const testCaseStartInfo = createTestCaseStartInfo(event.test);
sendMessageToJest('test-case-start', [testPath, testCaseStartInfo]);
break;
}
case 'test_skip':
case 'test_todo':
case 'test_done': {
const testResult = makeSingleTestResult(event.test);
Expand Down
76 changes: 56 additions & 20 deletions packages/jest-circus/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -464,6 +465,29 @@ export function invariant(
}
}

type TestDescription = {
ancestorTitles: Array<string>;
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 => {
Expand All @@ -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,
};
};
12 changes: 12 additions & 0 deletions packages/jest-core/src/ReporterDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -69,6 +70,17 @@ export default class ReporterDispatcher {
}
}

async onTestCaseStart(
test: Test,
testCaseStartInfo: Circus.TestCaseStartInfo,
): Promise<void> {
for (const reporter of this._reporters) {
if (reporter.onTestCaseStart) {
await reporter.onTestCaseStart(test, testCaseStartInfo);
}
}
}

async onTestCaseResult(
test: Test,
testCaseResult: TestCaseResult,
Expand Down
7 changes: 7 additions & 0 deletions packages/jest-core/src/TestScheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]) => {
Expand Down
6 changes: 5 additions & 1 deletion packages/jest-reporters/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +30,10 @@ export interface Reporter {
testResult: TestResult,
aggregatedResult: AggregatedResult,
) => Promise<void> | void;
readonly onTestCaseStart?: (
test: Test,
testCaseStartInfo: Circus.TestCaseStartInfo,
) => Promise<void> | void;
readonly onTestCaseResult?: (
test: Test,
testCaseResult: TestCaseResult,
Expand Down
3 changes: 2 additions & 1 deletion packages/jest-test-result/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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];
};

Expand Down
13 changes: 12 additions & 1 deletion packages/jest-types/src/Circus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ export type MatcherResults = {
};

export type TestStatus = 'skip' | 'done' | 'todo';

export type TestNamesPath = Array<TestName | BlockName>;

export type TestCaseStartInfo = {
ancestorTitles: Array<string>;
fullName: string;
mode: TestMode;
title: string;
startedAt?: number | null;
};

export type TestResult = {
duration?: number | null;
errors: Array<FormattedError>;
Expand All @@ -187,7 +198,7 @@ export type TestResult = {
location?: {column: number; line: number} | null;
numPassingAsserts: number;
retryReasons: Array<FormattedError>;
testPath: Array<TestName | BlockName>;
testPath: TestNamesPath;
};

export type RunResult = {
Expand Down

0 comments on commit 4f1cae7

Please sign in to comment.