Compare commits

...

10 Commits

Author SHA1 Message Date
Jozef Izso
7b7927aa7d test-reporter release v2.2.0 2025-11-13 21:35:26 +01:00
Jozef Izso
eeac280b8e Merge pull request #676 from dorny/dependabot/npm_and_yarn/js-yaml-4.1.1 2025-11-13 20:55:51 +01:00
dependabot[bot]
6939db53fb Bump js-yaml from 4.1.0 to 4.1.1
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-12 23:17:28 +00:00
Jozef Izso
b3812e0f5b Merge pull request #664 from pespinel/feature/collapsed-option 2025-11-12 18:03:25 +01:00
Jozef Izso
cd299561e7 tests: refactor input collapsed=auto to individual tests
Generated-by: Claude Sonnet 4.5
2025-11-12 14:42:24 +01:00
Jozef Izso
c7935221e6 feat: add validation for the collapsed input parameter
Generated-by: Claude Sonnet 4.5
2025-11-12 14:42:24 +01:00
Jozef Izso
5fb0582760 chore: run linter to fix code style issues 2025-11-12 14:42:24 +01:00
pespinel
7148297f02 test: fix linter and create tests 2025-11-12 14:42:23 +01:00
pespinel
828632acd0 feat: add collapsed option to control report visibility 2025-11-12 14:42:23 +01:00
Jozef Izso
4a41472ca4 Merge pull request #672 from dorny/bugfix/671-allow_hyphens_in_badge_image_names 2025-11-10 17:14:53 +01:00
8 changed files with 155 additions and 20 deletions

View File

@@ -1,5 +1,11 @@
# Changelog
## 2.2.0
* Feature: Add collapsed option to control report summary visibility https://github.com/dorny/test-reporter/pull/664
* Fix badge encoding for values including underscore and hyphens https://github.com/dorny/test-reporter/pull/672
* Fix missing `report-title` attribute in action definition https://github.com/dorny/test-reporter/pull/637
* Refactor variable names to fix shadowing issues https://github.com/dorny/test-reporter/pull/630
## 2.1.1
* Fix error when a TestMethod element does not have a className attribute in a trx file https://github.com/dorny/test-reporter/pull/623
* Add stack trace from trx to summary https://github.com/dorny/test-reporter/pull/615

View File

@@ -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('<details><summary>Expand for details</summary>')
expect(report).toContain('</details>')
})
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('<details><summary>Expand for details</summary>')
expect(report).not.toContain('</details>')
})
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('<details><summary>Expand for details</summary>')
expect(report).toContain('</details>')
})
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('<details><summary>Expand for details</summary>')
expect(report).not.toContain('</details>')
})
})

View File

@@ -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

22
dist/index.js generated vendored
View File

@@ -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(`<details><summary>Expand for details</summary>`);
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(`</details>`);
}
return sections;

13
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "test-reporter",
"version": "2.1.1",
"version": "2.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "test-reporter",
"version": "2.1.1",
"version": "2.2.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
@@ -37,7 +37,7 @@
"eslint-plugin-prettier": "^5.5.4",
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"js-yaml": "^4.1.0",
"js-yaml": "^4.1.1",
"prettier": "^3.6.2",
"ts-jest": "^29.4.5",
"typescript": "^5.9.3"
@@ -6346,10 +6346,11 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},

View File

@@ -1,6 +1,6 @@
{
"name": "test-reporter",
"version": "2.1.1",
"version": "2.2.0",
"private": true,
"description": "Presents test results from popular testing frameworks as Github check run",
"main": "lib/main.js",
@@ -64,7 +64,7 @@
"eslint-plugin-prettier": "^5.5.4",
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"js-yaml": "^4.1.0",
"js-yaml": "^4.1.1",
"prettier": "^3.6.2",
"ts-jest": "^29.4.5",
"typescript": "^5.9.3"

View File

@@ -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<typeof GitHub>
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')

View File

@@ -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(`<details><summary>Expand for details</summary>`)
sections.push(` `)
}
@@ -187,7 +193,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
sections.push(...suitesReports)
}
if (totalFailed === 0) {
if (shouldCollapse) {
sections.push(`</details>`)
}
return sections