From 4363cdb49999addab6f2b9d6402cfea2315c13e3 Mon Sep 17 00:00:00 2001 From: "jonbray.eth" Date: Wed, 12 Aug 2026 12:33:02 -0400 Subject: [PATCH] feat(reporter): add custom GitHub Actions summary title (#10891) Co-authored-by: Raul Macarie --- docs/guide/reporters.md | 16 +++++++++++ .../src/node/reporters/github-actions.ts | 19 +++++++++---- .../reporters/configuration-options.test-d.ts | 1 + .../e2e/test/reporters/github-actions.test.ts | 27 +++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/docs/guide/reporters.md b/docs/guide/reporters.md index a57593aba..11f61bc61 100644 --- a/docs/guide/reporters.md +++ b/docs/guide/reporters.md @@ -741,6 +741,22 @@ export default defineConfig({ }) ``` +The job summary title defaults to `Vitest Test Report`. You can use `jobSummary.title` to distinguish multiple Vitest invocations that append to the same job summary. + +```ts +export default defineConfig({ + test: { + reporters: [ + ['github-actions', { + jobSummary: { + title: 'My Test Report', + }, + }], + ], + }, +}) +``` + The flaky tests section of the summary includes permalink URLs that link test names directly to the relevant source lines on GitHub. These links are generated automatically using environment variables that GitHub Actions provides (`$GITHUB_REPOSITORY`, `$GITHUB_SHA`, and `$GITHUB_WORKSPACE`), so no configuration is needed in most cases. If you need to override these values — for example, when running in a container or a custom environment — you can customize them via the `fileLinks` option: diff --git a/packages/vitest/src/node/reporters/github-actions.ts b/packages/vitest/src/node/reporters/github-actions.ts index 198ef3b82..1ac71efa2 100644 --- a/packages/vitest/src/node/reporters/github-actions.ts +++ b/packages/vitest/src/node/reporters/github-actions.ts @@ -26,6 +26,12 @@ export interface GithubActionsReporterOptions { } interface JobSummaryOptions { + /** + * Title of the summary. + * + * @default 'Vitest Test Report' + */ + title: string /** * Whether to generate the summary. * @@ -73,6 +79,7 @@ const defaultOptions: ResolvedOptions = { onWritePath: defaultOnWritePath, displayAnnotations: true, jobSummary: { + title: 'Vitest Test Report', enabled: true, outputPath: process.env.GITHUB_STEP_SUMMARY, fileLinks: { @@ -174,7 +181,11 @@ export class GithubActionsReporter implements Reporter { } if (this.options.jobSummary.enabled === true && this.options.jobSummary.outputPath) { - const summary = renderSummary(collectSummaryData(testModules), this.options.jobSummary.fileLinks) + const summary = renderSummary( + collectSummaryData(testModules), + this.options.jobSummary.title, + this.options.jobSummary.fileLinks, + ) try { writeFileSync( @@ -435,12 +446,10 @@ function renderStats({ fileStats, testsStats }: SummaryData): string { return output } -const SUMMARY_HEADER = '## Vitest Test Report\n' - -function renderSummary(summaryData: SummaryData, fileLinks?: JobSummaryOptions['fileLinks']): string { +function renderSummary(summaryData: SummaryData, title: string = defaultOptions.jobSummary.title!, fileLinks?: JobSummaryOptions['fileLinks']): string { const fileLinkCreator = createGitHubFileLinkCreator(fileLinks) - let summary = `${SUMMARY_HEADER}${renderStats(summaryData)}` + let summary = `## ${title}\n${renderStats(summaryData)}` if (summaryData.flakyTests.length > 0) { summary += '\n### Flaky Tests\n\nThese tests passed only after one or more retries, indicating potential instability.\n' diff --git a/test/e2e/test/reporters/configuration-options.test-d.ts b/test/e2e/test/reporters/configuration-options.test-d.ts index a336b4abb..7b4e5dbe6 100644 --- a/test/e2e/test/reporters/configuration-options.test-d.ts +++ b/test/e2e/test/reporters/configuration-options.test-d.ts @@ -41,6 +41,7 @@ test('reporters, multiple', () => { test('reporters, with options', () => { assertType({ reporters: [ + ['github-actions', { jobSummary: { title: 'Custom Test Report' } }], ['json', { outputFile: 'test.json' }], ['junit', { classname: 'something', suiteName: 'Suite name', outputFile: 'test.json' }], ['vitest-sonar-reporter', { outputFile: 'report.xml' }], diff --git a/test/e2e/test/reporters/github-actions.test.ts b/test/e2e/test/reporters/github-actions.test.ts index 8d94bcd6b..e27574261 100644 --- a/test/e2e/test/reporters/github-actions.test.ts +++ b/test/e2e/test/reporters/github-actions.test.ts @@ -121,6 +121,33 @@ describe(GithubActionsReporter, () => { `) }) + it.for([ + { title: 'Custom Test Report', expectedTitle: 'Custom Test Report' }, + { title: undefined, expectedTitle: 'Vitest Test Report' }, + ] as const)('uses $expectedTitle when title is $title', async ({ title, expectedTitle }, { onTestFinished }) => { + const outputPath = resolve(tmpdir(), randomUUID()) + + onTestFinished(async () => { + await rm(outputPath).catch(() => { + console.error(`Could not remove ${outputPath}`) + }) + }) + + await runVitest({ + reporters: new GithubActionsReporter({ + jobSummary: { + title, + outputPath, + }, + }), + root: './fixtures/reporters/github-actions', + }) + + const summary = await readFile(outputPath, 'utf8') + + expect(summary.startsWith(`## ${expectedTitle}\n\n`)).toBe(true) + }) + it.for([{ enabled: false }, { outputPath: undefined }] as const)('does not write one when disabled or without `outputPath`', async (options) => { const outputPath = resolve(tmpdir(), randomUUID()) -- 2.51.2