diff --git a/__tests__/jest-junit.test.ts b/__tests__/jest-junit.test.ts
index f4b8335..6fb6c64 100644
--- a/__tests__/jest-junit.test.ts
+++ b/__tests__/jest-junit.test.ts
@@ -207,4 +207,100 @@ describe('jest-junit tests', () => {
// Report should have the title as the first line
expect(report).toMatch(/^# My Custom Title\n/)
})
+
+ it('report can be collapsed when configured', async () => {
+ const fixturePath = path.join(__dirname, 'fixtures', 'jest-junit.xml')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JestJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+ const report = getReport([result], {
+ ...DEFAULT_OPTIONS,
+ collapsed: 'always'
+ })
+ // Report should include collapsible details
+ expect(report).toContain('Expand for details
')
+ expect(report).toContain(' ')
+ })
+
+ it('report is not collapsed when configured to never', async () => {
+ const fixturePath = path.join(__dirname, 'fixtures', 'jest-junit.xml')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JestJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+ const report = getReport([result], {
+ ...DEFAULT_OPTIONS,
+ collapsed: 'never'
+ })
+ // Report should not include collapsible details
+ expect(report).not.toContain('Expand for details
')
+ expect(report).not.toContain(' ')
+ })
+
+ it('report auto-collapses when all tests pass', async () => {
+ // Test with a fixture that has all passing tests (no failures)
+ const fixturePath = path.join(__dirname, 'fixtures', 'jest-junit-eslint.xml')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JestJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+
+ // Verify this fixture has no failures
+ expect(result.failed).toBe(0)
+
+ const report = getReport([result], {
+ ...DEFAULT_OPTIONS,
+ collapsed: 'auto'
+ })
+
+ // Should collapse when all tests pass
+ expect(report).toContain('Expand for details
')
+ expect(report).toContain(' ')
+ })
+
+ it('report does not auto-collapse when tests fail', async () => {
+ // Test with a fixture that has failing tests
+ const fixturePath = path.join(__dirname, 'fixtures', 'jest-junit.xml')
+ const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
+ const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
+
+ const opts: ParseOptions = {
+ parseErrors: true,
+ trackedFiles: []
+ }
+
+ const parser = new JestJunitParser(opts)
+ const result = await parser.parse(filePath, fileContent)
+
+ // Verify this fixture has failures
+ expect(result.failed).toBeGreaterThan(0)
+
+ const report = getReport([result], {
+ ...DEFAULT_OPTIONS,
+ collapsed: 'auto'
+ })
+
+ // Should not collapse when there are failures
+ expect(report).not.toContain('Expand for details
')
+ expect(report).not.toContain(' ')
+ })
})
diff --git a/action.yml b/action.yml
index 7777d56..c8dd56b 100644
--- a/action.yml
+++ b/action.yml
@@ -89,6 +89,14 @@ inputs:
description: Customize badge title
required: false
default: 'tests'
+ collapsed:
+ description: |
+ Controls whether test report details are collapsed or expanded. Supported options:
+ - auto: Collapse only if all tests pass (default behavior)
+ - always: Always collapse the report details
+ - never: Always expand the report details
+ required: false
+ default: 'auto'
token:
description: GitHub Access Token
required: false
diff --git a/dist/index.js b/dist/index.js
index d5914fe..cdd6319 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -309,6 +309,7 @@ class TestReporter {
useActionsSummary = core.getInput('use-actions-summary', { required: false }) === 'true';
badgeTitle = core.getInput('badge-title', { required: false });
reportTitle = core.getInput('report-title', { required: false });
+ collapsed = core.getInput('collapsed', { required: false });
token = core.getInput('token', { required: true });
octokit;
context = (0, github_utils_1.getCheckRunContext)();
@@ -322,6 +323,10 @@ class TestReporter {
core.setFailed(`Input parameter 'list-tests' has invalid value`);
return;
}
+ if (this.collapsed !== 'auto' && this.collapsed !== 'always' && this.collapsed !== 'never') {
+ core.setFailed(`Input parameter 'collapsed' has invalid value`);
+ return;
+ }
if (isNaN(this.maxAnnotations) || this.maxAnnotations < 0 || this.maxAnnotations > 50) {
core.setFailed(`Input parameter 'max-annotations' has invalid value`);
return;
@@ -401,7 +406,7 @@ class TestReporter {
throw error;
}
}
- const { listSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle } = this;
+ const { listSuites, listTests, 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);
const skipped = results.reduce((sum, tr) => sum + tr.skipped, 0);
@@ -415,7 +420,8 @@ class TestReporter {
onlySummary,
useActionsSummary,
badgeTitle,
- reportTitle
+ reportTitle,
+ collapsed
});
core.info('Summary content:');
core.info(summary);
@@ -443,7 +449,8 @@ class TestReporter {
onlySummary,
useActionsSummary,
badgeTitle,
- reportTitle
+ reportTitle,
+ collapsed
});
core.info('Creating annotations');
const annotations = (0, get_annotations_1.getAnnotations)(results, this.maxAnnotations);
@@ -1924,7 +1931,8 @@ exports.DEFAULT_OPTIONS = {
onlySummary: false,
useActionsSummary: true,
badgeTitle: 'tests',
- reportTitle: ''
+ reportTitle: '',
+ collapsed: 'auto'
};
function getReport(results, options = exports.DEFAULT_OPTIONS) {
core.info('Generating check run summary');
@@ -2031,7 +2039,9 @@ function getBadge(passed, failed, skipped, options) {
function getTestRunsReport(testRuns, options) {
const sections = [];
const totalFailed = testRuns.reduce((sum, tr) => sum + tr.failed, 0);
- if (totalFailed === 0) {
+ // Determine if report should be collapsed based on collapsed option
+ const shouldCollapse = options.collapsed === 'always' || (options.collapsed === 'auto' && totalFailed === 0);
+ if (shouldCollapse) {
sections.push(`Expand for details
`);
sections.push(` `);
}
@@ -2056,7 +2066,7 @@ function getTestRunsReport(testRuns, options) {
const suitesReports = testRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat();
sections.push(...suitesReports);
}
- if (totalFailed === 0) {
+ if (shouldCollapse) {
sections.push(` `);
}
return sections;
diff --git a/src/main.ts b/src/main.ts
index 57137ab..218377f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -49,6 +49,7 @@ class TestReporter {
readonly useActionsSummary = core.getInput('use-actions-summary', {required: false}) === 'true'
readonly badgeTitle = core.getInput('badge-title', {required: false})
readonly reportTitle = core.getInput('report-title', {required: false})
+ readonly collapsed = core.getInput('collapsed', {required: false}) as 'auto' | 'always' | 'never'
readonly token = core.getInput('token', {required: true})
readonly octokit: InstanceType
readonly context = getCheckRunContext()
@@ -66,6 +67,11 @@ class TestReporter {
return
}
+ if (this.collapsed !== 'auto' && this.collapsed !== 'always' && this.collapsed !== 'never') {
+ core.setFailed(`Input parameter 'collapsed' has invalid value`)
+ return
+ }
+
if (isNaN(this.maxAnnotations) || this.maxAnnotations < 0 || this.maxAnnotations > 50) {
core.setFailed(`Input parameter 'max-annotations' has invalid value`)
return
@@ -166,7 +172,7 @@ class TestReporter {
}
}
- const {listSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle} = this
+ const {listSuites, listTests, 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)
@@ -182,7 +188,8 @@ class TestReporter {
onlySummary,
useActionsSummary,
badgeTitle,
- reportTitle
+ reportTitle,
+ collapsed
})
core.info('Summary content:')
@@ -211,7 +218,8 @@ class TestReporter {
onlySummary,
useActionsSummary,
badgeTitle,
- reportTitle
+ reportTitle,
+ collapsed
})
core.info('Creating annotations')
diff --git a/src/report/get-report.ts b/src/report/get-report.ts
index 5168b7a..d893b0e 100644
--- a/src/report/get-report.ts
+++ b/src/report/get-report.ts
@@ -16,6 +16,7 @@ export interface ReportOptions {
useActionsSummary: boolean
badgeTitle: string
reportTitle: string
+ collapsed: 'auto' | 'always' | 'never'
}
export const DEFAULT_OPTIONS: ReportOptions = {
@@ -25,7 +26,8 @@ export const DEFAULT_OPTIONS: ReportOptions = {
onlySummary: false,
useActionsSummary: true,
badgeTitle: 'tests',
- reportTitle: ''
+ reportTitle: '',
+ collapsed: 'auto'
}
export function getReport(results: TestRunResult[], options: ReportOptions = DEFAULT_OPTIONS): string {
@@ -154,7 +156,11 @@ export function getBadge(passed: number, failed: number, skipped: number, option
function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): string[] {
const sections: string[] = []
const totalFailed = testRuns.reduce((sum, tr) => sum + tr.failed, 0)
- if (totalFailed === 0) {
+
+ // Determine if report should be collapsed based on collapsed option
+ const shouldCollapse = options.collapsed === 'always' || (options.collapsed === 'auto' && totalFailed === 0)
+
+ if (shouldCollapse) {
sections.push(`Expand for details
`)
sections.push(` `)
}
@@ -187,7 +193,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
sections.push(...suitesReports)
}
- if (totalFailed === 0) {
+ if (shouldCollapse) {
sections.push(` `)
}
return sections