Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ module.exports = {
'packages/expect/src/print.ts',
'packages/expect/src/toThrowMatchers.ts',
'packages/expect-utils/src/utils.ts',
'packages/jest-core/src/ReporterDispatcher.ts',
'packages/jest-core/src/TestScheduler.ts',
'packages/jest-core/src/collectHandles.ts',
'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts',
'packages/jest-haste-map/src/index.ts',
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- `[jest-mock]` Add support for auto-mocking async generator functions ([#11080](https://github.com/facebook/jest/pull/11080))
- `[jest-mock]` Add `contexts` member to mock functions ([#12601](https://github.com/facebook/jest/pull/12601))
- `[jest-reporters]` Add GitHub Actions reporter ([#11320](https://github.com/facebook/jest/pull/11320), [#12658](https://github.com/facebook/jest/pull/12658)
- `[jest-reporters]` Pass `reporterContext` to custom reporter constructors as third argument ([#12657](https://github.com/facebook/jest/pull/12657))
- `[jest-resolve]` [**BREAKING**] Add support for `package.json` `exports` ([#11961](https://github.com/facebook/jest/pull/11961), [#12373](https://github.com/facebook/jest/pull/12373))
- `[jest-resolve, jest-runtime]` Add support for `data:` URI import and mock ([#12392](https://github.com/facebook/jest/pull/12392))
- `[jest-resolve, jest-runtime]` Add support for async resolver ([#11540](https://github.com/facebook/jest/pull/11540))
Expand Down
57 changes: 23 additions & 34 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -822,73 +822,62 @@ When using multi-project runner, it's recommended to add a `displayName` for eac

Default: `undefined`

Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs.

If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name.

This will override default reporters:
Use this configuration option to add reporters to Jest. Optionally, a reporter can be configured by passing options object as a second argument of a tuple:

```json
{
"reporters": ["<rootDir>/my-custom-reporter.js"]
"reporters": [
"default",
["<rootDir>/custom-reporter.js", {"banana": "yes", "pineapple": "no"}]
]
}
```

This will use custom reporter in addition to default reporters that Jest provides:
:::tip

```json
{
"reporters": ["default", "<rootDir>/my-custom-reporter.js"]
}
```
Take a look at a list of [awesome reporters](https://github.com/jest-community/awesome-jest#reporters) from Awesome Jest.

Additionally, custom reporters can be configured by passing an `options` object as a second argument:
If custom reporters are specified, the default Jest reporters will be overridden. If you wish to keep them, `'default'` can be passed as a module name:

```json
{
"reporters": [
"default",
["<rootDir>/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}]
["jest-junit", {"outputName": "junit-report.xml", "suiteName": "some-name"}]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to convince Prettier to keep this as separate line.

]
}
```

Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments:
:::

Example reporter:
Custom reporter module must export a class that takes `globalConfig`, `reporterConfig` and `reporterContext` as constructor arguments and implements at least `onRunComplete()` method (for the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts)):

```js title="my-custom-reporter.js"
class MyCustomReporter {
constructor(globalConfig, options) {
```js title="custom-reporter.js"
class CustomReporter {
constructor(globalConfig, reporterOptions, reporterContext) {
this._globalConfig = globalConfig;
this._options = options;
this._options = reporterOptions;
this._context = reporterContext;
}

onRunComplete(contexts, results) {
onRunComplete(testContexts, results) {
console.log('Custom reporter output:');
console.log('GlobalConfig: ', this._globalConfig);
console.log('Options: ', this._options);
console.log('Context: ', this._context);
}
}

module.exports = MyCustomReporter;
// or export default MyCustomReporter;
```

Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods

```js
class MyCustomReporter {
// ...
// Optionally, reporters can force Jest to exit with non zero code by returning
// an `Error` from `getLastError()` method.
getLastError() {
if (this._shouldFail) {
return new Error('my-custom-reporter.js reported an error');
return new Error('Custom error reported!');
}
}
}
```

For the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts)
module.exports = CustomReporter;
```

### `resetMocks` \[boolean]

Expand Down
24 changes: 20 additions & 4 deletions e2e/__tests__/__snapshots__/customReporters.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ Object {
"called": true,
"path": false,
},
"options": Object {
"reporterContext": Object {
"firstRun": true,
"previousSuccess": true,
},
"reporterOptions": Object {
"christoph": "pojer",
"dmitrii": "abramov",
"hello": "world",
Expand Down Expand Up @@ -55,7 +59,11 @@ Object {
"called": true,
"path": false,
},
"options": Object {
"reporterContext": Object {
"firstRun": true,
"previousSuccess": true,
},
"reporterOptions": Object {
"christoph": "pojer",
"dmitrii": "abramov",
"hello": "world",
Expand Down Expand Up @@ -97,7 +105,11 @@ Object {
"called": true,
"path": false,
},
"options": Object {},
"reporterContext": Object {
"firstRun": true,
"previousSuccess": true,
},
"reporterOptions": Object {},
}
`;

Expand Down Expand Up @@ -146,7 +158,11 @@ exports[`Custom Reporters Integration valid array format for adding reporters 1`
"called": true,
"path": false
},
"options": {
"reporterContext": {
"firstRun": true,
"previousSuccess": true
},
"reporterOptions": {
"Aaron Abramov": "Awesome"
}
}"
Expand Down
2 changes: 1 addition & 1 deletion e2e/custom-reporters/reporters/IncompleteReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* This only implements one method onRunComplete which should be called
*/
class IncompleteReporter {
onRunComplete(contexts, results) {
onRunComplete(testContexts, results) {
console.log('onRunComplete is called');
console.log(`Passed Tests: ${results.numPassedTests}`);
console.log(`Failed Tests: ${results.numFailedTests}`);
Expand Down
14 changes: 8 additions & 6 deletions e2e/custom-reporters/reporters/TestReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
* to get the output.
*/
class TestReporter {
constructor(globalConfig, options) {
this._options = options;
constructor(globalConfig, reporterOptions, reporterContext) {
this._context = reporterContext;
this._options = reporterOptions;

/**
* statsCollected property
Expand All @@ -30,7 +31,8 @@ class TestReporter {
onRunStart: {},
onTestResult: {times: 0},
onTestStart: {},
options,
reporterContext,
reporterOptions,
};
}

Expand Down Expand Up @@ -66,7 +68,7 @@ class TestReporter {
onRunStart.options = typeof options;
}

onRunComplete(contexts, results) {
onRunComplete(testContexts, results) {
const onRunComplete = this._statsCollected.onRunComplete;

onRunComplete.called = true;
Expand All @@ -75,9 +77,9 @@ class TestReporter {
onRunComplete.numFailedTests = results.numFailedTests;
onRunComplete.numTotalTests = results.numTotalTests;

if (this._statsCollected.options.maxWorkers) {
if (this._statsCollected.reporterOptions.maxWorkers) {
// Since it's a different number on different machines.
this._statsCollected.options.maxWorkers = '<<REPLACED>>';
this._statsCollected.reporterOptions.maxWorkers = '<<REPLACED>>';
}
// The Final Call
process.stdout.write(JSON.stringify(this._statsCollected, null, 4));
Expand Down
13 changes: 6 additions & 7 deletions packages/jest-core/src/ReporterDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/

/* eslint-disable local/ban-types-eventually */

import type {Reporter, ReporterOnStartOptions} from '@jest/reporters';
import type {
AggregatedResult,
Test,
TestCaseResult,
TestContext,
TestResult,
} from '@jest/test-result';
import type {Context} from 'jest-runtime';
import type {ReporterConstructor} from './TestScheduler';

export default class ReporterDispatcher {
private _reporters: Array<Reporter>;
Expand All @@ -27,9 +26,9 @@ export default class ReporterDispatcher {
this._reporters.push(reporter);
}

unregister(ReporterClass: Function): void {
unregister(reporterConstructor: ReporterConstructor): void {
this._reporters = this._reporters.filter(
reporter => !(reporter instanceof ReporterClass),
reporter => !(reporter instanceof reporterConstructor),
);
}

Expand Down Expand Up @@ -82,12 +81,12 @@ export default class ReporterDispatcher {
}

async onRunComplete(
contexts: Set<Context>,
testContexts: Set<TestContext>,
results: AggregatedResult,
): Promise<void> {
for (const reporter of this._reporters) {
if (reporter.onRunComplete) {
await reporter.onRunComplete(contexts, results);
await reporter.onRunComplete(testContexts, results);
}
}
}
Expand Down
Loading