mirror of
https://github.com/dorny/test-reporter.git
synced 2026-02-02 11:25:21 -08:00
Compare commits
15 Commits
feature/up
...
release/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b7927aa7d | ||
|
|
eeac280b8e | ||
|
|
6939db53fb | ||
|
|
b3812e0f5b | ||
|
|
cd299561e7 | ||
|
|
c7935221e6 | ||
|
|
5fb0582760 | ||
|
|
7148297f02 | ||
|
|
828632acd0 | ||
|
|
4a41472ca4 | ||
|
|
22dc7b52f4 | ||
|
|
bed521d765 | ||
|
|
6079ce3d17 | ||
|
|
de77f76b7e | ||
|
|
c883ae9738 |
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# 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
|
## 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
|
* 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
|
* Add stack trace from trx to summary https://github.com/dorny/test-reporter/pull/615
|
||||||
|
|||||||
@@ -207,4 +207,100 @@ describe('jest-junit tests', () => {
|
|||||||
// Report should have the title as the first line
|
// Report should have the title as the first line
|
||||||
expect(report).toMatch(/^# My Custom Title\n/)
|
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>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
120
__tests__/report/get-report.test.ts
Normal file
120
__tests__/report/get-report.test.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import {getBadge, DEFAULT_OPTIONS, ReportOptions} from '../../src/report/get-report'
|
||||||
|
|
||||||
|
describe('getBadge', () => {
|
||||||
|
describe('URI encoding with special characters', () => {
|
||||||
|
it('generates correct URI with simple badge title', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'tests'
|
||||||
|
}
|
||||||
|
const badge = getBadge(5, 0, 1, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles badge title with single hyphen', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'unit-tests'
|
||||||
|
}
|
||||||
|
const badge = getBadge(3, 0, 0, options)
|
||||||
|
// The hyphen in the badge title should be encoded as --
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles badge title with multiple hyphens', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'integration-api-tests'
|
||||||
|
}
|
||||||
|
const badge = getBadge(10, 0, 0, options)
|
||||||
|
// All hyphens in the title should be encoded as --
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles badge title with multiple underscores', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'my_integration_test'
|
||||||
|
}
|
||||||
|
const badge = getBadge(10, 0, 0, options)
|
||||||
|
// All underscores in the title should be encoded as __
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles badge title with version format containing hyphen', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'MariaDb 12.0-ubi database tests'
|
||||||
|
}
|
||||||
|
const badge = getBadge(1, 0, 0, options)
|
||||||
|
// The hyphen in "12.0-ubi" should be encoded as --
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles badge title with dots and hyphens', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'v1.2.3-beta-test'
|
||||||
|
}
|
||||||
|
const badge = getBadge(4, 1, 0, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('preserves structural hyphens between label and message', () => {
|
||||||
|
const options: ReportOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
badgeTitle: 'test-suite'
|
||||||
|
}
|
||||||
|
const badge = getBadge(2, 3, 1, options)
|
||||||
|
// The URI should have literal hyphens separating title-message-color
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('generates test outcome as color name for imgshields', () => {
|
||||||
|
it('uses success color when all tests pass', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(5, 0, 0, options)
|
||||||
|
expect(badge).toContain('-success)')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses critical color when tests fail', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(5, 2, 0, options)
|
||||||
|
expect(badge).toContain('-critical)')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses yellow color when no tests found', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(0, 0, 0, options)
|
||||||
|
expect(badge).toContain('-yellow)')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('badge message composition', () => {
|
||||||
|
it('includes only passed count when no failures or skips', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(5, 0, 0, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes passed and failed counts', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(5, 2, 0, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes passed, failed and skipped counts', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(5, 2, 1, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses "none" message when no tests', () => {
|
||||||
|
const options: ReportOptions = {...DEFAULT_OPTIONS}
|
||||||
|
const badge = getBadge(0, 0, 0, options)
|
||||||
|
expect(badge).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
@@ -89,6 +89,14 @@ inputs:
|
|||||||
description: Customize badge title
|
description: Customize badge title
|
||||||
required: false
|
required: false
|
||||||
default: 'tests'
|
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:
|
token:
|
||||||
description: GitHub Access Token
|
description: GitHub Access Token
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
32
dist/index.js
generated
vendored
32
dist/index.js
generated
vendored
@@ -309,6 +309,7 @@ class TestReporter {
|
|||||||
useActionsSummary = core.getInput('use-actions-summary', { required: false }) === 'true';
|
useActionsSummary = core.getInput('use-actions-summary', { required: false }) === 'true';
|
||||||
badgeTitle = core.getInput('badge-title', { required: false });
|
badgeTitle = core.getInput('badge-title', { required: false });
|
||||||
reportTitle = core.getInput('report-title', { required: false });
|
reportTitle = core.getInput('report-title', { required: false });
|
||||||
|
collapsed = core.getInput('collapsed', { required: false });
|
||||||
token = core.getInput('token', { required: true });
|
token = core.getInput('token', { required: true });
|
||||||
octokit;
|
octokit;
|
||||||
context = (0, github_utils_1.getCheckRunContext)();
|
context = (0, github_utils_1.getCheckRunContext)();
|
||||||
@@ -322,6 +323,10 @@ class TestReporter {
|
|||||||
core.setFailed(`Input parameter 'list-tests' has invalid value`);
|
core.setFailed(`Input parameter 'list-tests' has invalid value`);
|
||||||
return;
|
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) {
|
if (isNaN(this.maxAnnotations) || this.maxAnnotations < 0 || this.maxAnnotations > 50) {
|
||||||
core.setFailed(`Input parameter 'max-annotations' has invalid value`);
|
core.setFailed(`Input parameter 'max-annotations' has invalid value`);
|
||||||
return;
|
return;
|
||||||
@@ -401,7 +406,7 @@ class TestReporter {
|
|||||||
throw error;
|
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 passed = results.reduce((sum, tr) => sum + tr.passed, 0);
|
||||||
const failed = results.reduce((sum, tr) => sum + tr.failed, 0);
|
const failed = results.reduce((sum, tr) => sum + tr.failed, 0);
|
||||||
const skipped = results.reduce((sum, tr) => sum + tr.skipped, 0);
|
const skipped = results.reduce((sum, tr) => sum + tr.skipped, 0);
|
||||||
@@ -415,7 +420,8 @@ class TestReporter {
|
|||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
badgeTitle,
|
badgeTitle,
|
||||||
reportTitle
|
reportTitle,
|
||||||
|
collapsed
|
||||||
});
|
});
|
||||||
core.info('Summary content:');
|
core.info('Summary content:');
|
||||||
core.info(summary);
|
core.info(summary);
|
||||||
@@ -443,7 +449,8 @@ class TestReporter {
|
|||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
badgeTitle,
|
badgeTitle,
|
||||||
reportTitle
|
reportTitle,
|
||||||
|
collapsed
|
||||||
});
|
});
|
||||||
core.info('Creating annotations');
|
core.info('Creating annotations');
|
||||||
const annotations = (0, get_annotations_1.getAnnotations)(results, this.maxAnnotations);
|
const annotations = (0, get_annotations_1.getAnnotations)(results, this.maxAnnotations);
|
||||||
@@ -1909,6 +1916,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.DEFAULT_OPTIONS = void 0;
|
exports.DEFAULT_OPTIONS = void 0;
|
||||||
exports.getReport = getReport;
|
exports.getReport = getReport;
|
||||||
|
exports.getBadge = getBadge;
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const markdown_utils_1 = __nccwpck_require__(5129);
|
const markdown_utils_1 = __nccwpck_require__(5129);
|
||||||
const node_utils_1 = __nccwpck_require__(5384);
|
const node_utils_1 = __nccwpck_require__(5384);
|
||||||
@@ -1923,7 +1931,8 @@ exports.DEFAULT_OPTIONS = {
|
|||||||
onlySummary: false,
|
onlySummary: false,
|
||||||
useActionsSummary: true,
|
useActionsSummary: true,
|
||||||
badgeTitle: 'tests',
|
badgeTitle: 'tests',
|
||||||
reportTitle: ''
|
reportTitle: '',
|
||||||
|
collapsed: 'auto'
|
||||||
};
|
};
|
||||||
function getReport(results, options = exports.DEFAULT_OPTIONS) {
|
function getReport(results, options = exports.DEFAULT_OPTIONS) {
|
||||||
core.info('Generating check run summary');
|
core.info('Generating check run summary');
|
||||||
@@ -2022,13 +2031,17 @@ function getBadge(passed, failed, skipped, options) {
|
|||||||
color = 'yellow';
|
color = 'yellow';
|
||||||
}
|
}
|
||||||
const hint = failed > 0 ? 'Tests failed' : 'Tests passed successfully';
|
const hint = failed > 0 ? 'Tests failed' : 'Tests passed successfully';
|
||||||
const uri = encodeURIComponent(`${options.badgeTitle}-${message}-${color}`);
|
const encodedBadgeTitle = encodeImgShieldsURIComponent(options.badgeTitle);
|
||||||
return ``;
|
const encodedMessage = encodeImgShieldsURIComponent(message);
|
||||||
|
const encodedColor = encodeImgShieldsURIComponent(color);
|
||||||
|
return ``;
|
||||||
}
|
}
|
||||||
function getTestRunsReport(testRuns, options) {
|
function getTestRunsReport(testRuns, options) {
|
||||||
const sections = [];
|
const sections = [];
|
||||||
const totalFailed = testRuns.reduce((sum, tr) => sum + tr.failed, 0);
|
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(`<details><summary>Expand for details</summary>`);
|
||||||
sections.push(` `);
|
sections.push(` `);
|
||||||
}
|
}
|
||||||
@@ -2053,7 +2066,7 @@ function getTestRunsReport(testRuns, options) {
|
|||||||
const suitesReports = testRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat();
|
const suitesReports = testRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat();
|
||||||
sections.push(...suitesReports);
|
sections.push(...suitesReports);
|
||||||
}
|
}
|
||||||
if (totalFailed === 0) {
|
if (shouldCollapse) {
|
||||||
sections.push(`</details>`);
|
sections.push(`</details>`);
|
||||||
}
|
}
|
||||||
return sections;
|
return sections;
|
||||||
@@ -2153,6 +2166,9 @@ function getResultIcon(result) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function encodeImgShieldsURIComponent(component) {
|
||||||
|
return encodeURIComponent(component).replace(/-/g, '--').replace(/_/g, '__');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|||||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "test-reporter",
|
"name": "test-reporter",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "test-reporter",
|
"name": "test-reporter",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^16.0.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.1",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"ts-jest": "^29.4.5",
|
"ts-jest": "^29.4.5",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
@@ -6346,10 +6346,11 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "test-reporter",
|
"name": "test-reporter",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Presents test results from popular testing frameworks as Github check run",
|
"description": "Presents test results from popular testing frameworks as Github check run",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^16.0.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.1",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"ts-jest": "^29.4.5",
|
"ts-jest": "^29.4.5",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
|
|||||||
14
src/main.ts
14
src/main.ts
@@ -49,6 +49,7 @@ class TestReporter {
|
|||||||
readonly useActionsSummary = core.getInput('use-actions-summary', {required: false}) === 'true'
|
readonly useActionsSummary = core.getInput('use-actions-summary', {required: false}) === 'true'
|
||||||
readonly badgeTitle = core.getInput('badge-title', {required: false})
|
readonly badgeTitle = core.getInput('badge-title', {required: false})
|
||||||
readonly reportTitle = core.getInput('report-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 token = core.getInput('token', {required: true})
|
||||||
readonly octokit: InstanceType<typeof GitHub>
|
readonly octokit: InstanceType<typeof GitHub>
|
||||||
readonly context = getCheckRunContext()
|
readonly context = getCheckRunContext()
|
||||||
@@ -66,6 +67,11 @@ class TestReporter {
|
|||||||
return
|
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) {
|
if (isNaN(this.maxAnnotations) || this.maxAnnotations < 0 || this.maxAnnotations > 50) {
|
||||||
core.setFailed(`Input parameter 'max-annotations' has invalid value`)
|
core.setFailed(`Input parameter 'max-annotations' has invalid value`)
|
||||||
return
|
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 passed = results.reduce((sum, tr) => sum + tr.passed, 0)
|
||||||
const failed = results.reduce((sum, tr) => sum + tr.failed, 0)
|
const failed = results.reduce((sum, tr) => sum + tr.failed, 0)
|
||||||
@@ -182,7 +188,8 @@ class TestReporter {
|
|||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
badgeTitle,
|
badgeTitle,
|
||||||
reportTitle
|
reportTitle,
|
||||||
|
collapsed
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info('Summary content:')
|
core.info('Summary content:')
|
||||||
@@ -211,7 +218,8 @@ class TestReporter {
|
|||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
badgeTitle,
|
badgeTitle,
|
||||||
reportTitle
|
reportTitle,
|
||||||
|
collapsed
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info('Creating annotations')
|
core.info('Creating annotations')
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface ReportOptions {
|
|||||||
useActionsSummary: boolean
|
useActionsSummary: boolean
|
||||||
badgeTitle: string
|
badgeTitle: string
|
||||||
reportTitle: string
|
reportTitle: string
|
||||||
|
collapsed: 'auto' | 'always' | 'never'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_OPTIONS: ReportOptions = {
|
export const DEFAULT_OPTIONS: ReportOptions = {
|
||||||
@@ -25,7 +26,8 @@ export const DEFAULT_OPTIONS: ReportOptions = {
|
|||||||
onlySummary: false,
|
onlySummary: false,
|
||||||
useActionsSummary: true,
|
useActionsSummary: true,
|
||||||
badgeTitle: 'tests',
|
badgeTitle: 'tests',
|
||||||
reportTitle: ''
|
reportTitle: '',
|
||||||
|
collapsed: 'auto'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getReport(results: TestRunResult[], options: ReportOptions = DEFAULT_OPTIONS): string {
|
export function getReport(results: TestRunResult[], options: ReportOptions = DEFAULT_OPTIONS): string {
|
||||||
@@ -125,7 +127,7 @@ function getReportBadge(results: TestRunResult[], options: ReportOptions): strin
|
|||||||
return getBadge(passed, failed, skipped, options)
|
return getBadge(passed, failed, skipped, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBadge(passed: number, failed: number, skipped: number, options: ReportOptions): string {
|
export function getBadge(passed: number, failed: number, skipped: number, options: ReportOptions): string {
|
||||||
const text = []
|
const text = []
|
||||||
if (passed > 0) {
|
if (passed > 0) {
|
||||||
text.push(`${passed} passed`)
|
text.push(`${passed} passed`)
|
||||||
@@ -145,14 +147,20 @@ function getBadge(passed: number, failed: number, skipped: number, options: Repo
|
|||||||
color = 'yellow'
|
color = 'yellow'
|
||||||
}
|
}
|
||||||
const hint = failed > 0 ? 'Tests failed' : 'Tests passed successfully'
|
const hint = failed > 0 ? 'Tests failed' : 'Tests passed successfully'
|
||||||
const uri = encodeURIComponent(`${options.badgeTitle}-${message}-${color}`)
|
const encodedBadgeTitle = encodeImgShieldsURIComponent(options.badgeTitle)
|
||||||
return ``
|
const encodedMessage = encodeImgShieldsURIComponent(message)
|
||||||
|
const encodedColor = encodeImgShieldsURIComponent(color)
|
||||||
|
return ``
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): string[] {
|
function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): string[] {
|
||||||
const sections: string[] = []
|
const sections: string[] = []
|
||||||
const totalFailed = testRuns.reduce((sum, tr) => sum + tr.failed, 0)
|
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(`<details><summary>Expand for details</summary>`)
|
||||||
sections.push(` `)
|
sections.push(` `)
|
||||||
}
|
}
|
||||||
@@ -185,7 +193,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
|||||||
sections.push(...suitesReports)
|
sections.push(...suitesReports)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalFailed === 0) {
|
if (shouldCollapse) {
|
||||||
sections.push(`</details>`)
|
sections.push(`</details>`)
|
||||||
}
|
}
|
||||||
return sections
|
return sections
|
||||||
@@ -305,3 +313,7 @@ function getResultIcon(result: TestExecutionResult): string {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeImgShieldsURIComponent(component: string): string {
|
||||||
|
return encodeURIComponent(component).replace(/-/g, '--').replace(/_/g, '__')
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user