From fb2dd2ba55363b524c373e5107c8fcbe857e63ba Mon Sep 17 00:00:00 2001 From: Rick Shanor Date: Tue, 6 Jan 2026 07:39:38 -0800 Subject: [PATCH] feat: Add listTestCaseTime flag to print test times next to test names - Add a new listTestCaseTime flag, that optionally prints test times next to test names. This flag defaults to false, for backward compatibility. - Update get-report.ts to use this flag when generating a report. - Update existing tests to set ReportOptions listTestCaseTime: true to verify above feature. - Update README with documentation about this new flag. Note this feature was needed for individual test times under pytest, since the xml is generated with all tests under one test suite. https://github.com/dorny/test-reporter/issues/260 --- README.md | 4 ++++ __tests__/__outputs__/jest-junit.md | 6 +++--- __tests__/jest-junit.test.ts | 9 +++++++-- src/main.ts | 14 +++++++++++++- src/report/get-report.ts | 5 ++++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bdced5c..7439921 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,10 @@ jobs: # none list-tests: 'all' + # Show execution time for individual test cases + # When enabled, test case execution times will be displayed in parentheses next to test names + list-test-case-time: 'false' + # Limits number of created annotations with error message and stack trace captured during test execution. # Must be less or equal to 50. max-annotations: '10' diff --git a/__tests__/__outputs__/jest-junit.md b/__tests__/__outputs__/jest-junit.md index 951256f..40b701c 100644 --- a/__tests__/__outputs__/jest-junit.md +++ b/__tests__/__outputs__/jest-junit.md @@ -11,9 +11,9 @@ ### ❌ __tests__\main.test.js ``` Test 1 - ✅ Passing test + ✅ Passing test (1ms) Test 1 › Test 1.1 - ❌ Failing test + ❌ Failing test (2ms) Error: expect(received).toBeTruthy() ❌ Exception in target unit Error: Some error @@ -23,7 +23,7 @@ Test 2 ``` ### ❌ __tests__\second.test.js ``` -❌ Timeout test +❌ Timeout test (4ms) : Timeout - Async callback was not invoked within the 1 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 1 ms timeout specified by jest.setTimeout.Error: ⚪ Skipped test ``` \ No newline at end of file diff --git a/__tests__/jest-junit.test.ts b/__tests__/jest-junit.test.ts index 912ebde..194856b 100644 --- a/__tests__/jest-junit.test.ts +++ b/__tests__/jest-junit.test.ts @@ -3,7 +3,7 @@ import * as path from 'path' import {JestJunitParser} from '../src/parsers/jest-junit/jest-junit-parser' import {ParseOptions} from '../src/test-parser' -import {DEFAULT_OPTIONS, getReport} from '../src/report/get-report' +import {DEFAULT_OPTIONS, getReport, ReportOptions} from '../src/report/get-report' import {normalizeFilePath} from '../src/utils/path-utils' describe('jest-junit tests', () => { @@ -55,7 +55,12 @@ describe('jest-junit tests', () => { const result = await parser.parse(filePath, fileContent) expect(result).toMatchSnapshot() - const report = getReport([result]) + const reportOpts: ReportOptions = { + ...DEFAULT_OPTIONS, + listTestCaseTime: true + } + + const report = getReport([result], reportOpts) fs.mkdirSync(path.dirname(outputPath), {recursive: true}) fs.writeFileSync(outputPath, report) }) diff --git a/src/main.ts b/src/main.ts index 9b24d38..842205b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -43,6 +43,7 @@ class TestReporter { readonly reporter = core.getInput('reporter', {required: true}) readonly listSuites = core.getInput('list-suites', {required: true}) as 'all' | 'failed' | 'none' readonly listTests = core.getInput('list-tests', {required: true}) as 'all' | 'failed' | 'none' + readonly listTestCaseTime = core.getInput('list-test-case-time', {required: false}) === 'true' readonly maxAnnotations = parseInt(core.getInput('max-annotations', {required: true})) readonly failOnError = core.getInput('fail-on-error', {required: true}) === 'true' readonly failOnEmpty = core.getInput('fail-on-empty', {required: true}) === 'true' @@ -174,7 +175,16 @@ class TestReporter { } } - const {listSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed} = this + const { + listSuites, + listTests, + listTestCaseTime, + onlySummary, + useActionsSummary, + badgeTitle, + reportTitle, + collapsed + } = this const passed = results.reduce((sum, tr) => sum + tr.passed, 0) const failed = results.reduce((sum, tr) => sum + tr.failed, 0) @@ -188,6 +198,7 @@ class TestReporter { { listSuites, listTests, + listTestCaseTime, baseUrl, onlySummary, useActionsSummary, @@ -219,6 +230,7 @@ class TestReporter { const summary = getReport(results, { listSuites, listTests, + listTestCaseTime, baseUrl, onlySummary, useActionsSummary, diff --git a/src/report/get-report.ts b/src/report/get-report.ts index 02b9d49..2827bd5 100644 --- a/src/report/get-report.ts +++ b/src/report/get-report.ts @@ -11,6 +11,7 @@ const MAX_ACTIONS_SUMMARY_LENGTH = 1048576 export interface ReportOptions { listSuites: 'all' | 'failed' | 'none' listTests: 'all' | 'failed' | 'none' + listTestCaseTime: boolean baseUrl: string onlySummary: boolean useActionsSummary: boolean @@ -22,6 +23,7 @@ export interface ReportOptions { export const DEFAULT_OPTIONS: ReportOptions = { listSuites: 'all', listTests: 'all', + listTestCaseTime: false, baseUrl: '', onlySummary: false, useActionsSummary: true, @@ -281,7 +283,8 @@ function getTestsReport(ts: TestSuiteResult, runIndex: number, suiteIndex: numbe continue } const result = getResultIcon(tc.result) - sections.push(`${space}${result} ${tc.name}`) + const time = options.listTestCaseTime && tc.time ? ` (${formatTime(tc.time)})` : '' + sections.push(`${space}${result} ${tc.name}${time}`) if (tc.error) { const lines = (tc.error.message ?? getFirstNonEmptyLine(tc.error.details)?.trim()) ?.split(/\r?\n/g)