ortoni-report 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +35 -0
- package/dist/icon/fail.png +0 -0
- package/dist/icon/file.png +0 -0
- package/dist/icon/flaky.png +0 -0
- package/dist/icon/pass.png +0 -0
- package/dist/icon/retry.png +0 -0
- package/dist/icon/skip.png +0 -0
- package/dist/icon/test.png +0 -0
- package/dist/icon/timeout.png +0 -0
- package/dist/ortoni-report.d.ts +18 -2
- package/dist/ortoni-report.js +71 -27
- package/dist/ortoni-report.mjs +71 -27
- package/dist/report-template.hbs +215 -27
- package/dist/types/reporterConfig.js +2 -0
- package/dist/types/testResults.js +2 -0
- package/dist/utils/chart.js +11128 -0
- package/dist/utils/modal.js +76 -0
- package/dist/utils/utils.js +44 -0
- package/package.json +47 -49
- package/readme.md +15 -6
package/changelog.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Change Log:
|
|
2
2
|
|
|
3
|
+
## Version 1.1.3
|
|
4
|
+
|
|
5
|
+
**New Features:**
|
|
6
|
+
- Added detailed steps to the testDetails section in the HTML report.
|
|
7
|
+
- Introduced a new flaky icon for better visual representation in the report.
|
|
8
|
+
- Display of test steps in the HTML report.
|
|
9
|
+
- Added a filter for retry tests to better categorize and display them.
|
|
10
|
+
|
|
11
|
+
**Improved:**
|
|
12
|
+
- Updated the package dependencies to remove vulnerabilities.
|
|
13
|
+
- Enhanced time formatting to include milliseconds in the duration display.
|
|
14
|
+
- Enhanced the calculation and display of the success rate in the HTML report.
|
|
15
|
+
|
|
16
|
+
## Version 1.1.2
|
|
17
|
+
|
|
18
|
+
**New Features:**
|
|
19
|
+
- **Ellipsis Styling for Test Names:** Long test names are now truncated with ellipsis for better readability.
|
|
20
|
+
- **Retry, Pass, Fail, and Skip Indicators:** Added visual indicators (images) to denote the status of each test, including retries.
|
|
21
|
+
- **Enhanced `msToTime` Function:** Duration now includes milliseconds for more precise time tracking.
|
|
22
|
+
- **Summary Filters:** Clicking on summary filters (passed, failed & other) to display the selected filter.
|
|
23
|
+
|
|
24
|
+
**Improvements:**
|
|
25
|
+
- **Unified Status Handling:** The `applyFilter` function now treats `failed` and `timedOut` statuses as the same for consistent filtering.
|
|
26
|
+
- **Date and Time Formatting:** Passed date in the format `DD-MMM-YYYY` and included time for more detailed reporting.
|
|
27
|
+
- **Success Rate and Last Run Data:** Added the ability to pass `successRate` and `lastRun` data to the `onEnd` method for comprehensive report details.
|
|
28
|
+
|
|
29
|
+
**Bug Fixes:**
|
|
30
|
+
- Fixed issues with conditionals in Handlebars for status checking.
|
|
31
|
+
- Improved event listener attachment for filtering and search functionalities.
|
|
32
|
+
|
|
33
|
+
**Notes:**
|
|
34
|
+
- Screenshot handling has been enhanced with modal dialogs, using Pico.css for styling.
|
|
35
|
+
|
|
36
|
+
This release includes several visual and functional enhancements aimed at improving the usability and readability of the Playwright test reports. The added features and improvements will help users better understand test outcomes and statuses at a glance.
|
|
37
|
+
|
|
3
38
|
### Version 1.1.0
|
|
4
39
|
|
|
5
40
|
## New Features
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/ortoni-report.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
1
|
+
import { TestStep, Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
2
2
|
|
|
3
3
|
interface ReporterConfig {
|
|
4
4
|
projectName?: string;
|
|
@@ -6,6 +6,22 @@ interface ReporterConfig {
|
|
|
6
6
|
testType?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
interface TestResultData {
|
|
10
|
+
retry: string;
|
|
11
|
+
isRetry: number;
|
|
12
|
+
projectName: any;
|
|
13
|
+
suite: any;
|
|
14
|
+
title: string;
|
|
15
|
+
status: "passed" | "failed" | "timedOut" | "skipped" | "interrupted" | "expected" | "unexpected" | "flaky";
|
|
16
|
+
flaky: string;
|
|
17
|
+
duration: string;
|
|
18
|
+
errors: any[];
|
|
19
|
+
steps: TestStep[];
|
|
20
|
+
logs: string;
|
|
21
|
+
screenshotPath: string | null;
|
|
22
|
+
filePath: any;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
declare class OrtoniReport implements Reporter {
|
|
10
26
|
private results;
|
|
11
27
|
private groupedResults;
|
|
@@ -16,7 +32,7 @@ declare class OrtoniReport implements Reporter {
|
|
|
16
32
|
onTestBegin(test: TestCase, result: TestResult): void;
|
|
17
33
|
onTestEnd(test: TestCase, result: TestResult): void;
|
|
18
34
|
onEnd(result: FullResult): void;
|
|
19
|
-
generateHTML(): string;
|
|
35
|
+
generateHTML(filteredResults: TestResultData[], totalDuration: string): string;
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
export { OrtoniReport as default };
|
package/dist/ortoni-report.js
CHANGED
|
@@ -38,25 +38,39 @@ var import_path2 = __toESM(require("path"));
|
|
|
38
38
|
var import_handlebars = __toESM(require("handlebars"));
|
|
39
39
|
var import_safe = __toESM(require("colors/safe"));
|
|
40
40
|
|
|
41
|
-
// src/utils/
|
|
41
|
+
// src/utils/utils.ts
|
|
42
42
|
var import_path = __toESM(require("path"));
|
|
43
43
|
function msToTime(duration) {
|
|
44
|
+
const milliseconds = Math.floor(duration % 1e3);
|
|
44
45
|
const seconds = Math.floor(duration / 1e3 % 60);
|
|
45
46
|
const minutes = Math.floor(duration / (1e3 * 60) % 60);
|
|
46
47
|
const hours = Math.floor(duration / (1e3 * 60 * 60) % 24);
|
|
47
|
-
|
|
48
|
-
if (hours > 0)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
let result = "";
|
|
49
|
+
if (hours > 0) {
|
|
50
|
+
result += (hours < 10 ? "0" + hours : hours) + "h:";
|
|
51
|
+
}
|
|
52
|
+
if (minutes > 0 || hours > 0) {
|
|
53
|
+
result += (minutes < 10 ? "0" + minutes : minutes) + "m:";
|
|
54
|
+
}
|
|
55
|
+
if (seconds > 0 || minutes > 0 || hours > 0) {
|
|
56
|
+
result += (seconds < 10 ? "0" + seconds : seconds) + "s";
|
|
57
|
+
}
|
|
58
|
+
if (milliseconds > 0) {
|
|
59
|
+
result += ":" + (milliseconds < 100 ? "0" + milliseconds : milliseconds) + "ms";
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
55
62
|
}
|
|
56
63
|
function normalizeFilePath(filePath) {
|
|
57
64
|
const normalizedPath = import_path.default.normalize(filePath);
|
|
58
65
|
return import_path.default.basename(normalizedPath);
|
|
59
66
|
}
|
|
67
|
+
function formatDate(date) {
|
|
68
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
69
|
+
const month = date.toLocaleString("default", { month: "short" });
|
|
70
|
+
const year = date.getFullYear();
|
|
71
|
+
const time = date.toLocaleTimeString();
|
|
72
|
+
return `${day}-${month}-${year} ${time}`;
|
|
73
|
+
}
|
|
60
74
|
|
|
61
75
|
// src/ortoni-report.ts
|
|
62
76
|
var OrtoniReport = class {
|
|
@@ -75,22 +89,30 @@ var OrtoniReport = class {
|
|
|
75
89
|
onTestBegin(test, result) {
|
|
76
90
|
}
|
|
77
91
|
onTestEnd(test, result) {
|
|
92
|
+
let status = result.status;
|
|
93
|
+
if (test.outcome() === "flaky") {
|
|
94
|
+
status = "flaky";
|
|
95
|
+
}
|
|
78
96
|
const testResult = {
|
|
79
|
-
|
|
97
|
+
retry: result.retry > 0 ? "retry" : "",
|
|
98
|
+
isRetry: result.retry,
|
|
80
99
|
projectName: test.titlePath()[1],
|
|
81
|
-
// Get the project name
|
|
82
100
|
suite: test.titlePath()[3],
|
|
83
|
-
// Adjust the index based on your suite hierarchy
|
|
84
101
|
title: test.title,
|
|
85
|
-
status
|
|
102
|
+
status,
|
|
86
103
|
flaky: test.outcome(),
|
|
87
104
|
duration: msToTime(result.duration),
|
|
88
105
|
errors: result.errors.map((e) => import_safe.default.strip(e.message || e.toString())),
|
|
89
106
|
steps: result.steps.map((step) => ({
|
|
90
|
-
|
|
107
|
+
titlePath: step.titlePath,
|
|
91
108
|
category: step.category,
|
|
92
109
|
duration: step.duration,
|
|
93
|
-
|
|
110
|
+
error: step.error,
|
|
111
|
+
location: step.location,
|
|
112
|
+
parent: step.parent,
|
|
113
|
+
startTime: step.startTime,
|
|
114
|
+
steps: step.steps,
|
|
115
|
+
title: step.title
|
|
94
116
|
})),
|
|
95
117
|
logs: import_safe.default.strip(result.stdout.concat(result.stderr).map((log) => log).join("\n")),
|
|
96
118
|
screenshotPath: null,
|
|
@@ -112,7 +134,8 @@ var OrtoniReport = class {
|
|
|
112
134
|
this.results.push(testResult);
|
|
113
135
|
}
|
|
114
136
|
onEnd(result) {
|
|
115
|
-
this.results
|
|
137
|
+
const filteredResults = this.results.filter((r) => r.status !== "skipped" && !r.isRetry);
|
|
138
|
+
const totalDuration = msToTime(result.duration);
|
|
116
139
|
this.groupedResults = this.results.reduce((acc, result2, index) => {
|
|
117
140
|
const filePath = result2.filePath;
|
|
118
141
|
const suiteName = result2.suite;
|
|
@@ -132,30 +155,51 @@ var OrtoniReport = class {
|
|
|
132
155
|
import_handlebars.default.registerHelper("json", function(context) {
|
|
133
156
|
return safeStringify(context);
|
|
134
157
|
});
|
|
135
|
-
|
|
158
|
+
import_handlebars.default.registerHelper("eq", function(actualStatus, expectedStatus) {
|
|
159
|
+
return actualStatus === expectedStatus;
|
|
160
|
+
});
|
|
161
|
+
import_handlebars.default.registerHelper("or", () => {
|
|
162
|
+
var args = Array.prototype.slice.call(arguments);
|
|
163
|
+
var options = args.pop();
|
|
164
|
+
for (var i = 0; i < args.length; i++) {
|
|
165
|
+
if (args[i]) {
|
|
166
|
+
return options.fn(this);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return options.inverse(this);
|
|
170
|
+
});
|
|
171
|
+
import_handlebars.default.registerHelper("gt", function(a, b) {
|
|
172
|
+
return a > b;
|
|
173
|
+
});
|
|
174
|
+
const html = this.generateHTML(filteredResults, totalDuration);
|
|
136
175
|
const outputPath = import_path2.default.resolve(process.cwd(), "ortoni-report.html");
|
|
137
176
|
import_fs.default.writeFileSync(outputPath, html);
|
|
138
177
|
console.log(`Ortoni HTML report generated at ${outputPath}`);
|
|
139
178
|
}
|
|
140
|
-
generateHTML() {
|
|
179
|
+
generateHTML(filteredResults, totalDuration) {
|
|
180
|
+
const totalTests = filteredResults.length;
|
|
181
|
+
const passedTests = this.results.filter((r) => r.status === "passed").length;
|
|
182
|
+
const flakyTests = this.results.filter((r) => r.flaky === "flaky").length;
|
|
183
|
+
const failed = filteredResults.filter((r) => r.status === "failed" || r.status === "timedOut").length;
|
|
184
|
+
const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
|
|
141
185
|
const templateSource = import_fs.default.readFileSync(import_path2.default.resolve(__dirname, "report-template.hbs"), "utf-8");
|
|
142
186
|
const template = import_handlebars.default.compile(templateSource);
|
|
143
187
|
const data = {
|
|
144
|
-
totalDuration
|
|
188
|
+
totalDuration,
|
|
145
189
|
suiteName: this.suiteName,
|
|
146
190
|
results: this.results,
|
|
147
|
-
|
|
148
|
-
|
|
191
|
+
retryCount: this.results.filter((r) => r.isRetry).length,
|
|
192
|
+
passCount: passedTests,
|
|
193
|
+
failCount: failed,
|
|
149
194
|
skipCount: this.results.filter((r) => r.status === "skipped").length,
|
|
150
|
-
flakyCount:
|
|
151
|
-
totalCount:
|
|
195
|
+
flakyCount: flakyTests,
|
|
196
|
+
totalCount: filteredResults.length,
|
|
152
197
|
groupedResults: this.groupedResults,
|
|
153
198
|
projectName: this.config.projectName,
|
|
154
|
-
// Include project name
|
|
155
199
|
authorName: this.config.authorName,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
200
|
+
testType: this.config.testType,
|
|
201
|
+
successRate,
|
|
202
|
+
lastRunDate: formatDate(/* @__PURE__ */ new Date())
|
|
159
203
|
};
|
|
160
204
|
return template(data);
|
|
161
205
|
}
|
package/dist/ortoni-report.mjs
CHANGED
|
@@ -4,25 +4,39 @@ import path2 from "path";
|
|
|
4
4
|
import Handlebars from "handlebars";
|
|
5
5
|
import colors from "colors/safe";
|
|
6
6
|
|
|
7
|
-
// src/utils/
|
|
7
|
+
// src/utils/utils.ts
|
|
8
8
|
import path from "path";
|
|
9
9
|
function msToTime(duration) {
|
|
10
|
+
const milliseconds = Math.floor(duration % 1e3);
|
|
10
11
|
const seconds = Math.floor(duration / 1e3 % 60);
|
|
11
12
|
const minutes = Math.floor(duration / (1e3 * 60) % 60);
|
|
12
13
|
const hours = Math.floor(duration / (1e3 * 60 * 60) % 24);
|
|
13
|
-
|
|
14
|
-
if (hours > 0)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
let result = "";
|
|
15
|
+
if (hours > 0) {
|
|
16
|
+
result += (hours < 10 ? "0" + hours : hours) + "h:";
|
|
17
|
+
}
|
|
18
|
+
if (minutes > 0 || hours > 0) {
|
|
19
|
+
result += (minutes < 10 ? "0" + minutes : minutes) + "m:";
|
|
20
|
+
}
|
|
21
|
+
if (seconds > 0 || minutes > 0 || hours > 0) {
|
|
22
|
+
result += (seconds < 10 ? "0" + seconds : seconds) + "s";
|
|
23
|
+
}
|
|
24
|
+
if (milliseconds > 0) {
|
|
25
|
+
result += ":" + (milliseconds < 100 ? "0" + milliseconds : milliseconds) + "ms";
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
21
28
|
}
|
|
22
29
|
function normalizeFilePath(filePath) {
|
|
23
30
|
const normalizedPath = path.normalize(filePath);
|
|
24
31
|
return path.basename(normalizedPath);
|
|
25
32
|
}
|
|
33
|
+
function formatDate(date) {
|
|
34
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
35
|
+
const month = date.toLocaleString("default", { month: "short" });
|
|
36
|
+
const year = date.getFullYear();
|
|
37
|
+
const time = date.toLocaleTimeString();
|
|
38
|
+
return `${day}-${month}-${year} ${time}`;
|
|
39
|
+
}
|
|
26
40
|
|
|
27
41
|
// src/ortoni-report.ts
|
|
28
42
|
var OrtoniReport = class {
|
|
@@ -41,22 +55,30 @@ var OrtoniReport = class {
|
|
|
41
55
|
onTestBegin(test, result) {
|
|
42
56
|
}
|
|
43
57
|
onTestEnd(test, result) {
|
|
58
|
+
let status = result.status;
|
|
59
|
+
if (test.outcome() === "flaky") {
|
|
60
|
+
status = "flaky";
|
|
61
|
+
}
|
|
44
62
|
const testResult = {
|
|
45
|
-
|
|
63
|
+
retry: result.retry > 0 ? "retry" : "",
|
|
64
|
+
isRetry: result.retry,
|
|
46
65
|
projectName: test.titlePath()[1],
|
|
47
|
-
// Get the project name
|
|
48
66
|
suite: test.titlePath()[3],
|
|
49
|
-
// Adjust the index based on your suite hierarchy
|
|
50
67
|
title: test.title,
|
|
51
|
-
status
|
|
68
|
+
status,
|
|
52
69
|
flaky: test.outcome(),
|
|
53
70
|
duration: msToTime(result.duration),
|
|
54
71
|
errors: result.errors.map((e) => colors.strip(e.message || e.toString())),
|
|
55
72
|
steps: result.steps.map((step) => ({
|
|
56
|
-
|
|
73
|
+
titlePath: step.titlePath,
|
|
57
74
|
category: step.category,
|
|
58
75
|
duration: step.duration,
|
|
59
|
-
|
|
76
|
+
error: step.error,
|
|
77
|
+
location: step.location,
|
|
78
|
+
parent: step.parent,
|
|
79
|
+
startTime: step.startTime,
|
|
80
|
+
steps: step.steps,
|
|
81
|
+
title: step.title
|
|
60
82
|
})),
|
|
61
83
|
logs: colors.strip(result.stdout.concat(result.stderr).map((log) => log).join("\n")),
|
|
62
84
|
screenshotPath: null,
|
|
@@ -78,7 +100,8 @@ var OrtoniReport = class {
|
|
|
78
100
|
this.results.push(testResult);
|
|
79
101
|
}
|
|
80
102
|
onEnd(result) {
|
|
81
|
-
this.results
|
|
103
|
+
const filteredResults = this.results.filter((r) => r.status !== "skipped" && !r.isRetry);
|
|
104
|
+
const totalDuration = msToTime(result.duration);
|
|
82
105
|
this.groupedResults = this.results.reduce((acc, result2, index) => {
|
|
83
106
|
const filePath = result2.filePath;
|
|
84
107
|
const suiteName = result2.suite;
|
|
@@ -98,30 +121,51 @@ var OrtoniReport = class {
|
|
|
98
121
|
Handlebars.registerHelper("json", function(context) {
|
|
99
122
|
return safeStringify(context);
|
|
100
123
|
});
|
|
101
|
-
|
|
124
|
+
Handlebars.registerHelper("eq", function(actualStatus, expectedStatus) {
|
|
125
|
+
return actualStatus === expectedStatus;
|
|
126
|
+
});
|
|
127
|
+
Handlebars.registerHelper("or", () => {
|
|
128
|
+
var args = Array.prototype.slice.call(arguments);
|
|
129
|
+
var options = args.pop();
|
|
130
|
+
for (var i = 0; i < args.length; i++) {
|
|
131
|
+
if (args[i]) {
|
|
132
|
+
return options.fn(this);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return options.inverse(this);
|
|
136
|
+
});
|
|
137
|
+
Handlebars.registerHelper("gt", function(a, b) {
|
|
138
|
+
return a > b;
|
|
139
|
+
});
|
|
140
|
+
const html = this.generateHTML(filteredResults, totalDuration);
|
|
102
141
|
const outputPath = path2.resolve(process.cwd(), "ortoni-report.html");
|
|
103
142
|
fs.writeFileSync(outputPath, html);
|
|
104
143
|
console.log(`Ortoni HTML report generated at ${outputPath}`);
|
|
105
144
|
}
|
|
106
|
-
generateHTML() {
|
|
145
|
+
generateHTML(filteredResults, totalDuration) {
|
|
146
|
+
const totalTests = filteredResults.length;
|
|
147
|
+
const passedTests = this.results.filter((r) => r.status === "passed").length;
|
|
148
|
+
const flakyTests = this.results.filter((r) => r.flaky === "flaky").length;
|
|
149
|
+
const failed = filteredResults.filter((r) => r.status === "failed" || r.status === "timedOut").length;
|
|
150
|
+
const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
|
|
107
151
|
const templateSource = fs.readFileSync(path2.resolve(__dirname, "report-template.hbs"), "utf-8");
|
|
108
152
|
const template = Handlebars.compile(templateSource);
|
|
109
153
|
const data = {
|
|
110
|
-
totalDuration
|
|
154
|
+
totalDuration,
|
|
111
155
|
suiteName: this.suiteName,
|
|
112
156
|
results: this.results,
|
|
113
|
-
|
|
114
|
-
|
|
157
|
+
retryCount: this.results.filter((r) => r.isRetry).length,
|
|
158
|
+
passCount: passedTests,
|
|
159
|
+
failCount: failed,
|
|
115
160
|
skipCount: this.results.filter((r) => r.status === "skipped").length,
|
|
116
|
-
flakyCount:
|
|
117
|
-
totalCount:
|
|
161
|
+
flakyCount: flakyTests,
|
|
162
|
+
totalCount: filteredResults.length,
|
|
118
163
|
groupedResults: this.groupedResults,
|
|
119
164
|
projectName: this.config.projectName,
|
|
120
|
-
// Include project name
|
|
121
165
|
authorName: this.config.authorName,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
166
|
+
testType: this.config.testType,
|
|
167
|
+
successRate,
|
|
168
|
+
lastRunDate: formatDate(/* @__PURE__ */ new Date())
|
|
125
169
|
};
|
|
126
170
|
return template(data);
|
|
127
171
|
}
|