ortoni-report 1.1.5 → 1.1.7
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 +24 -0
- package/dist/css/main.css +22445 -22445
- package/dist/ortoni-report.d.ts +50 -4
- package/dist/ortoni-report.js +149 -118
- package/dist/ortoni-report.mjs +149 -118
- package/dist/report-template.hbs +311 -255
- package/dist/utils/modal.js +10 -3
- package/package.json +3 -2
- package/readme.md +64 -116
package/dist/ortoni-report.mjs
CHANGED
|
@@ -39,147 +39,178 @@ function formatDate(date) {
|
|
|
39
39
|
const time = date.toLocaleTimeString();
|
|
40
40
|
return `${day}-${month}-${year} ${time}`;
|
|
41
41
|
}
|
|
42
|
+
function safeStringify(obj, indent = 2) {
|
|
43
|
+
const cache = /* @__PURE__ */ new Set();
|
|
44
|
+
const json = JSON.stringify(obj, (key, value) => {
|
|
45
|
+
if (typeof value === "object" && value !== null) {
|
|
46
|
+
if (cache.has(value)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
cache.add(value);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}, indent);
|
|
53
|
+
cache.clear();
|
|
54
|
+
return json;
|
|
55
|
+
}
|
|
42
56
|
|
|
43
57
|
// src/ortoni-report.ts
|
|
44
58
|
var OrtoniReport = class {
|
|
45
59
|
constructor(config = {}) {
|
|
60
|
+
this.projectRoot = "";
|
|
46
61
|
this.results = [];
|
|
47
62
|
this.projectSet = /* @__PURE__ */ new Set();
|
|
63
|
+
this.tagsSet = /* @__PURE__ */ new Set();
|
|
48
64
|
this.config = config;
|
|
49
65
|
}
|
|
50
66
|
onBegin(config, suite) {
|
|
51
67
|
this.results = [];
|
|
68
|
+
this.projectRoot = config.rootDir;
|
|
52
69
|
}
|
|
53
70
|
onTestBegin(test, result) {
|
|
54
71
|
}
|
|
55
72
|
onTestEnd(test, result) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
73
|
+
try {
|
|
74
|
+
let status = result.status;
|
|
75
|
+
if (test.outcome() === "flaky") {
|
|
76
|
+
status = "flaky";
|
|
77
|
+
}
|
|
78
|
+
const projectName = test.titlePath()[1];
|
|
79
|
+
this.projectSet.add(projectName);
|
|
80
|
+
const location = test.location;
|
|
81
|
+
const filePath = normalizeFilePath(test.titlePath()[2]);
|
|
82
|
+
const tagPattern = /@[\w]+/g;
|
|
83
|
+
const testTags = test.title.match(tagPattern) || [];
|
|
84
|
+
const title = test.title.replace(tagPattern, "").trim();
|
|
85
|
+
const suiteTags = test.titlePath()[3].match(tagPattern) || [];
|
|
86
|
+
const suite = test.titlePath()[3].replace(tagPattern, "").trim();
|
|
87
|
+
const testResult = {
|
|
88
|
+
suiteTags,
|
|
89
|
+
testTags,
|
|
90
|
+
location: `${filePath}:${location.line}:${location.column}`,
|
|
91
|
+
retry: result.retry > 0 ? "retry" : "",
|
|
92
|
+
isRetry: result.retry,
|
|
93
|
+
projectName,
|
|
94
|
+
suite,
|
|
95
|
+
title,
|
|
96
|
+
status,
|
|
97
|
+
flaky: test.outcome(),
|
|
98
|
+
duration: msToTime(result.duration),
|
|
99
|
+
errors: result.errors.map((e) => colors.strip(e.message || e.toString())),
|
|
100
|
+
steps: result.steps.map((step) => {
|
|
101
|
+
const location2 = step.location ? `${path2.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
|
|
102
|
+
return {
|
|
103
|
+
snippet: colors.strip(step.error?.snippet || ""),
|
|
104
|
+
title: step.title,
|
|
105
|
+
location: step.error ? location2 : ""
|
|
106
|
+
};
|
|
107
|
+
}),
|
|
108
|
+
logs: colors.strip(result.stdout.concat(result.stderr).map((log) => log).join("\n")),
|
|
109
|
+
filePath,
|
|
110
|
+
projects: this.projectSet,
|
|
111
|
+
base64Image: this.config.base64Image
|
|
112
|
+
};
|
|
113
|
+
if (result.attachments) {
|
|
114
|
+
const screenshot = result.attachments.find((attachment) => attachment.name === "screenshot");
|
|
115
|
+
if (this.config.base64Image) {
|
|
116
|
+
if (screenshot && screenshot.path) {
|
|
117
|
+
try {
|
|
118
|
+
const screenshotContent = fs.readFileSync(screenshot.path, "base64");
|
|
119
|
+
testResult.screenshotPath = `data:image/png;base64,${screenshotContent}`;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`OrtoniReport: Failed to read screenshot file: ${screenshot.path}`, error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
if (screenshot && screenshot.path) {
|
|
126
|
+
testResult.screenshotPath = path2.resolve(screenshot.path);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const tracePath = result.attachments.find((attachment) => attachment.name === "trace");
|
|
130
|
+
if (tracePath?.path) {
|
|
131
|
+
testResult.tracePath = path2.resolve(__dirname, tracePath.path);
|
|
132
|
+
}
|
|
133
|
+
const videoPath = result.attachments.find((attachment) => attachment.name === "video");
|
|
134
|
+
if (videoPath?.path) {
|
|
135
|
+
testResult.videoPath = path2.resolve(__dirname, videoPath.path);
|
|
136
|
+
}
|
|
92
137
|
}
|
|
138
|
+
this.results.push(testResult);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("OrtoniReport: Error processing test end:", error);
|
|
93
141
|
}
|
|
94
|
-
this.results.push(testResult);
|
|
95
142
|
}
|
|
96
143
|
onEnd(result) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
acc[filePath]
|
|
105
|
-
|
|
106
|
-
if (!acc[filePath][suiteName]) {
|
|
107
|
-
acc[filePath][suiteName] = {};
|
|
108
|
-
}
|
|
109
|
-
if (!acc[filePath][suiteName][projectName]) {
|
|
110
|
-
acc[filePath][suiteName][projectName] = [];
|
|
111
|
-
}
|
|
112
|
-
acc[filePath][suiteName][projectName].push({ ...result2, index });
|
|
113
|
-
return acc;
|
|
114
|
-
}, {});
|
|
115
|
-
Handlebars.registerHelper("json", function(context) {
|
|
116
|
-
return safeStringify(context);
|
|
117
|
-
});
|
|
118
|
-
Handlebars.registerHelper("eq", function(actualStatus, expectedStatus) {
|
|
119
|
-
return actualStatus === expectedStatus;
|
|
120
|
-
});
|
|
121
|
-
Handlebars.registerHelper("or", () => {
|
|
122
|
-
var args = Array.prototype.slice.call(arguments);
|
|
123
|
-
var options = args.pop();
|
|
124
|
-
for (var i = 0; i < args.length; i++) {
|
|
125
|
-
if (args[i]) {
|
|
126
|
-
return options.fn(this);
|
|
144
|
+
try {
|
|
145
|
+
const filteredResults = this.results.filter((r) => r.status !== "skipped" && !r.isRetry);
|
|
146
|
+
const totalDuration = msToTime(result.duration);
|
|
147
|
+
this.groupedResults = this.results.reduce((acc, result2, index) => {
|
|
148
|
+
const filePath = result2.filePath;
|
|
149
|
+
const suiteName = result2.suite;
|
|
150
|
+
const projectName = result2.projectName;
|
|
151
|
+
if (!acc[filePath]) {
|
|
152
|
+
acc[filePath] = {};
|
|
127
153
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
if (!acc[filePath][suiteName]) {
|
|
155
|
+
acc[filePath][suiteName] = {};
|
|
156
|
+
}
|
|
157
|
+
if (!acc[filePath][suiteName][projectName]) {
|
|
158
|
+
acc[filePath][suiteName][projectName] = [];
|
|
159
|
+
}
|
|
160
|
+
acc[filePath][suiteName][projectName].push({ ...result2, index });
|
|
161
|
+
return acc;
|
|
162
|
+
}, {});
|
|
163
|
+
Handlebars.registerHelper("json", function(context) {
|
|
164
|
+
return safeStringify(context);
|
|
165
|
+
});
|
|
166
|
+
Handlebars.registerHelper("eq", function(actualStatus, expectedStatus) {
|
|
167
|
+
return actualStatus === expectedStatus;
|
|
168
|
+
});
|
|
169
|
+
const html = this.generateHTML(filteredResults, totalDuration);
|
|
170
|
+
const outputPath = path2.resolve(process.cwd(), "ortoni-report.html");
|
|
171
|
+
fs.writeFileSync(outputPath, html);
|
|
172
|
+
console.log(`Ortoni HTML report generated at ${outputPath}`);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error("OrtoniReport: Error generating report:", error);
|
|
175
|
+
}
|
|
138
176
|
}
|
|
139
177
|
generateHTML(filteredResults, totalDuration) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
178
|
+
try {
|
|
179
|
+
const totalTests = filteredResults.length;
|
|
180
|
+
const passedTests = this.results.filter((r) => r.status === "passed").length;
|
|
181
|
+
const flakyTests = this.results.filter((r) => r.flaky === "flaky").length;
|
|
182
|
+
const failed = filteredResults.filter((r) => r.status === "failed" || r.status === "timedOut").length;
|
|
183
|
+
const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
|
|
184
|
+
const templateSource = fs.readFileSync(path2.resolve(__dirname, "report-template.hbs"), "utf-8");
|
|
185
|
+
const template = Handlebars.compile(templateSource);
|
|
186
|
+
const logo = this.config.logo;
|
|
187
|
+
const data = {
|
|
188
|
+
logo: logo ? path2.resolve(logo) : void 0,
|
|
189
|
+
totalDuration,
|
|
190
|
+
suiteName: this.suiteName,
|
|
191
|
+
results: this.results,
|
|
192
|
+
retryCount: this.results.filter((r) => r.isRetry).length,
|
|
193
|
+
passCount: passedTests,
|
|
194
|
+
failCount: failed,
|
|
195
|
+
skipCount: this.results.filter((r) => r.status === "skipped").length,
|
|
196
|
+
flakyCount: flakyTests,
|
|
197
|
+
totalCount: filteredResults.length,
|
|
198
|
+
groupedResults: this.groupedResults,
|
|
199
|
+
projectName: this.config.projectName,
|
|
200
|
+
authorName: this.config.authorName,
|
|
201
|
+
testType: this.config.testType,
|
|
202
|
+
preferredTheme: this.config.preferredTheme,
|
|
203
|
+
successRate,
|
|
204
|
+
lastRunDate: formatDate(/* @__PURE__ */ new Date()),
|
|
205
|
+
projects: this.projectSet
|
|
206
|
+
};
|
|
207
|
+
return template(data);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error("OrtoniReport: Error generating HTML:", error);
|
|
210
|
+
return `<html><body><h1>Report generation failed</h1><pre>${error.stack}</pre></body></html>`;
|
|
211
|
+
}
|
|
167
212
|
}
|
|
168
213
|
};
|
|
169
|
-
function safeStringify(obj, indent = 2) {
|
|
170
|
-
const cache = /* @__PURE__ */ new Set();
|
|
171
|
-
const json = JSON.stringify(obj, (key, value) => {
|
|
172
|
-
if (typeof value === "object" && value !== null) {
|
|
173
|
-
if (cache.has(value)) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
cache.add(value);
|
|
177
|
-
}
|
|
178
|
-
return value;
|
|
179
|
-
}, indent);
|
|
180
|
-
cache.clear();
|
|
181
|
-
return json;
|
|
182
|
-
}
|
|
183
214
|
export {
|
|
184
215
|
OrtoniReport as default
|
|
185
216
|
};
|