Skip to content

Commit 9b23449

Browse files
authored
Merge pull request #1 from estruyf/dev
2 parents 682fce5 + 05ba3bd commit 9b23449

13 files changed

+208
-106
lines changed

.github/workflows/release-dev.yml

+7
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@ on:
44
branches:
55
- dev
66

7+
# Cancel the previous runs (if any are still waiting)
8+
concurrency:
9+
group: ${{ github.workflow }}
10+
cancel-in-progress: true
11+
712
jobs:
813
build:
914
runs-on: ubuntu-latest
15+
environment:
16+
name: npm
1017
steps:
1118
- uses: actions/checkout@v3
1219
- uses: actions/setup-node@v3

.github/workflows/testing.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ name: E2E Testing
33
on:
44
workflow_dispatch:
55
push:
6-
branches: main
6+
branches:
7+
- main
8+
- dev
79

810
jobs:
911
testing:

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [1.1.0]
6+
7+
- Update test suite logic
8+
- Added OS name
9+
- Added browser/project name
10+
- Added retries count
11+
- Added `showError` option
12+
513
## [1.0.0]
614

715
- Initial release

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The reporter supports the following configuration options:
3535
| --- | --- | --- |
3636
| title | Title of the report | `Test results` |
3737
| useDetails | Use details in summary which creates expandable content | `false` |
38+
| showError | Show error message in summary | `false` |
3839

3940
To use these option, you can update the reporter configuration:
4041

@@ -45,7 +46,8 @@ export default defineConfig({
4546
reporter: [
4647
['@estruyf/github-actions-reporter', {
4748
title: 'My custom title',
48-
useDetails: true
49+
useDetails: true,
50+
showError: true
4951
}]
5052
],
5153
});

assets/example-with-details.png

9.4 KB
Loading

assets/example-without-details.png

12.2 KB
Loading

package-lock.json

+26-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@estruyf/github-actions-reporter",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "GitHub Actions reporter for Playwright",
55
"main": "dist/index.js",
66
"scripts": {
@@ -34,7 +34,8 @@
3434
"typescript": "^5.1.6"
3535
},
3636
"dependencies": {
37-
"@actions/core": "^1.10.0"
37+
"@actions/core": "^1.10.0",
38+
"ansi-to-html": "^0.7.2"
3839
},
3940
"peerDependencies": {
4041
"@playwright/test": "^1.37.0"

playwright.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const config: PlaywrightTestConfig<{}, {}> = {
1111
retries: process.env.CI ? 2 : 1,
1212
workers: process.env.CI ? 1 : undefined,
1313
reporter: [
14-
["./src/index.ts", { title: "Reporter testing" }],
14+
["./src/index.ts", { title: "Reporter testing", showError: true }],
1515
[
1616
"./src/index.ts",
1717
{ title: "Reporter testing with details", useDetails: true },

src/index.ts

+65-67
Original file line numberDiff line numberDiff line change
@@ -3,102 +3,100 @@ import type {
33
FullConfig,
44
Suite,
55
TestCase,
6-
TestResult,
76
FullResult,
8-
TestStatus,
97
} from "@playwright/test/reporter";
108
import * as core from "@actions/core";
119
import { basename } from "path";
1210
import { getHtmlTable } from "./utils/getHtmlTable";
1311
import { getTableRows } from "./utils/getTableRows";
12+
import { checkForFailedTests } from "./utils/checkForFailedTests";
13+
import Convert from "ansi-to-html";
1414

1515
interface GitHubActionOptions {
1616
title?: string;
1717
useDetails?: boolean;
18-
}
19-
20-
interface TestDetails {
21-
total: number | undefined;
22-
23-
tests: {
24-
[fileName: string]: TestResults;
25-
};
26-
}
27-
28-
export interface TestResults {
29-
[testTitle: string]: {
30-
status: TestStatus | "pending";
31-
duration: number;
32-
};
18+
showError?: boolean;
3319
}
3420

3521
class GitHubAction implements Reporter {
36-
private testDetails: TestDetails = {
37-
total: undefined,
38-
tests: {},
39-
};
22+
private suite: Suite | undefined;
4023

4124
constructor(private options: GitHubActionOptions = {}) {
4225
console.log(`Using GitHub Actions reporter`);
4326
}
4427

45-
onBegin(config: FullConfig, suite: Suite) {
46-
this.testDetails.total = suite.allTests().length;
47-
}
48-
49-
onTestBegin(test: TestCase, result: TestResult) {
50-
const fileName = basename(test.location.file);
51-
52-
if (!this.testDetails.tests[fileName]) {
53-
this.testDetails.tests[fileName] = {};
54-
}
55-
56-
if (!this.testDetails.tests[fileName][test.title]) {
57-
this.testDetails.tests[fileName][test.title] = {
58-
status: "pending",
59-
duration: 0,
60-
};
61-
}
62-
}
63-
64-
onTestEnd(test: TestCase, result: TestResult) {
65-
const fileName = basename(test.location.file);
66-
67-
this.testDetails.tests[fileName][test.title] = {
68-
status: result.status,
69-
duration: result.duration,
70-
};
28+
onBegin(_: FullConfig, suite: Suite) {
29+
this.suite = suite;
7130
}
7231

7332
async onEnd(result: FullResult) {
74-
if (process.env.GITHUB_ACTIONS) {
33+
if (process.env.GITHUB_ACTIONS && this.suite) {
34+
const os = process.platform;
7535
const summary = core.summary;
7636
summary.addHeading(this.options.title || `Test results`, 1);
7737

78-
summary.addRaw(`Total tests: ${this.testDetails.total}`);
38+
summary.addRaw(`Total tests: ${this.suite.allTests().length}`);
7939

8040
if (this.options.useDetails) {
8141
summary.addSeparator();
8242
}
8343

84-
for (const fileName of Object.keys(this.testDetails.tests)) {
85-
if (this.options.useDetails) {
86-
const content = getHtmlTable(this.testDetails.tests[fileName]);
87-
88-
// Check if there are any failed tests
89-
const failedTests = Object.values(
90-
this.testDetails.tests[fileName]
91-
).filter((test) => test.status !== "passed");
92-
93-
summary.addDetails(
94-
`${failedTests.length === 0 ? "✅" : "❌"} ${fileName}`,
95-
content
96-
);
97-
} else {
98-
summary.addHeading(fileName, 2);
99-
100-
const tableRows = getTableRows(this.testDetails.tests[fileName]);
101-
summary.addTable(tableRows);
44+
for (const suite of this.suite?.suites) {
45+
const project = suite.project();
46+
47+
// Get all the test files
48+
const files = suite
49+
.allTests()
50+
.map((test) => test.location.file)
51+
.reduce((acc, curr) => {
52+
if (!acc.includes(curr)) {
53+
acc.push(curr);
54+
}
55+
56+
return acc;
57+
}, [] as string[]);
58+
59+
// Get all the tests per file
60+
const tests = files.reduce((acc, curr) => {
61+
acc[curr] = suite.allTests().filter((test) => {
62+
return test.location.file === curr;
63+
});
64+
65+
return acc;
66+
}, {} as { [fileName: string]: TestCase[] });
67+
68+
for (const filePath of Object.keys(tests)) {
69+
const fileName = basename(filePath);
70+
71+
if (this.options.useDetails) {
72+
const content = getHtmlTable(
73+
tests[filePath],
74+
!!this.options.showError
75+
);
76+
77+
// Check if there are any failed tests
78+
const hasFailedTests = checkForFailedTests(tests[filePath]);
79+
80+
summary.addDetails(
81+
`${hasFailedTests ? "❌" : "✅"} ${fileName} (${os}${
82+
project!.name ? ` / ${project!.name}` : ""
83+
})`,
84+
content
85+
);
86+
} else {
87+
summary.addHeading(
88+
`${fileName} (${os}${
89+
project!.name ? ` / ${project!.name}` : ""
90+
})`,
91+
2
92+
);
93+
94+
const tableRows = getTableRows(
95+
tests[filePath],
96+
!!this.options.showError
97+
);
98+
summary.addTable(tableRows);
99+
}
102100
}
103101
}
104102

src/utils/checkForFailedTests.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TestCase } from "@playwright/test/reporter";
2+
3+
export const checkForFailedTests = (tests: TestCase[]) => {
4+
const failedTests = tests.filter((test) => {
5+
return test.results[test.results.length - 1].status !== "passed";
6+
});
7+
8+
return failedTests.length > 0;
9+
};

src/utils/getHtmlTable.ts

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { TestResults } from "..";
1+
import { TestCase } from "@playwright/test/reporter";
2+
import Convert from "ansi-to-html";
3+
4+
export const getHtmlTable = (tests: TestCase[], showError: boolean): string => {
5+
const convert = new Convert();
26

3-
export const getHtmlTable = (tests: TestResults): string => {
47
const content: string[] = [];
58

69
content.push(`<br>`);
@@ -10,19 +13,34 @@ export const getHtmlTable = (tests: TestResults): string => {
1013
content.push(`<th>Test</th>`);
1114
content.push(`<th>Status</th>`);
1215
content.push(`<th>Duration</th>`);
16+
content.push(`<th>Retries</th>`);
17+
if (showError) {
18+
content.push(`<th>Error</th>`);
19+
}
1320
content.push(`</tr>`);
1421
content.push(`</thead>`);
1522
content.push(`<tbody>`);
1623

17-
for (const testName of Object.keys(tests)) {
18-
const test = tests[testName];
24+
for (const test of tests) {
25+
// Get the last result
26+
const result = test.results[test.results.length - 1];
1927

2028
content.push(`<tr>`);
21-
content.push(`<td>${testName}</td>`);
29+
content.push(`<td>${test.title}</td>`);
2230
content.push(
23-
`<td>${test.status === "passed" ? "✅ Pass" : "❌ Fail"}</td>`
31+
`<td>${result.status === "passed" ? "✅ Pass" : "❌ Fail"}</td>`
2432
);
25-
content.push(`<td>${test.duration / 1000}s</td>`);
33+
content.push(`<td>${result.duration / 1000}s</td>`);
34+
content.push(`<td>${result.retry}</td>`);
35+
if (showError) {
36+
content.push(
37+
`<td>${
38+
result.error && result.error.message
39+
? convert.toHtml(result.error.message!)
40+
: ""
41+
}</td>`
42+
);
43+
}
2644
content.push(`</tr>`);
2745
}
2846

0 commit comments

Comments
 (0)