From 92f6a29c8c3316b104329f5cd72dd4f29dff26d2 Mon Sep 17 00:00:00 2001 From: Matteo Dell'Acqua <82184604+MatteoH2O1999@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:29:41 +0100 Subject: [PATCH] Update with new spec --- packages/jest-core/src/TestScheduler.ts | 13 +- .../src/__tests__/TestScheduler.test.js | 206 +--------- .../src/GitHubActionsReporter.ts | 321 +++++++++++++++- .../src/GithubActionsLogsReporter.ts | 362 ------------------ .../GithubActionsLogsReporter.test.js | 2 +- packages/jest-reporters/src/index.ts | 1 - 6 files changed, 327 insertions(+), 578 deletions(-) delete mode 100644 packages/jest-reporters/src/GithubActionsLogsReporter.ts diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index 58a090adbdbc..ad96154c2fbb 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -12,7 +12,6 @@ import { CoverageReporter, DefaultReporter, GitHubActionsReporter, - GithubActionsLogsReporter, BaseReporter as JestReporter, NotifyReporter, Reporter, @@ -343,15 +342,9 @@ class TestScheduler { switch (reporter) { case 'default': summary = true; - if (verbose) { - this.addReporter(new VerboseReporter(this._globalConfig)); - } else { - GITHUB_ACTIONS - ? this.addReporter( - new GithubActionsLogsReporter(this._globalConfig), - ) - : this.addReporter(new DefaultReporter(this._globalConfig)); - } + verbose + ? this.addReporter(new VerboseReporter(this._globalConfig)) + : this.addReporter(new DefaultReporter(this._globalConfig)); break; case 'github-actions': GITHUB_ACTIONS && this.addReporter(new GitHubActionsReporter()); diff --git a/packages/jest-core/src/__tests__/TestScheduler.test.js b/packages/jest-core/src/__tests__/TestScheduler.test.js index 8a5adc441361..d50517f34104 100644 --- a/packages/jest-core/src/__tests__/TestScheduler.test.js +++ b/packages/jest-core/src/__tests__/TestScheduler.test.js @@ -10,7 +10,6 @@ import { CoverageReporter, DefaultReporter, GitHubActionsReporter, - GithubActionsLogsReporter, NotifyReporter, SummaryReporter, VerboseReporter, @@ -60,203 +59,13 @@ beforeEach(() => { spyShouldRunInBand.mockClear(); }); -describe('reporters with GITHUB_ACTIONS = true', () => { +describe('reporters', () => { const CustomReporter = require('/custom-reporter.js'); afterEach(() => { jest.clearAllMocks(); }); - test('works with default value', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: undefined, - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('does not enable any reporters, if empty list is passed', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(0); - }); - - test('sets up default reporters', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [['default', {}]], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('sets up verbose reporter', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [['default', {}]], - verbose: true, - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); - expect(VerboseReporter).toHaveBeenCalledTimes(1); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('sets up github actions reporter', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [ - ['default', {}], - ['github-actions', {}], - ], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(1); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('sets up notify reporter', async () => { - await createTestScheduler( - makeGlobalConfig({ - notify: true, - reporters: [['default', {}]], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(1); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('sets up coverage reporter', async () => { - await createTestScheduler( - makeGlobalConfig({ - collectCoverage: true, - reporters: [['default', {}]], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(1); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('allows enabling summary reporter separately', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [['summary', {}]], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - }); - - test('sets up custom reporter', async () => { - await createTestScheduler( - makeGlobalConfig({ - reporters: [ - ['default', {}], - ['/custom-reporter.js', {}], - ], - }), - {}, - {}, - ); - - expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(1); - expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); - expect(NotifyReporter).toHaveBeenCalledTimes(0); - expect(CoverageReporter).toHaveBeenCalledTimes(0); - expect(SummaryReporter).toHaveBeenCalledTimes(1); - expect(CustomReporter).toHaveBeenCalledTimes(1); - }); -}); - -describe('reporters with GITHUB_ACTIONS = false', () => { - const CustomReporter = require('/custom-reporter.js'); - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeAll(() => { - const ci = require('ci-info'); - ci.GITHUB_ACTIONS = false; - }); - - afterAll(() => { - const ci = require('ci-info'); - ci.GITHUB_ACTIONS = true; - }); - test('works with default value', async () => { await createTestScheduler( makeGlobalConfig({ @@ -267,7 +76,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -285,7 +93,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -303,7 +110,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -322,7 +128,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(1); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -330,7 +135,7 @@ describe('reporters with GITHUB_ACTIONS = false', () => { expect(SummaryReporter).toHaveBeenCalledTimes(1); }); - test('does not set up github actions reporter', async () => { + test('sets up github actions reporter', async () => { await createTestScheduler( makeGlobalConfig({ reporters: [ @@ -343,9 +148,8 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); - expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); + expect(GitHubActionsReporter).toHaveBeenCalledTimes(1); expect(NotifyReporter).toHaveBeenCalledTimes(0); expect(CoverageReporter).toHaveBeenCalledTimes(0); expect(SummaryReporter).toHaveBeenCalledTimes(1); @@ -362,7 +166,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(1); @@ -381,7 +184,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -399,7 +201,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(0); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); @@ -420,7 +221,6 @@ describe('reporters with GITHUB_ACTIONS = false', () => { ); expect(DefaultReporter).toHaveBeenCalledTimes(1); - expect(GithubActionsLogsReporter).toHaveBeenCalledTimes(0); expect(VerboseReporter).toHaveBeenCalledTimes(0); expect(GitHubActionsReporter).toHaveBeenCalledTimes(0); expect(NotifyReporter).toHaveBeenCalledTimes(0); diff --git a/packages/jest-reporters/src/GitHubActionsReporter.ts b/packages/jest-reporters/src/GitHubActionsReporter.ts index 7d244d728852..a9aa64e374e3 100644 --- a/packages/jest-reporters/src/GitHubActionsReporter.ts +++ b/packages/jest-reporters/src/GitHubActionsReporter.ts @@ -5,8 +5,15 @@ * LICENSE file in the root directory of this source tree. */ +import chalk = require('chalk'); import stripAnsi = require('strip-ansi'); -import type {Test, TestResult} from '@jest/test-result'; +import type { + AggregatedResult, + AssertionResult, + Test, + TestContext, + TestResult, +} from '@jest/test-result'; import type {Config} from '@jest/types'; import { formatPath, @@ -26,9 +33,50 @@ type AnnotationOptions = { const titleSeparator = ' \u203A '; +type performaceInfo = { + end: number; + runtime: number; + slow: boolean; + start: number; +}; + +type resultTreeLeaf = { + name: string; + passed: boolean; + duration: number; + children: Array; +}; + +type resultTreeNode = { + name: string; + passed: boolean; + children: Array; +}; + +type resultTree = { + children: Array; + name: string; + passed: boolean; + performanceInfo: performaceInfo; +}; + export default class GitHubActionsReporter extends BaseReporter { static readonly filename = __filename; + override onTestResult( + test: Test, + testResult: TestResult, + aggregatedResults: AggregatedResult, + ): void { + this.__printFullResult(test.context, testResult); + if (this.__isLastTestSuite(aggregatedResults)) { + this.log(''); + if (this.__printFailedTestLogs(test, aggregatedResults)) { + this.log(''); + } + } + } + onTestFileResult({context}: Test, {testResults}: TestResult): void { testResults.forEach(result => { const title = [...result.ancestorTitles, result.title].join( @@ -81,4 +129,275 @@ export default class GitHubActionsReporter extends BaseReporter { `\n::${type} file=${file},line=${line},title=${title}::${message}`, ); } + + __isLastTestSuite(results: AggregatedResult): boolean { + const passedTestSuites = results.numPassedTestSuites; + const failedTestSuites = results.numFailedTestSuites; + const totalTestSuites = results.numTotalTestSuites; + const computedTotal = passedTestSuites + failedTestSuites; + if (computedTotal < totalTestSuites) { + return false; + } else if (computedTotal === totalTestSuites) { + return true; + } else { + throw new Error( + `Sum(${computedTotal}) of passed (${passedTestSuites}) and failed (${failedTestSuites}) test suites is greater than the total number of test suites (${totalTestSuites}). Please report the bug at https://github.com/facebook/jest/issues`, + ); + } + } + + __printFullResult(context: TestContext, results: TestResult): void { + const rootDir = context.config.rootDir; + let testDir = results.testFilePath.replace(rootDir, ''); + testDir = testDir.slice(1, testDir.length); + const resultTree = this.__getResultTree( + results.testResults, + testDir, + results.perfStats, + ); + this.__printResultTree(resultTree); + } + + __arrayEqual(a1: Array, a2: Array): boolean { + if (a1.length !== a2.length) { + return false; + } + for (let index = 0; index < a1.length; index++) { + const element = a1[index]; + if (element !== a2[index]) { + return false; + } + } + return true; + } + + __arrayChild(a1: Array, a2: Array): boolean { + if (a1.length - a2.length !== 1) { + return false; + } + for (let index = 0; index < a2.length; index++) { + const element = a2[index]; + if (element !== a1[index]) { + return false; + } + } + return true; + } + + __getResultTree( + suiteResult: Array, + testPath: string, + suitePerf: performaceInfo, + ): resultTree { + const root: resultTree = { + children: [], + name: testPath, + passed: true, + performanceInfo: suitePerf, + }; + const branches: Array> = []; + suiteResult.forEach(element => { + if (element.ancestorTitles.length === 0) { + let passed = true; + if (element.status !== 'passed') { + root.passed = false; + passed = false; + } + if (!element.duration || isNaN(element.duration)) { + throw new Error('Expected duration to be a number, got NaN'); + } + root.children.push({ + children: [], + duration: Math.max(element.duration, 1), + name: element.title, + passed, + }); + } else { + let alreadyInserted = false; + for (let index = 0; index < branches.length; index++) { + if ( + this.__arrayEqual( + branches[index], + element.ancestorTitles.slice(0, 1), + ) + ) { + alreadyInserted = true; + break; + } + } + if (!alreadyInserted) { + branches.push(element.ancestorTitles.slice(0, 1)); + } + } + }); + branches.forEach(element => { + const newChild = this.__getResultChildren(suiteResult, element); + if (!newChild.passed) { + root.passed = false; + } + root.children.push(newChild); + }); + return root; + } + + __getResultChildren( + suiteResult: Array, + ancestors: Array, + ): resultTreeNode { + const node: resultTreeNode = { + children: [], + name: ancestors[ancestors.length - 1], + passed: true, + }; + const branches: Array> = []; + suiteResult.forEach(element => { + let passed = true; + let duration = element.duration; + if (!duration || isNaN(duration)) { + duration = 1; + } + if (this.__arrayEqual(element.ancestorTitles, ancestors)) { + if (element.status !== 'passed') { + node.passed = false; + passed = false; + } + node.children.push({ + children: [], + duration, + name: element.title, + passed, + }); + } else if ( + this.__arrayChild( + element.ancestorTitles.slice(0, ancestors.length + 1), + ancestors, + ) + ) { + let alreadyInserted = false; + for (let index = 0; index < branches.length; index++) { + if ( + this.__arrayEqual( + branches[index], + element.ancestorTitles.slice(0, ancestors.length + 1), + ) + ) { + alreadyInserted = true; + break; + } + } + if (!alreadyInserted) { + branches.push(element.ancestorTitles.slice(0, ancestors.length + 1)); + } + } + }); + branches.forEach(element => { + const newChild = this.__getResultChildren(suiteResult, element); + if (!newChild.passed) { + node.passed = false; + } + node.children.push(newChild); + }); + return node; + } + + __printResultTree(resultTree: resultTree): void { + let perfMs; + if (resultTree.performanceInfo.slow) { + perfMs = ` (${chalk.red.inverse( + `${resultTree.performanceInfo.runtime} ms`, + )})`; + } else { + perfMs = ` (${resultTree.performanceInfo.runtime} ms)`; + } + if (resultTree.passed) { + this.__startGroup( + `${chalk.bold.green.inverse('PASS')} ${resultTree.name}${perfMs}`, + ); + resultTree.children.forEach(child => { + this.__recursivePrintResultTree(child, true, 1); + }); + this.__endGroup(); + } else { + this.log( + ` ${chalk.bold.red.inverse('FAIL')} ${resultTree.name}${perfMs}`, + ); + resultTree.children.forEach(child => { + this.__recursivePrintResultTree(child, false, 1); + }); + } + } + + __recursivePrintResultTree( + resultTree: resultTreeNode | resultTreeLeaf, + alreadyGrouped: boolean, + depth: number, + ): void { + if (resultTree.children.length === 0) { + if (!('duration' in resultTree)) { + throw new Error('Expected a leaf. Got a node.'); + } + let numberSpaces = depth; + if (!alreadyGrouped) { + numberSpaces++; + } + const spaces = ' '.repeat(numberSpaces); + let resultSymbol; + if (resultTree.passed) { + resultSymbol = chalk.green('\u2713'); + } else { + resultSymbol = chalk.red('\u00D7'); + } + this.log( + `${spaces + resultSymbol} ${resultTree.name} (${ + resultTree.duration + } ms)`, + ); + } else { + if (resultTree.passed) { + if (alreadyGrouped) { + this.log(' '.repeat(depth) + resultTree.name); + resultTree.children.forEach(child => { + this.__recursivePrintResultTree(child, true, depth + 1); + }); + } else { + this.__startGroup(' '.repeat(depth) + resultTree.name); + resultTree.children.forEach(child => { + this.__recursivePrintResultTree(child, true, depth + 1); + }); + this.__endGroup(); + } + } else { + this.log(' '.repeat(depth + 1) + resultTree.name); + resultTree.children.forEach(child => { + this.__recursivePrintResultTree(child, false, depth + 1); + }); + } + } + } + + __printFailedTestLogs(context: Test, testResults: AggregatedResult): boolean { + const rootDir = context.context.config.rootDir; + const results = testResults.testResults; + let written = false; + results.forEach(result => { + let testDir = result.testFilePath; + testDir = testDir.replace(rootDir, ''); + testDir = testDir.slice(1, testDir.length); + if (result.failureMessage) { + written = true; + this.__startGroup(`Errors thrown in ${testDir}`); + this.log(result.failureMessage); + this.__endGroup(); + } + }); + return written; + } + + __startGroup(title: string): void { + this.log(`::group::${title}`); + } + + __endGroup(): void { + this.log('::endgroup::'); + } } diff --git a/packages/jest-reporters/src/GithubActionsLogsReporter.ts b/packages/jest-reporters/src/GithubActionsLogsReporter.ts deleted file mode 100644 index 60057c5681a8..000000000000 --- a/packages/jest-reporters/src/GithubActionsLogsReporter.ts +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import chalk = require('chalk'); -import type { - AggregatedResult, - AssertionResult, - Test, - TestContext, - TestResult, -} from '@jest/test-result'; -import type {Config} from '@jest/types'; -import DefaultReporter from './DefaultReporter'; -import type {ReporterOnStartOptions} from './types'; - -type performaceInfo = { - end: number; - runtime: number; - slow: boolean; - start: number; -}; - -type resultTreeLeaf = { - name: string; - passed: boolean; - duration: number; - children: Array; -}; - -type resultTreeNode = { - name: string; - passed: boolean; - children: Array; -}; - -type resultTree = { - children: Array; - name: string; - passed: boolean; - performanceInfo: performaceInfo; -}; - -export default class GithubActionsLogsReporter extends DefaultReporter { - // copied from https://github.com/MatteoH2O1999/github-actions-jest-reporter/blob/master/src/gha.reporter.js - override onTestResult( - test: Test, - testResult: TestResult, - aggregatedResults: AggregatedResult, - ): void { - this.__printFullResult(test.context, testResult); - if (this.__isLastTestSuite(aggregatedResults)) { - this.log(''); - if (this.__printFailedTestLogs(test, aggregatedResults)) { - this.log(''); - } - } - } - - // eslint-disable-next-line @typescript-eslint/no-empty-function - override onRunComplete(): void {} - - override onRunStart( - _aggregatedResults: AggregatedResult, - _options: ReporterOnStartOptions, - // eslint-disable-next-line @typescript-eslint/no-empty-function - ): void {} - - // eslint-disable-next-line @typescript-eslint/no-empty-function - override onTestStart(_test: Test): void {} - - override onTestCaseResult( - _test: Test, - _testCaseResult: AssertionResult, - // eslint-disable-next-line @typescript-eslint/no-empty-function - ): void {} - - override testFinished( - _config: Config.ProjectConfig, - _testResult: TestResult, - _aggregatedResults: AggregatedResult, - // eslint-disable-next-line @typescript-eslint/no-empty-function - ): void {} - - __isLastTestSuite(results: AggregatedResult): boolean { - const passedTestSuites = results.numPassedTestSuites; - const failedTestSuites = results.numFailedTestSuites; - const totalTestSuites = results.numTotalTestSuites; - const computedTotal = passedTestSuites + failedTestSuites; - if (computedTotal < totalTestSuites) { - return false; - } else if (computedTotal === totalTestSuites) { - return true; - } else { - throw new Error( - `Sum(${computedTotal}) of passed (${passedTestSuites}) and failed (${failedTestSuites}) test suites is greater than the total number of test suites (${totalTestSuites}). Please report the bug at https://github.com/facebook/jest/issues`, - ); - } - } - - __printFullResult(context: TestContext, results: TestResult): void { - const rootDir = context.config.rootDir; - let testDir = results.testFilePath.replace(rootDir, ''); - testDir = testDir.slice(1, testDir.length); - const resultTree = this.__getResultTree( - results.testResults, - testDir, - results.perfStats, - ); - this.__printResultTree(resultTree); - } - - __arrayEqual(a1: Array, a2: Array): boolean { - if (a1.length !== a2.length) { - return false; - } - for (let index = 0; index < a1.length; index++) { - const element = a1[index]; - if (element !== a2[index]) { - return false; - } - } - return true; - } - - __arrayChild(a1: Array, a2: Array): boolean { - if (a1.length - a2.length !== 1) { - return false; - } - for (let index = 0; index < a2.length; index++) { - const element = a2[index]; - if (element !== a1[index]) { - return false; - } - } - return true; - } - - __getResultTree( - suiteResult: Array, - testPath: string, - suitePerf: performaceInfo, - ): resultTree { - const root: resultTree = { - children: [], - name: testPath, - passed: true, - performanceInfo: suitePerf, - }; - const branches: Array> = []; - suiteResult.forEach(element => { - if (element.ancestorTitles.length === 0) { - let passed = true; - if (element.status === 'failed') { - root.passed = false; - passed = false; - } else if (element.status !== 'passed') { - throw new Error( - `Expected status to be 'failed' or 'passed', got ${element.status}`, - ); - } - if (!element.duration || isNaN(element.duration)) { - throw new Error('Expected duration to be a number, got NaN'); - } - root.children.push({ - children: [], - duration: Math.max(element.duration, 1), - name: element.title, - passed, - }); - } else { - let alreadyInserted = false; - for (let index = 0; index < branches.length; index++) { - if ( - this.__arrayEqual( - branches[index], - element.ancestorTitles.slice(0, 1), - ) - ) { - alreadyInserted = true; - break; - } - } - if (!alreadyInserted) { - branches.push(element.ancestorTitles.slice(0, 1)); - } - } - }); - branches.forEach(element => { - const newChild = this.__getResultChildren(suiteResult, element); - if (!newChild.passed) { - root.passed = false; - } - root.children.push(newChild); - }); - return root; - } - - __getResultChildren( - suiteResult: Array, - ancestors: Array, - ): resultTreeNode { - const node: resultTreeNode = { - children: [], - name: ancestors[ancestors.length - 1], - passed: true, - }; - const branches: Array> = []; - suiteResult.forEach(element => { - let passed = true; - let duration = element.duration; - if (!duration || isNaN(duration)) { - duration = 1; - } - if (this.__arrayEqual(element.ancestorTitles, ancestors)) { - if (element.status === 'failed') { - node.passed = false; - passed = false; - } - node.children.push({ - children: [], - duration, - name: element.title, - passed, - }); - } else if ( - this.__arrayChild( - element.ancestorTitles.slice(0, ancestors.length + 1), - ancestors, - ) - ) { - let alreadyInserted = false; - for (let index = 0; index < branches.length; index++) { - if ( - this.__arrayEqual( - branches[index], - element.ancestorTitles.slice(0, ancestors.length + 1), - ) - ) { - alreadyInserted = true; - break; - } - } - if (!alreadyInserted) { - branches.push(element.ancestorTitles.slice(0, ancestors.length + 1)); - } - } - }); - branches.forEach(element => { - const newChild = this.__getResultChildren(suiteResult, element); - if (!newChild.passed) { - node.passed = false; - } - node.children.push(newChild); - }); - return node; - } - - __printResultTree(resultTree: resultTree): void { - let perfMs; - if (resultTree.performanceInfo.slow) { - perfMs = ` (${chalk.red.inverse( - `${resultTree.performanceInfo.runtime} ms`, - )})`; - } else { - perfMs = ` (${resultTree.performanceInfo.runtime} ms)`; - } - if (resultTree.passed) { - this.__startGroup( - `${chalk.bold.green.inverse('PASS')} ${resultTree.name}${perfMs}`, - ); - resultTree.children.forEach(child => { - this.__recursivePrintResultTree(child, true, 1); - }); - this.__endGroup(); - } else { - this.log( - ` ${chalk.bold.red.inverse('FAIL')} ${resultTree.name}${perfMs}`, - ); - resultTree.children.forEach(child => { - this.__recursivePrintResultTree(child, false, 1); - }); - } - } - - __recursivePrintResultTree( - resultTree: resultTreeNode | resultTreeLeaf, - alreadyGrouped: boolean, - depth: number, - ): void { - if (resultTree.children.length === 0) { - if (!('duration' in resultTree)) { - throw new Error('Expected a leaf. Got a node.'); - } - let numberSpaces = depth; - if (!alreadyGrouped) { - numberSpaces++; - } - const spaces = ' '.repeat(numberSpaces); - let resultSymbol; - if (resultTree.passed) { - resultSymbol = chalk.green('\u2713'); - } else { - resultSymbol = chalk.red('\u00D7'); - } - this.log( - `${spaces + resultSymbol} ${resultTree.name} (${ - resultTree.duration - } ms)`, - ); - } else { - if (resultTree.passed) { - if (alreadyGrouped) { - this.log(' '.repeat(depth) + resultTree.name); - resultTree.children.forEach(child => { - this.__recursivePrintResultTree(child, true, depth + 1); - }); - } else { - this.__startGroup(' '.repeat(depth) + resultTree.name); - resultTree.children.forEach(child => { - this.__recursivePrintResultTree(child, true, depth + 1); - }); - this.__endGroup(); - } - } else { - this.log(' '.repeat(depth + 1) + resultTree.name); - resultTree.children.forEach(child => { - this.__recursivePrintResultTree(child, false, depth + 1); - }); - } - } - } - - __printFailedTestLogs(context: Test, testResults: AggregatedResult): boolean { - const rootDir = context.context.config.rootDir; - const results = testResults.testResults; - let written = false; - results.forEach(result => { - let testDir = result.testFilePath; - testDir = testDir.replace(rootDir, ''); - testDir = testDir.slice(1, testDir.length); - if (result.failureMessage) { - written = true; - this.__startGroup(`Errors thrown in ${testDir}`); - this.log(result.failureMessage); - this.__endGroup(); - } - }); - return written; - } - - __startGroup(title: string): void { - this.log(`::group::${title}`); - } - - __endGroup(): void { - this.log('::endgroup::'); - } -} diff --git a/packages/jest-reporters/src/__tests__/GithubActionsLogsReporter.test.js b/packages/jest-reporters/src/__tests__/GithubActionsLogsReporter.test.js index b91c0ead72c8..ca805d828662 100644 --- a/packages/jest-reporters/src/__tests__/GithubActionsLogsReporter.test.js +++ b/packages/jest-reporters/src/__tests__/GithubActionsLogsReporter.test.js @@ -11,7 +11,7 @@ import util from 'util'; import chalk from 'chalk'; import {beforeEach, describe, expect, jest, test} from '@jest/globals'; import BaseReporter from '../BaseReporter'; -import GhaReporter from '../GithubActionsLogsReporter'; +import GhaReporter from '../GitHubActionsReporter'; const xSymbol = '\u00D7'; const ySymbol = '\u2713'; diff --git a/packages/jest-reporters/src/index.ts b/packages/jest-reporters/src/index.ts index ef52469cfc6d..61f3cde17739 100644 --- a/packages/jest-reporters/src/index.ts +++ b/packages/jest-reporters/src/index.ts @@ -30,7 +30,6 @@ export {default as GitHubActionsReporter} from './GitHubActionsReporter'; export {default as NotifyReporter} from './NotifyReporter'; export {default as SummaryReporter} from './SummaryReporter'; export {default as VerboseReporter} from './VerboseReporter'; -export {default as GithubActionsLogsReporter} from './GithubActionsLogsReporter'; export type { Reporter, ReporterOnStartOptions,