Compare commits
54 commits
| 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 |
16 changed files with 2872 additions and 1402 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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
# 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
|
## 2.7.0
|
||||||
* Feature: Add `slug-prefix` output for link anchors https://github.com/dorny/test-reporter/pull/731
|
* 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
|
* Feature: Report `jest-junit` testsuite errors as failures https://github.com/dorny/test-reporter/pull/155
|
||||||
|
|
|
||||||
19
README.md
19
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'
|
||||||
|
|
@ -219,6 +225,7 @@ 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 |
|
| slug_prefix| Random anchor links slug prefix generated for the summary headers |
|
||||||
|
|
||||||
## Supported formats
|
## Supported formats
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
12
action.yml
12
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,10 +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:
|
slug_prefix:
|
||||||
description: Random prefix added to generated report anchor slugs for this action run
|
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
|
||||||
|
|
|
||||||
1860
dist/index.js
generated
vendored
1860
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
|
||||||
|
|
|
||||||
2014
package-lock.json
generated
2014
package-lock.json
generated
File diff suppressed because it is too large
Load diff
32
package.json
32
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "test-reporter",
|
"name": "test-reporter",
|
||||||
"version": "2.7.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.57.0",
|
"@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.4",
|
"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.3.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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
33
src/main.ts
33
src/main.ts
|
|
@ -2,6 +2,9 @@ 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 {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'
|
||||||
|
|
@ -44,6 +47,7 @@ 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'
|
||||||
|
|
@ -71,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
|
||||||
|
|
@ -177,7 +186,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 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)
|
||||||
|
|
@ -192,6 +211,7 @@ class TestReporter {
|
||||||
listSuites,
|
listSuites,
|
||||||
listTests,
|
listTests,
|
||||||
slugPrefix,
|
slugPrefix,
|
||||||
|
listFiles,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
|
|
@ -204,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}`)
|
||||||
|
|
@ -224,6 +245,7 @@ class TestReporter {
|
||||||
listSuites,
|
listSuites,
|
||||||
listTests,
|
listTests,
|
||||||
slugPrefix,
|
slugPrefix,
|
||||||
|
listFiles,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onlySummary,
|
onlySummary,
|
||||||
useActionsSummary,
|
useActionsSummary,
|
||||||
|
|
@ -234,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'
|
||||||
|
|
@ -260,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':
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export interface ReportOptions {
|
||||||
listSuites: 'all' | 'failed' | 'none'
|
listSuites: 'all' | 'failed' | 'none'
|
||||||
listTests: 'all' | 'failed' | 'none'
|
listTests: 'all' | 'failed' | 'none'
|
||||||
slugPrefix: string
|
slugPrefix: string
|
||||||
|
listFiles: 'all' | 'failed' | 'none'
|
||||||
baseUrl: string
|
baseUrl: string
|
||||||
onlySummary: boolean
|
onlySummary: boolean
|
||||||
useActionsSummary: boolean
|
useActionsSummary: boolean
|
||||||
|
|
@ -24,6 +25,7 @@ export const DEFAULT_OPTIONS: ReportOptions = {
|
||||||
listSuites: 'all',
|
listSuites: 'all',
|
||||||
listTests: 'all',
|
listTests: 'all',
|
||||||
slugPrefix: '',
|
slugPrefix: '',
|
||||||
|
listFiles: 'all',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
onlySummary: false,
|
onlySummary: false,
|
||||||
useActionsSummary: true,
|
useActionsSummary: true,
|
||||||
|
|
@ -173,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],
|
||||||
|
|
@ -197,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue