diff --git a/CHANGELOG.md b/CHANGELOG.md index c223e90f5725..987ba7c079a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[jest-reporters]` Add "skipped" and "todo" symbols to Github Actions Reporter ([#14309](https://github.com/jestjs/jest/pull/14309)) + ### Chore & Maintenance ### Performance diff --git a/packages/jest-reporters/src/GitHubActionsReporter.ts b/packages/jest-reporters/src/GitHubActionsReporter.ts index 7cbf65ce2734..596af49af554 100644 --- a/packages/jest-reporters/src/GitHubActionsReporter.ts +++ b/packages/jest-reporters/src/GitHubActionsReporter.ts @@ -10,6 +10,7 @@ import stripAnsi = require('strip-ansi'); import type { AggregatedResult, AssertionResult, + Status, Test, TestContext, TestResult, @@ -21,6 +22,7 @@ import { getTopFrame, separateMessageFromStack, } from 'jest-message-util'; +import {specialChars} from 'jest-util'; import BaseReporter from './BaseReporter'; type AnnotationOptions = { @@ -32,6 +34,7 @@ type AnnotationOptions = { }; const titleSeparator = ' \u203A '; +const ICONS = specialChars.ICONS; type PerformanceInfo = { end: number; @@ -42,7 +45,7 @@ type PerformanceInfo = { type ResultTreeLeaf = { name: string; - passed: boolean; + passed: Status; duration: number; children: Array; }; @@ -215,17 +218,15 @@ export default class GitHubActionsReporter extends BaseReporter { const branches: Array> = []; suiteResult.forEach(element => { if (element.ancestorTitles.length === 0) { - let passed = true; - if (element.status !== 'passed') { + if (element.status === 'failed') { root.passed = false; - passed = false; } const duration = element.duration || 1; root.children.push({ children: [], duration, name: element.title, - passed, + passed: element.status, }); } else { let alreadyInserted = false; @@ -263,21 +264,19 @@ export default class GitHubActionsReporter extends BaseReporter { }; 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') { + if (element.status === 'failed') { node.passed = false; - passed = false; } node.children.push({ children: [], duration, name: element.title, - passed, + passed: element.status, }); } else if ( this.arrayChild( @@ -354,10 +353,20 @@ export default class GitHubActionsReporter extends BaseReporter { } const spaces = ' '.repeat(numberSpaces); let resultSymbol; - if (resultTree.passed) { - resultSymbol = chalk.green('\u2713'); - } else { - resultSymbol = chalk.red('\u00D7'); + switch (resultTree.passed) { + case 'passed': + resultSymbol = chalk.green(ICONS.success); + break; + case 'failed': + resultSymbol = chalk.red(ICONS.failed); + break; + case 'todo': + resultSymbol = chalk.magenta(ICONS.todo); + break; + case 'pending': + case 'skipped': + resultSymbol = chalk.yellow(ICONS.pending); + break; } this.log( `${spaces + resultSymbol} ${resultTree.name} (${ diff --git a/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.ts b/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.ts index 700653b70c78..aafc0dde8de5 100644 --- a/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.ts +++ b/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.ts @@ -8,17 +8,25 @@ import type { AggregatedResult, AssertionResult, + Status, Test, TestCaseResult, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; +import {normalizeIcons} from '../../../../e2e/Utils'; import GitHubActionsReporter from '../GitHubActionsReporter'; afterEach(() => { jest.clearAllMocks(); }); +const oldLog = GitHubActionsReporter.prototype.log; + +GitHubActionsReporter.prototype.log = (message: string) => { + oldLog(normalizeIcons(message)); +}; + const mockedStderrWrite = jest .spyOn(process.stderr, 'write') .mockImplementation(() => true); @@ -174,7 +182,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: false, + passed: 'failed', }, ], name: '/', @@ -217,7 +225,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: true, + passed: 'passed', }, ], name: '/', @@ -262,7 +270,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: false, + passed: 'failed', }, ], name: 'Test describe', @@ -311,7 +319,68 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: true, + passed: 'passed', + }, + ], + name: 'Test describe', + passed: true, + }, + ], + name: '/', + passed: true, + performanceInfo: { + end: 30, + runtime: 20, + slow: false, + start: 10, + }, + }; + const gha = new GitHubActionsReporter({} as Config.GlobalConfig, { + silent: false, + }); + + const generated = gha['getResultTree'](testResults, '/', suitePerf); + + expect(mockedStderrWrite).not.toHaveBeenCalled(); + expect(generated).toEqual(expectedResults); + }); + + test('skipped single test and todo single test inside describe', () => { + const testResults = [ + { + ancestorTitles: ['Test describe'], + duration: 10, + status: 'skipped', + title: 'test', + }, + { + ancestorTitles: ['Test describe'], + duration: 14, + status: 'todo', + title: 'test2', + }, + ] as unknown as Array; + const suitePerf = { + end: 30, + runtime: 20, + slow: false, + start: 10, + }; + const expectedResults = { + children: [ + { + children: [ + { + children: [], + duration: 10, + name: 'test', + passed: 'skipped', + }, + { + children: [], + duration: 14, + name: 'test2', + passed: 'todo', }, ], name: 'Test describe', @@ -346,7 +415,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: false, + passed: 'failed' as Status, }, ], name: '/', @@ -374,7 +443,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: true, + passed: 'passed' as Status, }, ], name: '/', @@ -404,7 +473,7 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: false, + passed: 'failed' as Status, }, ], name: 'Test describe', @@ -438,7 +507,75 @@ describe('logs', () => { children: [], duration: 10, name: 'test', - passed: true, + passed: 'passed' as Status, + }, + ], + name: 'Test describe', + passed: true, + }, + ], + name: '/', + passed: true, + performanceInfo: { + end: 30, + runtime: 20, + slow: false, + start: 10, + }, + }; + const gha = new GitHubActionsReporter({} as Config.GlobalConfig, { + silent: false, + }); + + gha['printResultTree'](generatedTree); + + expect(mockedStderrWrite.mock.calls).toMatchSnapshot(); + }); + + test('todo single test inside describe', () => { + const generatedTree = { + children: [ + { + children: [ + { + children: [], + duration: 10, + name: 'test', + passed: 'todo' as Status, + }, + ], + name: 'Test describe', + passed: true, + }, + ], + name: '/', + passed: true, + performanceInfo: { + end: 30, + runtime: 20, + slow: false, + start: 10, + }, + }; + const gha = new GitHubActionsReporter({} as Config.GlobalConfig, { + silent: false, + }); + + gha['printResultTree'](generatedTree); + + expect(mockedStderrWrite.mock.calls).toMatchSnapshot(); + }); + + test('skipped single test inside describe', () => { + const generatedTree = { + children: [ + { + children: [ + { + children: [], + duration: 10, + name: 'test', + passed: 'skipped' as Status, }, ], name: 'Test describe', diff --git a/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.ts.snap b/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.ts.snap index 1010136fe994..296da56d83af 100644 --- a/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.ts.snap +++ b/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.ts.snap @@ -100,7 +100,7 @@ Array [ ", ], Array [ - " × test (10 ms) + " ✕ test (10 ms) ", ], ] @@ -113,7 +113,7 @@ Array [ ", ], Array [ - " × test (10 ms) + " ✕ test (10 ms) ", ], ] @@ -156,3 +156,45 @@ Array [ ], ] `; + +exports[`logs Result tree output skipped single test inside describe 1`] = ` +Array [ + Array [ + "::group::PASS / (20 ms) +", + ], + Array [ + " Test describe +", + ], + Array [ + " ○ test (10 ms) +", + ], + Array [ + "::endgroup:: +", + ], +] +`; + +exports[`logs Result tree output todo single test inside describe 1`] = ` +Array [ + Array [ + "::group::PASS / (20 ms) +", + ], + Array [ + " Test describe +", + ], + Array [ + " ✎ test (10 ms) +", + ], + Array [ + "::endgroup:: +", + ], +] +`;