mirror of
https://github.com/dorny/test-reporter.git
synced 2026-05-06 10:37:36 +02:00
Merge pull request #773 from dorny/codex/list-files-input
This commit is contained in:
commit
1ab1766274
7 changed files with 237 additions and 33 deletions
|
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
## 3.1.0
|
||||
* Feature: Add `list-files` input to control test report file listing https://github.com/dorny/test-reporter/pull/773
|
||||
|
||||
## 3.0.0
|
||||
* Feature: Use NodeJS 24 LTS as default runtime https://github.com/dorny/test-reporter/pull/738
|
||||
|
||||
|
|
|
|||
|
|
@ -184,6 +184,12 @@ jobs:
|
|||
# none
|
||||
list-tests: 'all'
|
||||
|
||||
# Limits which test result files are listed:
|
||||
# all
|
||||
# failed
|
||||
# none
|
||||
list-files: 'all'
|
||||
|
||||
# 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'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {getBadge, DEFAULT_OPTIONS, ReportOptions} from '../../src/report/get-report.js'
|
||||
import {DEFAULT_OPTIONS, getBadge, getReport, ReportOptions} from '../../src/report/get-report.js'
|
||||
import {TestCaseResult, TestGroupResult, TestRunResult, TestSuiteResult} from '../../src/test-results.js'
|
||||
|
||||
describe('getBadge', () => {
|
||||
describe('URI encoding with special characters', () => {
|
||||
|
|
@ -131,3 +132,147 @@ describe('getBadge', () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getReport', () => {
|
||||
// Helper function to create test results
|
||||
function createTestResult(path: string, passed: number, failed: number, skipped: number): TestRunResult {
|
||||
const tests: TestCaseResult[] = []
|
||||
for (let i = 0; i < passed; i++) {
|
||||
tests.push(new TestCaseResult(`passed-test-${i}`, 'success', 100))
|
||||
}
|
||||
for (let i = 0; i < failed; i++) {
|
||||
tests.push(
|
||||
new TestCaseResult(`failed-test-${i}`, 'failed', 100, {
|
||||
details: 'Test failed',
|
||||
message: 'Assertion error'
|
||||
})
|
||||
)
|
||||
}
|
||||
for (let i = 0; i < skipped; i++) {
|
||||
tests.push(new TestCaseResult(`skipped-test-${i}`, 'skipped', 0))
|
||||
}
|
||||
|
||||
const group = new TestGroupResult('test-group', tests)
|
||||
const suite = new TestSuiteResult('test-suite', [group])
|
||||
return new TestRunResult(path, [suite])
|
||||
}
|
||||
|
||||
describe('list-files parameter', () => {
|
||||
const results = [
|
||||
createTestResult('passing-file.spec.ts', 5, 0, 0),
|
||||
createTestResult('failing-file.spec.ts', 3, 2, 1),
|
||||
createTestResult('passing-with-skipped-file.spec.ts', 8, 0, 2)
|
||||
]
|
||||
|
||||
it('shows all files when list-files is "all"', () => {
|
||||
const report = getReport(results, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'all',
|
||||
listSuites: 'none',
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
expect(report).toContain('passing-file.spec.ts')
|
||||
expect(report).toContain('failing-file.spec.ts')
|
||||
expect(report).toContain('passing-with-skipped-file.spec.ts')
|
||||
})
|
||||
|
||||
it('shows only failed files when list-files is "failed"', () => {
|
||||
const report = getReport(results, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'failed',
|
||||
listSuites: 'none',
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
expect(report).not.toContain('passing-file.spec.ts')
|
||||
expect(report).toContain('failing-file.spec.ts')
|
||||
expect(report).not.toContain('passing-with-skipped-file.spec.ts')
|
||||
})
|
||||
|
||||
it('shows no file details when list-files is "none"', () => {
|
||||
const report = getReport(results, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'none',
|
||||
listSuites: 'none',
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).toContain('![')
|
||||
expect(report).not.toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
expect(report).not.toContain('passing-file.spec.ts')
|
||||
expect(report).not.toContain('failing-file.spec.ts')
|
||||
expect(report).not.toContain('passing-with-skipped-file.spec.ts')
|
||||
})
|
||||
|
||||
it('does not show an empty summary table when list-files is "none" and only-summary is enabled', () => {
|
||||
const report = getReport(results, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'none',
|
||||
listSuites: 'all',
|
||||
onlySummary: true,
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).toContain('![')
|
||||
expect(report).not.toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
expect(report).not.toContain('passing-file.spec.ts')
|
||||
expect(report).not.toContain('failing-file.spec.ts')
|
||||
expect(report).not.toContain('passing-with-skipped-file.spec.ts')
|
||||
})
|
||||
|
||||
it('works correctly with list-suites and list-tests when list-files is "failed"', () => {
|
||||
const report = getReport(results, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'failed',
|
||||
listSuites: 'all',
|
||||
listTests: 'all'
|
||||
})
|
||||
|
||||
expect(report).toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
expect(report).not.toContain('passing-file.spec.ts')
|
||||
expect(report).toContain('failing-file.spec.ts')
|
||||
expect(report).not.toContain('passing-with-skipped-file.spec.ts')
|
||||
// Should show suite details for the failed file
|
||||
expect(report).toContain('test-suite')
|
||||
})
|
||||
|
||||
it('filters correctly when all files pass and list-files is "failed"', () => {
|
||||
const allPassingResults = [
|
||||
createTestResult('passing-file-1.spec.ts', 5, 0, 0),
|
||||
createTestResult('passing-file-2.spec.ts', 8, 0, 2)
|
||||
]
|
||||
|
||||
const report = getReport(allPassingResults, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'failed',
|
||||
listSuites: 'all',
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).not.toContain('passing-file-1.spec.ts')
|
||||
expect(report).not.toContain('passing-file-2.spec.ts')
|
||||
expect(report).toContain('![')
|
||||
expect(report).not.toContain('|Report|Passed|Failed|Skipped|Time|')
|
||||
})
|
||||
|
||||
it('filters correctly when all files fail and list-files is "failed"', () => {
|
||||
const allFailingResults = [
|
||||
createTestResult('failing-file-1.spec.ts', 0, 5, 0),
|
||||
createTestResult('failing-file-2.spec.ts', 1, 2, 1)
|
||||
]
|
||||
|
||||
const report = getReport(allFailingResults, {
|
||||
...DEFAULT_OPTIONS,
|
||||
listFiles: 'failed',
|
||||
listSuites: 'all',
|
||||
listTests: 'none'
|
||||
})
|
||||
|
||||
expect(report).toContain('failing-file-1.spec.ts')
|
||||
expect(report).toContain('failing-file-2.spec.ts')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ inputs:
|
|||
- none
|
||||
required: false
|
||||
default: 'all'
|
||||
list-files:
|
||||
description: |
|
||||
Limits which test result files are listed. Supported options:
|
||||
- all
|
||||
- failed
|
||||
- none
|
||||
required: false
|
||||
default: 'all'
|
||||
max-annotations:
|
||||
description: |
|
||||
Limits number of created annotations with error message and stack trace captured during test execution.
|
||||
|
|
|
|||
22
dist/index.js
generated
vendored
22
dist/index.js
generated
vendored
|
|
@ -56954,6 +56954,7 @@ const DEFAULT_OPTIONS = {
|
|||
listSuites: 'all',
|
||||
listTests: 'all',
|
||||
slugPrefix: '',
|
||||
listFiles: 'all',
|
||||
baseUrl: '',
|
||||
onlySummary: false,
|
||||
useActionsSummary: true,
|
||||
|
|
@ -57074,8 +57075,13 @@ function getTestRunsReport(testRuns, options) {
|
|||
sections.push(`<details><summary>Expand for details</summary>`);
|
||||
sections.push(` `);
|
||||
}
|
||||
if (testRuns.length > 0 || options.onlySummary) {
|
||||
const tableData = testRuns
|
||||
// Filter test runs based on list-files option
|
||||
const filteredTestRuns = options.listFiles === 'failed'
|
||||
? testRuns.filter(tr => tr.result === 'failed')
|
||||
: options.listFiles === 'none'
|
||||
? []
|
||||
: testRuns;
|
||||
const tableData = filteredTestRuns
|
||||
.map((tr, originalIndex) => ({ tr, originalIndex }))
|
||||
.filter(({ tr }) => tr.passed > 0 || tr.failed > 0 || tr.skipped > 0)
|
||||
.map(({ tr, originalIndex }) => {
|
||||
|
|
@ -57088,11 +57094,12 @@ function getTestRunsReport(testRuns, options) {
|
|||
const skipped = tr.skipped > 0 ? `${tr.skipped} ${Icon.skip}` : '';
|
||||
return [nameLink, passed, failed, skipped, time];
|
||||
});
|
||||
if (tableData.length > 0) {
|
||||
const resultsTable = table(['Report', 'Passed', 'Failed', 'Skipped', 'Time'], [Align.Left, Align.Right, Align.Right, Align.Right, Align.Right], ...tableData);
|
||||
sections.push(resultsTable);
|
||||
}
|
||||
if (options.onlySummary === false) {
|
||||
const suitesReports = testRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat();
|
||||
const suitesReports = filteredTestRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat();
|
||||
sections.push(...suitesReports);
|
||||
}
|
||||
if (shouldCollapse) {
|
||||
|
|
@ -58949,6 +58956,7 @@ class TestReporter {
|
|||
reporter = getInput('reporter', { required: true });
|
||||
listSuites = getInput('list-suites', { required: true });
|
||||
listTests = getInput('list-tests', { required: true });
|
||||
listFiles = getInput('list-files', { required: true });
|
||||
maxAnnotations = parseInt(getInput('max-annotations', { required: true }));
|
||||
failOnError = getInput('fail-on-error', { required: true }) === 'true';
|
||||
failOnEmpty = getInput('fail-on-empty', { required: true }) === 'true';
|
||||
|
|
@ -58972,6 +58980,10 @@ class TestReporter {
|
|||
setFailed(`Input parameter 'list-tests' has invalid value`);
|
||||
return;
|
||||
}
|
||||
if (this.listFiles !== 'all' && this.listFiles !== 'failed' && this.listFiles !== 'none') {
|
||||
setFailed(`Input parameter 'list-files' has invalid value`);
|
||||
return;
|
||||
}
|
||||
if (this.collapsed !== 'auto' && this.collapsed !== 'always' && this.collapsed !== 'never') {
|
||||
setFailed(`Input parameter 'collapsed' has invalid value`);
|
||||
return;
|
||||
|
|
@ -59056,7 +59068,7 @@ class TestReporter {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
const { listSuites, listTests, slugPrefix, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed } = this;
|
||||
const { listSuites, listTests, slugPrefix, listFiles, 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);
|
||||
|
|
@ -59067,6 +59079,7 @@ class TestReporter {
|
|||
listSuites,
|
||||
listTests,
|
||||
slugPrefix,
|
||||
listFiles,
|
||||
baseUrl,
|
||||
onlySummary,
|
||||
useActionsSummary,
|
||||
|
|
@ -59096,6 +59109,7 @@ class TestReporter {
|
|||
listSuites,
|
||||
listTests,
|
||||
slugPrefix,
|
||||
listFiles,
|
||||
baseUrl,
|
||||
onlySummary,
|
||||
useActionsSummary,
|
||||
|
|
|
|||
20
src/main.ts
20
src/main.ts
|
|
@ -44,6 +44,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 listFiles = core.getInput('list-files', {required: true}) as 'all' | 'failed' | 'none'
|
||||
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'
|
||||
|
|
@ -71,6 +72,11 @@ class TestReporter {
|
|||
return
|
||||
}
|
||||
|
||||
if (this.listFiles !== 'all' && this.listFiles !== 'failed' && this.listFiles !== 'none') {
|
||||
core.setFailed(`Input parameter 'list-files' has invalid value`)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.collapsed !== 'auto' && this.collapsed !== 'always' && this.collapsed !== 'never') {
|
||||
core.setFailed(`Input parameter 'collapsed' has invalid value`)
|
||||
return
|
||||
|
|
@ -177,7 +183,17 @@ class TestReporter {
|
|||
}
|
||||
}
|
||||
|
||||
const {listSuites, listTests, slugPrefix, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed} = this
|
||||
const {
|
||||
listSuites,
|
||||
listTests,
|
||||
slugPrefix,
|
||||
listFiles,
|
||||
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)
|
||||
|
|
@ -192,6 +208,7 @@ class TestReporter {
|
|||
listSuites,
|
||||
listTests,
|
||||
slugPrefix,
|
||||
listFiles,
|
||||
baseUrl,
|
||||
onlySummary,
|
||||
useActionsSummary,
|
||||
|
|
@ -224,6 +241,7 @@ class TestReporter {
|
|||
listSuites,
|
||||
listTests,
|
||||
slugPrefix,
|
||||
listFiles,
|
||||
baseUrl,
|
||||
onlySummary,
|
||||
useActionsSummary,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export interface ReportOptions {
|
|||
listSuites: 'all' | 'failed' | 'none'
|
||||
listTests: 'all' | 'failed' | 'none'
|
||||
slugPrefix: string
|
||||
listFiles: 'all' | 'failed' | 'none'
|
||||
baseUrl: string
|
||||
onlySummary: boolean
|
||||
useActionsSummary: boolean
|
||||
|
|
@ -24,6 +25,7 @@ export const DEFAULT_OPTIONS: ReportOptions = {
|
|||
listSuites: 'all',
|
||||
listTests: 'all',
|
||||
slugPrefix: '',
|
||||
listFiles: 'all',
|
||||
baseUrl: '',
|
||||
onlySummary: false,
|
||||
useActionsSummary: true,
|
||||
|
|
@ -173,8 +175,15 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
|||
sections.push(` `)
|
||||
}
|
||||
|
||||
if (testRuns.length > 0 || options.onlySummary) {
|
||||
const tableData = testRuns
|
||||
// Filter test runs based on list-files option
|
||||
const filteredTestRuns =
|
||||
options.listFiles === 'failed'
|
||||
? testRuns.filter(tr => tr.result === 'failed')
|
||||
: options.listFiles === 'none'
|
||||
? []
|
||||
: testRuns
|
||||
|
||||
const tableData = filteredTestRuns
|
||||
.map((tr, originalIndex) => ({tr, originalIndex}))
|
||||
.filter(({tr}) => tr.passed > 0 || tr.failed > 0 || tr.skipped > 0)
|
||||
.map(({tr, originalIndex}) => {
|
||||
|
|
@ -188,6 +197,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
|||
return [nameLink, passed, failed, skipped, time]
|
||||
})
|
||||
|
||||
if (tableData.length > 0) {
|
||||
const resultsTable = table(
|
||||
['Report', 'Passed', 'Failed', 'Skipped', 'Time'],
|
||||
[Align.Left, Align.Right, Align.Right, Align.Right, Align.Right],
|
||||
|
|
@ -197,7 +207,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
|||
}
|
||||
|
||||
if (options.onlySummary === false) {
|
||||
const suitesReports = testRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat()
|
||||
const suitesReports = filteredTestRuns.map((tr, i) => getSuitesReport(tr, i, options)).flat()
|
||||
sections.push(...suitesReports)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue