diff --git a/CHANGELOG.md b/CHANGELOG.md index dfa05c42e017..905504cccc1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - `[jest-core]` Pass project config to `globalSetup`/`globalTeardown` function as second argument ([#12440](https://github.com/facebook/jest/pull/12440)) - `[jest-core]` Stabilize test runners with event emitters ([#12641](https://github.com/facebook/jest/pull/12641)) - `[jest-core, jest-watcher]` [**BREAKING**] Move `TestWatcher` class to `jest-watcher` package ([#12652](https://github.com/facebook/jest/pull/12652)) +- `[jest-core]` Allow using Summary Reporter as stand-alone reporter ([#12687](https://github.com/facebook/jest/pull/12687)) - `[jest-environment-jsdom]` [**BREAKING**] Upgrade jsdom to 19.0.0 ([#12290](https://github.com/facebook/jest/pull/12290)) - `[jest-environment-jsdom]` [**BREAKING**] Add default `browser` condition to `exportConditions` for `jsdom` environment ([#11924](https://github.com/facebook/jest/pull/11924)) - `[jest-environment-jsdom]` [**BREAKING**] Pass global config to Jest environment constructor for `jsdom` environment ([#12461](https://github.com/facebook/jest/pull/12461)) diff --git a/docs/Configuration.md b/docs/Configuration.md index afb0a21b0481..11d60d5f4896 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -856,6 +856,16 @@ If included in the list, the built-in GitHub Actions Reporter will annotate chan } ``` +#### Summary Reporter + +Summary reporter prints out summary of all tests. It is a part of default reporter, hence it will be enabled if `'default'` is included in the list. For instance, you might want to use it as stand-alone reporter instead of the default one, or together with [Silent Reporter](https://github.com/rickhanlonii/jest-silent-reporter): + +```json +{ + "reporters": ["jest-silent-reporter", "summary"] +} +``` + #### Custom Reporters :::tip diff --git a/e2e/__tests__/customReporters.test.ts b/e2e/__tests__/customReporters.test.ts index 8ae42e478d2a..90aca200027b 100644 --- a/e2e/__tests__/customReporters.test.ts +++ b/e2e/__tests__/customReporters.test.ts @@ -142,7 +142,6 @@ describe('Custom Reporters Integration', () => { 'package.json': JSON.stringify({ jest: { reporters: ['default', '/reporter.js'], - testEnvironment: 'node', }, }), 'reporter.js': ` @@ -167,7 +166,6 @@ describe('Custom Reporters Integration', () => { 'package.json': JSON.stringify({ jest: { reporters: ['default', '/reporter.mjs'], - testEnvironment: 'node', }, }), 'reporter.mjs': ` diff --git a/jest.config.ci.mjs b/jest.config.ci.mjs index 8934ee58bf63..ffba96d753c6 100644 --- a/jest.config.ci.mjs +++ b/jest.config.ci.mjs @@ -20,5 +20,6 @@ export default { 'jest-silent-reporter', {showPaths: true, showWarnings: true, useDots: true}, ], + 'summary', ], }; diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index b7fd07b904c9..1f1fa8c65d13 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -433,7 +433,7 @@ const normalizeReporters = (options: Config.InitialOptionsWithRootDir) => { normalizedReporterConfig[0], ); - if (!['default', 'github-actions'].includes(reporterPath)) { + if (!['default', 'github-actions', 'summary'].includes(reporterPath)) { const reporter = Resolver.findNodeModule(reporterPath, { basedir: options.rootDir, }); diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index 0dcb80354e02..7c088051334b 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -55,8 +55,6 @@ type TestRunnerConstructor = new ( export type TestSchedulerContext = ReporterContext & TestRunnerContext; -type ReporterMap = Record>; - export async function createTestScheduler( globalConfig: Config.GlobalConfig, context: TestSchedulerContext, @@ -329,77 +327,59 @@ class TestScheduler { } async _setupReporters() { - const {collectCoverage, notify, reporters} = this._globalConfig; + const {collectCoverage: coverage, notify, verbose} = this._globalConfig; + const reporters = this._globalConfig.reporters || [['default', {}]]; + let summary = false; + + for (const [reporter, options] of reporters) { + switch (reporter) { + case 'default': + summary = true; + verbose + ? this.addReporter(new VerboseReporter(this._globalConfig)) + : this.addReporter(new DefaultReporter(this._globalConfig)); + break; + case 'github-actions': + GITHUB_ACTIONS && this.addReporter(new GitHubActionsReporter()); + break; + case 'summary': + summary = true; + break; + default: + await this._addCustomReporter(reporter, options); + } + } if (notify) { this.addReporter(new NotifyReporter(this._globalConfig, this._context)); } - if (!reporters) { - this._setupDefaultReporters(collectCoverage); - return; - } - - let reporterMap: ReporterMap = {}; - - reporters.forEach(reporter => { - reporterMap = Object.assign(reporterMap, { - [reporter[0]]: reporter[1], - }); - }); - - const reporterNames = Object.keys(reporterMap); - - const isDefault = reporterNames.includes('default'); - const isGitHubActions = - GITHUB_ACTIONS && reporterNames.includes('github-actions'); - - if (isDefault) { - this._setupDefaultReporters(collectCoverage); - } - - if (isGitHubActions) { - this.addReporter(new GitHubActionsReporter()); - } - - if (!isDefault && collectCoverage) { + if (coverage) { this.addReporter(new CoverageReporter(this._globalConfig, this._context)); } - if (reporterNames.length) { - await this._addCustomReporters(reporterMap); - } - } - - private _setupDefaultReporters(collectCoverage: boolean) { - this.addReporter( - this._globalConfig.verbose - ? new VerboseReporter(this._globalConfig) - : new DefaultReporter(this._globalConfig), - ); - - if (collectCoverage) { - this.addReporter(new CoverageReporter(this._globalConfig, this._context)); + if (summary) { + this.addReporter(new SummaryReporter(this._globalConfig)); } - - this.addReporter(new SummaryReporter(this._globalConfig)); } - private async _addCustomReporters(reporters: ReporterMap) { - for (const path in reporters) { - if (['default', 'github-actions'].includes(path)) continue; + private async _addCustomReporter( + reporter: string, + options: Record, + ) { + try { + const Reporter: ReporterConstructor = await requireOrImportModule( + reporter, + ); - try { - const Reporter: ReporterConstructor = await requireOrImportModule(path); - this.addReporter( - new Reporter(this._globalConfig, reporters[path], this._context), - ); - } catch (error: any) { - error.message = `An error occurred while adding the reporter at path "${chalk.bold( - path, - )}".\n${error.message}`; - throw error; - } + this.addReporter( + new Reporter(this._globalConfig, options, this._context), + ); + } catch (error: any) { + error.message = `An error occurred while adding the reporter at path "${chalk.bold( + reporter, + )}".\n${error instanceof Error ? error.message : ''}`; + throw error; } } diff --git a/packages/jest-core/src/__tests__/TestScheduler.test.js b/packages/jest-core/src/__tests__/TestScheduler.test.js index 005dcabfb300..66aba2262bb0 100644 --- a/packages/jest-core/src/__tests__/TestScheduler.test.js +++ b/packages/jest-core/src/__tests__/TestScheduler.test.js @@ -6,14 +6,29 @@ * */ -import {GitHubActionsReporter, SummaryReporter} from '@jest/reporters'; +import { + CoverageReporter, + DefaultReporter, + GitHubActionsReporter, + NotifyReporter, + SummaryReporter, + VerboseReporter, +} from '@jest/reporters'; import {makeGlobalConfig, makeProjectConfig} from '@jest/test-utils'; import {createTestScheduler} from '../TestScheduler'; import * as testSchedulerHelper from '../testSchedulerHelper'; -jest.mock('ci-info', () => ({GITHUB_ACTIONS: true})); - -jest.mock('@jest/reporters'); +jest + .mock('ci-info', () => ({GITHUB_ACTIONS: true})) + .mock('@jest/reporters') + .mock( + '/custom-reporter.js', + () => + jest.fn(() => ({ + onTestStart() {}, + })), + {virtual: true}, + ); const mockSerialRunner = { isSerial: true, @@ -38,68 +53,175 @@ beforeEach(() => { spyShouldRunInBand.mockClear(); }); -test('config for reporters supports `default`', async () => { - const undefinedReportersScheduler = await createTestScheduler( - makeGlobalConfig({ - reporters: undefined, - }), - {}, - {}, - ); - const numberOfReporters = - undefinedReportersScheduler._dispatcher._reporters.length; +describe('reporters', () => { + const CustomReporter = require('/custom-reporter.js'); - const defaultReportersScheduler = await createTestScheduler( - makeGlobalConfig({ - reporters: [['default', {}]], - }), - {}, - {}, - ); - expect(defaultReportersScheduler._dispatcher._reporters.length).toBe( - numberOfReporters, - ); + afterEach(() => { + jest.clearAllMocks(); + }); - const emptyReportersScheduler = await createTestScheduler( - makeGlobalConfig({ - reporters: [], - }), - {}, - {}, - ); - expect(emptyReportersScheduler._dispatcher._reporters.length).toBe(0); -}); + test('works with default value', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: undefined, + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); -test('config for reporters supports `github-actions`', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [], - }), - {}, - {}, - ); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); + test('does not enable any reporters, if empty list is passed', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(0); + }); - await createTestScheduler( - makeGlobalConfig({ - reporters: [['github-actions', {}]], - }), - {}, - {}, - ); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(1); - - await createTestScheduler( - makeGlobalConfig({ - reporters: [ - ['default', {}], - ['github-actions', {}], - ], - }), - {}, - {}, - ); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(2); + test('sets up default reporters', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up verbose reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['default', {}]], + verbose: true, + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(1); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up github actions reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [ + ['default', {}], + ['github-actions', {}], + ], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(1); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up notify reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + notify: true, + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(1); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up coverage reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + collectCoverage: true, + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(1); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('allows enabling summary reporter separately', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['summary', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up custom reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [ + ['default', {}], + ['/custom-reporter.js', {}], + ], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + expect(CustomReporter).toBeCalledTimes(1); + }); }); test('.addReporter() .removeReporter()', async () => {