Compare commits
74 commits
feature/16
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29672c52a8 |
||
|
|
373689ed6b |
||
|
|
bd45c7a559 |
||
|
|
49ad12d837 |
||
|
|
f610967bd1 |
||
|
|
1ab1766274 |
||
|
|
528bb1a4e4 |
||
|
|
e05b863b43 |
||
|
|
d7f15447e6 |
||
|
|
0af87079d7 |
||
|
|
45c8d46681 |
||
|
|
ba6f1d11e2 |
||
|
|
a48134be3e |
||
|
|
97ba6567fc |
||
|
|
093d99bd59 |
||
|
|
23735d3781 |
||
|
|
67804e5bc2 |
||
|
|
a5b9b5c6e7 |
||
|
|
c36971571b |
||
|
|
724024e7cf |
||
|
|
0caa66d75c |
||
|
|
fdce2624c0 | ||
|
|
ecf6e5c4c6 |
||
|
|
9c68bf6253 |
||
|
|
8ebd1f2035 |
||
|
|
195e976be5 |
||
|
|
a88cf9b158 |
||
|
|
4a3df5aacc |
||
|
|
db37d6d935 |
||
|
|
ab50174d2c |
||
|
|
ef55eb29a9 |
||
|
|
2d29da79de |
||
|
|
ce1640be7e |
||
|
|
08ceae2b23 |
||
|
|
fcff37b0cc |
||
|
|
63a06a304e |
||
|
|
1e586914f0 |
||
|
|
7b052bb685 | ||
|
|
18e03ad9cc |
||
|
|
bae76033c4 |
||
|
|
2dcf091ad5 |
||
|
|
a43b3a5f73 |
||
|
|
79216dc71a |
||
|
|
62c3873e1e |
||
|
|
bd74ca6c12 |
||
|
|
0c2b2aa1e0 |
||
|
|
f7eee307f2 |
||
|
|
ad0a96f767 |
||
|
|
543415ce39 |
||
|
|
f1284d056a |
||
|
|
3c1b53fe19 |
||
|
|
dc9521099d |
||
|
|
38e11d6bf5 |
||
|
|
9489e055fc |
||
|
|
df62474295 |
||
|
|
24ade537c8 |
||
|
|
574d6b100c |
||
|
|
ce9f9c4738 |
||
|
|
0d9f24e0e1 |
||
|
|
5e233102fe |
||
|
|
f8ae4deee6 |
||
|
|
540c28072b |
||
|
|
a03559fc1d |
||
|
|
eff5ed2ea7 |
||
|
|
a0ec52b6d3 | ||
|
|
d36ac2431e |
||
|
|
ff5a70913c |
||
|
|
3b635e8e7b |
||
|
|
a1dabb90f7 |
||
|
|
e8df218184 |
||
|
|
1908acedff |
||
|
|
08dfe2715b |
||
|
|
7f0723a953 |
||
|
|
3d76b34a45 |
25 changed files with 3470 additions and 1524 deletions
3
.github/workflows/check-dist.yml
vendored
3
.github/workflows/check-dist.yml
vendored
|
|
@ -16,6 +16,9 @@ on:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-dist:
|
check-dist:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
||||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
|
@ -8,6 +8,9 @@ on:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-test:
|
build-test:
|
||||||
name: Build & Test
|
name: Build & Test
|
||||||
|
|
|
||||||
3
.github/workflows/manual-run.yml
vendored
3
.github/workflows/manual-run.yml
vendored
|
|
@ -3,6 +3,9 @@ name: Manual run
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-dist:
|
check-dist:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
||||||
4
.github/workflows/test-report.yml
vendored
4
.github/workflows/test-report.yml
vendored
|
|
@ -6,6 +6,10 @@ on:
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
report:
|
report:
|
||||||
name: Workflow test
|
name: Workflow test
|
||||||
|
|
|
||||||
2
.nvmrc
2
.nvmrc
|
|
@ -1 +1 @@
|
||||||
v20
|
v24
|
||||||
|
|
|
||||||
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -1,5 +1,17 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.1.0
|
||||||
|
* Feature: Add `list-files` input to control test report file listing https://github.com/dorny/test-reporter/pull/773
|
||||||
|
* Feature: Add `summary_file` output with the path to the generated summary in Markdown format https://github.com/dorny/test-reporter/pull/772
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
* Feature: Use NodeJS 24 LTS as default runtime https://github.com/dorny/test-reporter/pull/738
|
||||||
|
|
||||||
|
## 2.7.0
|
||||||
|
* Feature: Add `slug-prefix` output for link anchors https://github.com/dorny/test-reporter/pull/731
|
||||||
|
* Feature: Report `jest-junit` testsuite errors as failures https://github.com/dorny/test-reporter/pull/155
|
||||||
|
* Security: Update dependencies to fix reported security vulnerabilities
|
||||||
|
|
||||||
## 2.6.0
|
## 2.6.0
|
||||||
* Fix: For `workflow_run` events, resolve the commit of the check run from related pull request head commits first (matching `workflow_run.head_branch`, then first PR), and fall back to `workflow_run.head_sha` for non-PR runs https://github.com/dorny/test-reporter/pull/673
|
* Fix: For `workflow_run` events, resolve the commit of the check run from related pull request head commits first (matching `workflow_run.head_branch`, then first PR), and fall back to `workflow_run.head_sha` for non-PR runs https://github.com/dorny/test-reporter/pull/673
|
||||||
* Change: The `test-reporter` action will listed all artifacts associated with the build run https://github.com/dorny/test-reporter/pull/693
|
* Change: The `test-reporter` action will listed all artifacts associated with the build run https://github.com/dorny/test-reporter/pull/693
|
||||||
|
|
|
||||||
27
README.md
27
README.md
|
|
@ -47,12 +47,12 @@ jobs:
|
||||||
name: Build & Test
|
name: Build & Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # checkout the repo
|
- uses: actions/checkout@v6 # checkout the repo
|
||||||
- run: npm ci # install packages
|
- run: npm ci # install packages
|
||||||
- run: npm test # run tests (configured to use jest-junit reporter)
|
- run: npm test # run tests (configured to use jest-junit reporter)
|
||||||
|
|
||||||
- name: Test Report
|
- name: Test Report
|
||||||
uses: dorny/test-reporter@v2
|
uses: dorny/test-reporter@v3
|
||||||
if: ${{ !cancelled() }} # run this step even if previous step failed
|
if: ${{ !cancelled() }} # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: JEST Tests # Name of the check run which will be created
|
name: JEST Tests # Name of the check run which will be created
|
||||||
|
|
@ -78,10 +78,10 @@ jobs:
|
||||||
build-test:
|
build-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # checkout the repo
|
- uses: actions/checkout@v6 # checkout the repo
|
||||||
- run: npm ci # install packages
|
- run: npm ci # install packages
|
||||||
- run: npm test # run tests (configured to use jest-junit reporter)
|
- run: npm test # run tests (configured to use jest-junit reporter)
|
||||||
- uses: actions/upload-artifact@v4 # upload test results
|
- uses: actions/upload-artifact@v7 # upload test results
|
||||||
if: ${{ !cancelled() }} # run this step even if previous step failed
|
if: ${{ !cancelled() }} # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
|
|
@ -103,7 +103,7 @@ jobs:
|
||||||
report:
|
report:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dorny/test-reporter@v2
|
- uses: dorny/test-reporter@v3
|
||||||
with:
|
with:
|
||||||
artifact: test-results # artifact name
|
artifact: test-results # artifact name
|
||||||
name: JEST Tests # Name of the check run which will be created
|
name: JEST Tests # Name of the check run which will be created
|
||||||
|
|
@ -114,7 +114,7 @@ jobs:
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: dorny/test-reporter@v2
|
- uses: dorny/test-reporter@v3
|
||||||
with:
|
with:
|
||||||
|
|
||||||
# Name or regex of artifact containing test results
|
# Name or regex of artifact containing test results
|
||||||
|
|
@ -184,6 +184,12 @@ jobs:
|
||||||
# none
|
# none
|
||||||
list-tests: 'all'
|
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.
|
# Limits number of created annotations with error message and stack trace captured during test execution.
|
||||||
# Must be less or equal to 50.
|
# Must be less or equal to 50.
|
||||||
max-annotations: '10'
|
max-annotations: '10'
|
||||||
|
|
@ -194,6 +200,13 @@ jobs:
|
||||||
# Set this action as failed if no test results were found
|
# Set this action as failed if no test results were found
|
||||||
fail-on-empty: 'true'
|
fail-on-empty: 'true'
|
||||||
|
|
||||||
|
# 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
|
||||||
|
collapsed: 'auto'
|
||||||
|
|
||||||
# Relative path under $GITHUB_WORKSPACE where the repository was checked out.
|
# Relative path under $GITHUB_WORKSPACE where the repository was checked out.
|
||||||
working-directory: ''
|
working-directory: ''
|
||||||
|
|
||||||
|
|
@ -212,6 +225,8 @@ jobs:
|
||||||
| time | Test execution time [ms] |
|
| time | Test execution time [ms] |
|
||||||
| url | Check run URL |
|
| url | Check run URL |
|
||||||
| url_html | Check run URL HTML |
|
| url_html | Check run URL HTML |
|
||||||
|
| summary_file | Path to a file containing the generated test report summary in Markdown format |
|
||||||
|
| slug_prefix| Random anchor links slug prefix generated for the summary headers |
|
||||||
|
|
||||||
## Supported formats
|
## Supported formats
|
||||||
|
|
||||||
|
|
|
||||||
22
__tests__/__outputs__/jest-test-errors-results.md
Normal file
22
__tests__/__outputs__/jest-test-errors-results.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|

|
||||||
|
|Report|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[fixtures/test-errors/jest/jest-test-results.xml](#user-content-r0)||2 ❌||646ms|
|
||||||
|
## ❌ <a id="user-content-r0" href="#user-content-r0">fixtures/test-errors/jest/jest-test-results.xml</a>
|
||||||
|
**2** tests were completed in **646ms** with **0** passed, **2** failed and **0** skipped.
|
||||||
|
|Test suite|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[libs/bar.spec.ts](#user-content-r0s0)||1 ❌||0ms|
|
||||||
|
|[libs/foo.spec.ts](#user-content-r0s1)||1 ❌||0ms|
|
||||||
|
### ❌ <a id="user-content-r0s0" href="#user-content-r0s0">libs/bar.spec.ts</a>
|
||||||
|
```
|
||||||
|
Test suite failed to run
|
||||||
|
❌ libs/bar.spec.ts
|
||||||
|
● Test suite failed to run
|
||||||
|
```
|
||||||
|
### ❌ <a id="user-content-r0s1" href="#user-content-r0s1">libs/foo.spec.ts</a>
|
||||||
|
```
|
||||||
|
Test suite failed to run
|
||||||
|
❌ libs/foo.spec.ts
|
||||||
|
● Test suite failed to run
|
||||||
|
```
|
||||||
|
|
@ -1,5 +1,68 @@
|
||||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`jest-junit tests jest testsuite errors example test results matches snapshot 1`] = `
|
||||||
|
TestRunResult {
|
||||||
|
"path": "fixtures/test-errors/jest/jest-test-results.xml",
|
||||||
|
"suites": [
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "Test suite failed to run",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": " ● Test suite failed to run
|
||||||
|
|
||||||
|
tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file
|
||||||
|
|
||||||
|
13 "typeRoots": ["./src/lib/types", "./node_modules/@types"],
|
||||||
|
~~~~~~~~~~~
|
||||||
|
",
|
||||||
|
"line": undefined,
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "libs/foo.spec.ts",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "libs/foo.spec.ts",
|
||||||
|
"totalTime": 0,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "Test suite failed to run",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": " ● Test suite failed to run
|
||||||
|
|
||||||
|
tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file
|
||||||
|
|
||||||
|
13 "typeRoots": ["./src/lib/types", "./node_modules/@types"],
|
||||||
|
~~~~~~~~~~~
|
||||||
|
",
|
||||||
|
"line": undefined,
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "libs/bar.spec.ts",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "libs/bar.spec.ts",
|
||||||
|
"totalTime": 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"totalTime": 646,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`jest-junit tests parsing ESLint report without timing information works - PR #134 1`] = `
|
exports[`jest-junit tests parsing ESLint report without timing information works - PR #134 1`] = `
|
||||||
TestRunResult {
|
TestRunResult {
|
||||||
"path": "fixtures/jest-junit-eslint.xml",
|
"path": "fixtures/jest-junit-eslint.xml",
|
||||||
|
|
|
||||||
3
__tests__/fixtures/test-errors/jest/files.txt
Normal file
3
__tests__/fixtures/test-errors/jest/files.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
libs/bar.spec.ts
|
||||||
|
libs/foo.spec.ts
|
||||||
|
tsconfig.json
|
||||||
25
__tests__/fixtures/test-errors/jest/jest-test-results.xml
Normal file
25
__tests__/fixtures/test-errors/jest/jest-test-results.xml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites name="jest tests" tests="0" failures="0" errors="2" time="0.646">
|
||||||
|
<testsuite name="libs/foo.spec.ts" errors="1" failures="0" skipped="0" timestamp="1970-01-01T00:00:00" time="0" tests="0">
|
||||||
|
<testcase classname="Test suite failed to run" name="libs/foo.spec.ts" time="0">
|
||||||
|
<error> ● Test suite failed to run
|
||||||
|
|
||||||
|
tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file
|
||||||
|
|
||||||
|
13 "typeRoots": ["./src/lib/types", "./node_modules/@types"],
|
||||||
|
~~~~~~~~~~~
|
||||||
|
</error>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="libs/bar.spec.ts" errors="1" failures="0" skipped="0" timestamp="1970-01-01T00:00:00" time="0" tests="0">
|
||||||
|
<testcase classname="Test suite failed to run" name="libs/bar.spec.ts" time="0">
|
||||||
|
<error> ● Test suite failed to run
|
||||||
|
|
||||||
|
tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file
|
||||||
|
|
||||||
|
13 "typeRoots": ["./src/lib/types", "./node_modules/@types"],
|
||||||
|
~~~~~~~~~~~
|
||||||
|
</error>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
@ -351,4 +351,27 @@ 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## 1 passed, 4 failed and 1 skipped\n/)
|
expect(report).toMatch(/^# My Custom Title\n## 1 passed, 4 failed and 1 skipped\n/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('jest testsuite errors example test results matches snapshot', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'test-errors', 'jest', 'jest-test-results.xml')
|
||||||
|
const trackedFilesPath = path.join(__dirname, 'fixtures', 'test-errors', 'jest', 'files.txt')
|
||||||
|
const outputPath = path.join(__dirname, '__outputs__', 'jest-test-errors-results.md')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const trackedFiles = fs.readFileSync(trackedFilesPath, {encoding: 'utf8'}).split(/\n\r?/g)
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles
|
||||||
|
//workDir: '/home/dorny/dorny/jest/'
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new JestJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
expect(result).toMatchSnapshot()
|
||||||
|
|
||||||
|
const report = getReport([result])
|
||||||
|
fs.mkdirSync(path.dirname(outputPath), {recursive: true})
|
||||||
|
fs.writeFileSync(outputPath, report)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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('getBadge', () => {
|
||||||
describe('URI encoding with special characters', () => {
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
28
__tests__/utils/slugger.test.ts
Normal file
28
__tests__/utils/slugger.test.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {DEFAULT_OPTIONS} from '../../src/report/get-report.js'
|
||||||
|
import {slug} from '../../src/utils/slugger.js'
|
||||||
|
|
||||||
|
describe('slugger', () => {
|
||||||
|
it('adds prefix from report options to generated slug', () => {
|
||||||
|
const result = slug('r0s1', {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
slugPrefix: 'prefix-'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
id: 'user-content-prefix-r0s1',
|
||||||
|
link: '#user-content-prefix-r0s1'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sanitizes custom prefix using existing slug normalization', () => {
|
||||||
|
const result = slug('r0', {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
slugPrefix: ' my /custom_prefix?.'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
id: 'user-content-my-customprefix-r0',
|
||||||
|
link: '#user-content-my-customprefix-r0'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
14
action.yml
14
action.yml
|
|
@ -54,6 +54,14 @@ inputs:
|
||||||
- none
|
- none
|
||||||
required: false
|
required: false
|
||||||
default: 'all'
|
default: 'all'
|
||||||
|
list-files:
|
||||||
|
description: |
|
||||||
|
Limits which test result files are listed. Supported options:
|
||||||
|
- all
|
||||||
|
- failed
|
||||||
|
- none
|
||||||
|
required: false
|
||||||
|
default: 'all'
|
||||||
max-annotations:
|
max-annotations:
|
||||||
description: |
|
description: |
|
||||||
Limits number of created annotations with error message and stack trace captured during test execution.
|
Limits number of created annotations with error message and stack trace captured during test execution.
|
||||||
|
|
@ -122,8 +130,12 @@ outputs:
|
||||||
description: Check run URL
|
description: Check run URL
|
||||||
url_html:
|
url_html:
|
||||||
description: Check run URL HTML
|
description: Check run URL HTML
|
||||||
|
summary_file:
|
||||||
|
description: Path to a file containing the generated test report summary in Markdown format
|
||||||
|
slug_prefix:
|
||||||
|
description: Random prefix added to generated report anchor slugs for this action run
|
||||||
runs:
|
runs:
|
||||||
using: 'node20'
|
using: 'node24'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
branding:
|
branding:
|
||||||
color: blue
|
color: blue
|
||||||
|
|
|
||||||
1929
dist/index.js
generated
vendored
1929
dist/index.js
generated
vendored
File diff suppressed because it is too large
Load diff
2
dist/licenses.txt
generated
vendored
2
dist/licenses.txt
generated
vendored
|
|
@ -889,7 +889,7 @@ reusify
|
||||||
MIT
|
MIT
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2015 Matteo Collina
|
Copyright (c) 2015-2024 Matteo Collina
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
2529
package-lock.json
generated
2529
package-lock.json
generated
File diff suppressed because it is too large
Load diff
36
package.json
36
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "test-reporter",
|
"name": "test-reporter",
|
||||||
"version": "2.5.0",
|
"version": "3.0.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",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
@ -37,37 +37,37 @@
|
||||||
"author": "Michal Dorner <dorner.michal@gmail.com>",
|
"author": "Michal Dorner <dorner.michal@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.0",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^3.0.0",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/github": "^9.0.0",
|
"@actions/github": "^9.1.1",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.17",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.4",
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@octokit/webhooks-types": "^7.6.1",
|
"@octokit/webhooks-types": "^7.6.1",
|
||||||
"@types/adm-zip": "^0.5.7",
|
"@types/adm-zip": "^0.5.8",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^20.19.23",
|
"@types/node": "^24.12.2",
|
||||||
"@types/picomatch": "^4.0.2",
|
"@types/picomatch": "^4.0.3",
|
||||||
"@types/xml2js": "^0.4.14",
|
"@types/xml2js": "^0.4.14",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
||||||
"@typescript-eslint/parser": "^8.56.1",
|
"@typescript-eslint/parser": "^8.59.0",
|
||||||
"@vercel/ncc": "^0.38.4",
|
"@vercel/ncc": "^0.38.4",
|
||||||
"eol-converter-cli": "^1.1.0",
|
"eol-converter-cli": "^1.1.0",
|
||||||
"eslint": "^9.39.3",
|
"eslint": "^9.39.4",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-github": "^6.0.0",
|
"eslint-plugin-github": "^6.0.0",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-jest": "^29.15.0",
|
"eslint-plugin-jest": "^29.15.2",
|
||||||
"eslint-plugin-prettier": "^5.5.5",
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.3.0",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^17.0.0",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.3",
|
||||||
"ts-jest": "^29.4.6",
|
"ts-jest": "^29.4.9",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^6.0.3"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"sax": "^1.4.3"
|
"sax": "^1.4.3"
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
"titleTemplate": "{title}"
|
"titleTemplate": "{title}"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=24"
|
||||||
},
|
},
|
||||||
"markdownlint-cli2": {
|
"markdownlint-cli2": {
|
||||||
"ignores": [
|
"ignores": [
|
||||||
|
|
|
||||||
7
reports/jest/package-lock.json
generated
7
reports/jest/package-lock.json
generated
|
|
@ -4548,10 +4548,11 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
38
src/main.ts
38
src/main.ts
|
|
@ -1,6 +1,10 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import {GitHub} from '@actions/github/lib/utils'
|
import {GitHub} from '@actions/github/lib/utils'
|
||||||
|
import {randomBytes} from 'node:crypto'
|
||||||
|
import {writeFileSync} from 'node:fs'
|
||||||
|
import {tmpdir} from 'node:os'
|
||||||
|
import {join} from 'node:path'
|
||||||
|
|
||||||
import {ArtifactProvider} from './input-providers/artifact-provider.js'
|
import {ArtifactProvider} from './input-providers/artifact-provider.js'
|
||||||
import {LocalFileProvider} from './input-providers/local-file-provider.js'
|
import {LocalFileProvider} from './input-providers/local-file-provider.js'
|
||||||
|
|
@ -43,12 +47,14 @@ class TestReporter {
|
||||||
readonly reporter = core.getInput('reporter', {required: true})
|
readonly reporter = core.getInput('reporter', {required: true})
|
||||||
readonly listSuites = core.getInput('list-suites', {required: true}) as 'all' | 'failed' | 'none'
|
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 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 maxAnnotations = parseInt(core.getInput('max-annotations', {required: true}))
|
||||||
readonly failOnError = core.getInput('fail-on-error', {required: true}) === 'true'
|
readonly failOnError = core.getInput('fail-on-error', {required: true}) === 'true'
|
||||||
readonly failOnEmpty = core.getInput('fail-on-empty', {required: true}) === 'true'
|
readonly failOnEmpty = core.getInput('fail-on-empty', {required: true}) === 'true'
|
||||||
readonly workDirInput = core.getInput('working-directory', {required: false})
|
readonly workDirInput = core.getInput('working-directory', {required: false})
|
||||||
readonly onlySummary = core.getInput('only-summary', {required: false}) === 'true'
|
readonly onlySummary = core.getInput('only-summary', {required: false}) === 'true'
|
||||||
readonly useActionsSummary = core.getInput('use-actions-summary', {required: false}) === 'true'
|
readonly useActionsSummary = core.getInput('use-actions-summary', {required: false}) === 'true'
|
||||||
|
readonly slugPrefix = `tr-${randomBytes(4).toString('base64url')}-`
|
||||||
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 collapsed = core.getInput('collapsed', {required: false}) as 'auto' | 'always' | 'never'
|
||||||
|
|
@ -69,6 +75,11 @@ class TestReporter {
|
||||||
return
|
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') {
|
if (this.collapsed !== 'auto' && this.collapsed !== 'always' && this.collapsed !== 'never') {
|
||||||
core.setFailed(`Input parameter 'collapsed' has invalid value`)
|
core.setFailed(`Input parameter 'collapsed' has invalid value`)
|
||||||
return
|
return
|
||||||
|
|
@ -144,6 +155,7 @@ class TestReporter {
|
||||||
core.setOutput('failed', failed)
|
core.setOutput('failed', failed)
|
||||||
core.setOutput('skipped', skipped)
|
core.setOutput('skipped', skipped)
|
||||||
core.setOutput('time', time)
|
core.setOutput('time', time)
|
||||||
|
core.setOutput('slug_prefix', this.slugPrefix)
|
||||||
|
|
||||||
if (this.failOnError && isFailed) {
|
if (this.failOnError && isFailed) {
|
||||||
core.setFailed(`Failed test were found and 'fail-on-error' option is set to ${this.failOnError}`)
|
core.setFailed(`Failed test were found and 'fail-on-error' option is set to ${this.failOnError}`)
|
||||||
|
|
@ -174,7 +186,17 @@ class TestReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {listSuites, listTests, 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 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)
|
||||||
|
|
@ -188,6 +210,8 @@ class TestReporter {
|
||||||
{
|
{
|
||||||
listSuites,
|
listSuites,
|
||||||
listTests,
|
listTests,
|
||||||
|
slugPrefix,
|
||||||
|
listFiles,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
|
|
@ -200,6 +224,7 @@ class TestReporter {
|
||||||
|
|
||||||
core.info('Summary content:')
|
core.info('Summary content:')
|
||||||
core.info(summary)
|
core.info(summary)
|
||||||
|
this.writeSummaryFile(summary)
|
||||||
await core.summary.addRaw(summary).write()
|
await core.summary.addRaw(summary).write()
|
||||||
} else {
|
} else {
|
||||||
core.info(`Creating check run ${name}`)
|
core.info(`Creating check run ${name}`)
|
||||||
|
|
@ -219,6 +244,8 @@ class TestReporter {
|
||||||
const summary = getReport(results, {
|
const summary = getReport(results, {
|
||||||
listSuites,
|
listSuites,
|
||||||
listTests,
|
listTests,
|
||||||
|
slugPrefix,
|
||||||
|
listFiles,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
|
|
@ -229,6 +256,7 @@ class TestReporter {
|
||||||
|
|
||||||
core.info('Creating annotations')
|
core.info('Creating annotations')
|
||||||
const annotations = getAnnotations(results, this.maxAnnotations)
|
const annotations = getAnnotations(results, this.maxAnnotations)
|
||||||
|
this.writeSummaryFile(summary)
|
||||||
|
|
||||||
const isFailed = this.failOnError && results.some(tr => tr.result === 'failed')
|
const isFailed = this.failOnError && results.some(tr => tr.result === 'failed')
|
||||||
const conclusion = isFailed ? 'failure' : 'success'
|
const conclusion = isFailed ? 'failure' : 'success'
|
||||||
|
|
@ -255,6 +283,14 @@ class TestReporter {
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeSummaryFile(summary: string): void {
|
||||||
|
const dir = process.env.RUNNER_TEMP || tmpdir()
|
||||||
|
const file = join(dir, `test-reporter-summary-${randomBytes(8).toString('hex')}.md`)
|
||||||
|
writeFileSync(file, summary)
|
||||||
|
core.info(`Summary written to ${file}`)
|
||||||
|
core.setOutput('summary_file', file)
|
||||||
|
}
|
||||||
|
|
||||||
getParser(reporter: string, options: ParseOptions): TestParser {
|
getParser(reporter: string, options: ParseOptions): TestParser {
|
||||||
switch (reporter) {
|
switch (reporter) {
|
||||||
case 'dart-json':
|
case 'dart-json':
|
||||||
|
|
|
||||||
|
|
@ -75,17 +75,18 @@ export class JestJunitParser implements TestParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTestCaseResult(test: TestCase): TestExecutionResult {
|
private getTestCaseResult(test: TestCase): TestExecutionResult {
|
||||||
if (test.failure) return 'failed'
|
if (test.failure || test.error) return 'failed'
|
||||||
if (test.skipped) return 'skipped'
|
if (test.skipped) return 'skipped'
|
||||||
return 'success'
|
return 'success'
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTestCaseError(tc: TestCase): TestCaseError | undefined {
|
private getTestCaseError(tc: TestCase): TestCaseError | undefined {
|
||||||
if (!this.options.parseErrors || !tc.failure) {
|
if (!this.options.parseErrors || !(tc.failure || tc.error)) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const details = typeof tc.failure[0] === 'string' ? tc.failure[0] : tc.failure[0]['_']
|
const message = tc.failure ? tc.failure[0] : tc.error ? tc.error[0] : 'unknown failure'
|
||||||
|
const details = typeof message === 'string' ? message : message['_']
|
||||||
let path
|
let path
|
||||||
let line
|
let line
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,5 @@ export interface TestCase {
|
||||||
}
|
}
|
||||||
failure?: string[]
|
failure?: string[]
|
||||||
skipped?: string[]
|
skipped?: string[]
|
||||||
|
error?: string[]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ const MAX_ACTIONS_SUMMARY_LENGTH = 1048576
|
||||||
export interface ReportOptions {
|
export interface ReportOptions {
|
||||||
listSuites: 'all' | 'failed' | 'none'
|
listSuites: 'all' | 'failed' | 'none'
|
||||||
listTests: 'all' | 'failed' | 'none'
|
listTests: 'all' | 'failed' | 'none'
|
||||||
|
slugPrefix: string
|
||||||
|
listFiles: 'all' | 'failed' | 'none'
|
||||||
baseUrl: string
|
baseUrl: string
|
||||||
onlySummary: boolean
|
onlySummary: boolean
|
||||||
useActionsSummary: boolean
|
useActionsSummary: boolean
|
||||||
|
|
@ -22,6 +24,8 @@ export interface ReportOptions {
|
||||||
export const DEFAULT_OPTIONS: ReportOptions = {
|
export const DEFAULT_OPTIONS: ReportOptions = {
|
||||||
listSuites: 'all',
|
listSuites: 'all',
|
||||||
listTests: 'all',
|
listTests: 'all',
|
||||||
|
slugPrefix: '',
|
||||||
|
listFiles: 'all',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
onlySummary: false,
|
onlySummary: false,
|
||||||
useActionsSummary: true,
|
useActionsSummary: true,
|
||||||
|
|
@ -171,21 +175,29 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
||||||
sections.push(` `)
|
sections.push(` `)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testRuns.length > 0 || options.onlySummary) {
|
// Filter test runs based on list-files option
|
||||||
const tableData = testRuns
|
const filteredTestRuns =
|
||||||
.map((tr, originalIndex) => ({tr, originalIndex}))
|
options.listFiles === 'failed'
|
||||||
.filter(({tr}) => tr.passed > 0 || tr.failed > 0 || tr.skipped > 0)
|
? testRuns.filter(tr => tr.result === 'failed')
|
||||||
.map(({tr, originalIndex}) => {
|
: options.listFiles === 'none'
|
||||||
const time = formatTime(tr.time)
|
? []
|
||||||
const name = tr.path
|
: testRuns
|
||||||
const addr = options.baseUrl + makeRunSlug(originalIndex, options).link
|
|
||||||
const nameLink = link(name, addr)
|
|
||||||
const passed = tr.passed > 0 ? `${tr.passed} ${Icon.success}` : ''
|
|
||||||
const failed = tr.failed > 0 ? `${tr.failed} ${Icon.fail}` : ''
|
|
||||||
const skipped = tr.skipped > 0 ? `${tr.skipped} ${Icon.skip}` : ''
|
|
||||||
return [nameLink, passed, failed, skipped, time]
|
|
||||||
})
|
|
||||||
|
|
||||||
|
const tableData = filteredTestRuns
|
||||||
|
.map((tr, originalIndex) => ({tr, originalIndex}))
|
||||||
|
.filter(({tr}) => tr.passed > 0 || tr.failed > 0 || tr.skipped > 0)
|
||||||
|
.map(({tr, originalIndex}) => {
|
||||||
|
const time = formatTime(tr.time)
|
||||||
|
const name = tr.path
|
||||||
|
const addr = options.baseUrl + makeRunSlug(originalIndex, options).link
|
||||||
|
const nameLink = link(name, addr)
|
||||||
|
const passed = tr.passed > 0 ? `${tr.passed} ${Icon.success}` : ''
|
||||||
|
const failed = tr.failed > 0 ? `${tr.failed} ${Icon.fail}` : ''
|
||||||
|
const skipped = tr.skipped > 0 ? `${tr.skipped} ${Icon.skip}` : ''
|
||||||
|
return [nameLink, passed, failed, skipped, time]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (tableData.length > 0) {
|
||||||
const resultsTable = table(
|
const resultsTable = table(
|
||||||
['Report', 'Passed', 'Failed', 'Skipped', 'Time'],
|
['Report', 'Passed', 'Failed', 'Skipped', 'Time'],
|
||||||
[Align.Left, Align.Right, Align.Right, Align.Right, Align.Right],
|
[Align.Left, Align.Right, Align.Right, Align.Right, Align.Right],
|
||||||
|
|
@ -195,7 +207,7 @@ function getTestRunsReport(testRuns: TestRunResult[], options: ReportOptions): s
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.onlySummary === false) {
|
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)
|
sections.push(...suitesReports)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import {ReportOptions} from '../report/get-report.js'
|
import {ReportOptions} from '../report/get-report.js'
|
||||||
|
|
||||||
export function slug(name: string, options: ReportOptions): {id: string; link: string} {
|
export function slug(name: string, options: ReportOptions): {id: string; link: string} {
|
||||||
const slugId = name
|
const slugId = `${options.slugPrefix}${name}`
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/_/g, '')
|
.replace(/_/g, '')
|
||||||
.replace(/[./\\]/g, '-')
|
.replace(/[./\\]/g, '-')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue