ortoni-report 3.0.5 → 4.0.1-beta.0
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 +30 -0
- package/dist/chunk-45EJSEX2.mjs +632 -0
- package/dist/chunk-75EAJL2U.mjs +632 -0
- package/dist/chunk-FGIYOFIC.mjs +632 -0
- package/dist/chunk-FHKWBHU6.mjs +633 -0
- package/dist/chunk-GLICR3VS.mjs +637 -0
- package/dist/chunk-HFO6XSKC.mjs +633 -0
- package/dist/chunk-HOZD6YIV.mjs +634 -0
- package/dist/chunk-IJO2YIFE.mjs +637 -0
- package/dist/chunk-INS3E7E6.mjs +638 -0
- package/dist/chunk-JEIWNUQY.mjs +632 -0
- package/dist/chunk-JPLAGYR7.mjs +632 -0
- package/dist/chunk-MPZLDOCN.mjs +631 -0
- package/dist/chunk-NM6ULN2O.mjs +632 -0
- package/dist/chunk-OZS6QIJS.mjs +638 -0
- package/dist/chunk-P57227VN.mjs +633 -0
- package/dist/chunk-QMTRYN5N.js +635 -0
- package/dist/chunk-TI33PMMQ.mjs +639 -0
- package/dist/chunk-Z5NBP5TS.mjs +635 -0
- package/dist/cli/cli.cjs +678 -0
- package/dist/cli/cli.d.cts +1 -0
- package/dist/cli/cli.js +609 -18
- package/dist/cli/cli.mjs +89 -9
- package/dist/index.html +21 -0
- package/dist/ortoni-report.cjs +2134 -0
- package/dist/ortoni-report.d.cts +111 -0
- package/dist/ortoni-report.d.mts +3 -12
- package/dist/ortoni-report.d.ts +3 -12
- package/dist/ortoni-report.js +201 -326
- package/dist/ortoni-report.mjs +78 -746
- package/package.json +4 -5
- package/readme.md +26 -33
- package/dist/chunk-AY2PKDHU.mjs +0 -69
- package/dist/chunk-OOALU4XG.mjs +0 -72
- package/dist/chunk-ZSIRUQUA.mjs +0 -68
- package/dist/style/main.css +0 -80
- package/dist/views/analytics.hbs +0 -103
- package/dist/views/head.hbs +0 -11
- package/dist/views/main.hbs +0 -1295
- package/dist/views/project.hbs +0 -238
- package/dist/views/sidebar.hbs +0 -244
- package/dist/views/summaryCard.hbs +0 -15
- package/dist/views/testIcons.hbs +0 -13
- package/dist/views/testPanel.hbs +0 -45
- package/dist/views/testStatus.hbs +0 -9
- package/dist/views/userInfo.hbs +0 -260
package/dist/ortoni-report.js
CHANGED
|
@@ -54,17 +54,23 @@ var FileManager = class {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
writeReportFile(filename,
|
|
57
|
+
writeReportFile(filename, data) {
|
|
58
|
+
const templatePath = import_path.default.join(__dirname, "..", "index.html");
|
|
59
|
+
let html = import_fs.default.readFileSync(templatePath, "utf-8");
|
|
60
|
+
const reportJSON = JSON.stringify({
|
|
61
|
+
data
|
|
62
|
+
});
|
|
63
|
+
html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
|
|
64
|
+
import_fs.default.writeFileSync(filename, html);
|
|
65
|
+
return filename;
|
|
66
|
+
}
|
|
67
|
+
writeRawFile(filename, data) {
|
|
58
68
|
const outputPath = import_path.default.join(process.cwd(), this.folderPath, filename);
|
|
59
|
-
import_fs.default.
|
|
69
|
+
import_fs.default.mkdirSync(import_path.default.dirname(outputPath), { recursive: true });
|
|
70
|
+
const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
71
|
+
import_fs.default.writeFileSync(outputPath, content, "utf-8");
|
|
60
72
|
return outputPath;
|
|
61
73
|
}
|
|
62
|
-
readCssContent() {
|
|
63
|
-
return import_fs.default.readFileSync(
|
|
64
|
-
import_path.default.resolve(__dirname, "style", "main.css"),
|
|
65
|
-
"utf-8"
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
74
|
copyTraceViewerAssets(skip) {
|
|
69
75
|
if (skip) return;
|
|
70
76
|
const traceViewerFolder = import_path.default.join(
|
|
@@ -99,157 +105,48 @@ var FileManager = class {
|
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
|
|
102
|
-
// src/helpers/HTMLGenerator.ts
|
|
103
|
-
var import_path3 = __toESM(require("path"));
|
|
104
|
-
|
|
105
108
|
// src/utils/groupProjects.ts
|
|
106
109
|
function groupResults(config, results) {
|
|
107
110
|
if (config.showProject) {
|
|
108
111
|
const groupedResults = results.reduce((acc, result, index) => {
|
|
109
112
|
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
113
|
+
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
110
114
|
const { filePath, suite, projectName } = result;
|
|
111
115
|
acc[filePath] = acc[filePath] || {};
|
|
112
116
|
acc[filePath][suite] = acc[filePath][suite] || {};
|
|
113
117
|
acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
|
|
114
|
-
acc[filePath][suite][projectName].push({ ...result, index, testId });
|
|
118
|
+
acc[filePath][suite][projectName].push({ ...result, index, testId, key });
|
|
115
119
|
return acc;
|
|
116
120
|
}, {});
|
|
117
121
|
return groupedResults;
|
|
118
122
|
} else {
|
|
119
123
|
const groupedResults = results.reduce((acc, result, index) => {
|
|
120
124
|
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
125
|
+
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
121
126
|
const { filePath, suite } = result;
|
|
122
127
|
acc[filePath] = acc[filePath] || {};
|
|
123
128
|
acc[filePath][suite] = acc[filePath][suite] || [];
|
|
124
|
-
acc[filePath][suite].push({ ...result, index, testId });
|
|
129
|
+
acc[filePath][suite].push({ ...result, index, testId, key });
|
|
125
130
|
return acc;
|
|
126
131
|
}, {});
|
|
127
132
|
return groupedResults;
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
// src/utils/utils.ts
|
|
132
|
-
var import_path2 = __toESM(require("path"));
|
|
133
|
-
function msToTime(duration) {
|
|
134
|
-
const milliseconds = Math.floor(duration % 1e3);
|
|
135
|
-
const seconds = Math.floor(duration / 1e3 % 60);
|
|
136
|
-
const minutes = Math.floor(duration / (1e3 * 60) % 60);
|
|
137
|
-
const hours = Math.floor(duration / (1e3 * 60 * 60) % 24);
|
|
138
|
-
let result = "";
|
|
139
|
-
if (hours > 0) {
|
|
140
|
-
result += `${hours}h:`;
|
|
141
|
-
}
|
|
142
|
-
if (minutes > 0 || hours > 0) {
|
|
143
|
-
result += `${minutes < 10 ? "0" + minutes : minutes}m:`;
|
|
144
|
-
}
|
|
145
|
-
if (seconds > 0 || minutes > 0 || hours > 0) {
|
|
146
|
-
result += `${seconds < 10 ? "0" + seconds : seconds}s`;
|
|
147
|
-
}
|
|
148
|
-
if (milliseconds > 0 && !(seconds > 0 || minutes > 0 || hours > 0)) {
|
|
149
|
-
result += `${milliseconds}ms`;
|
|
150
|
-
} else if (milliseconds > 0) {
|
|
151
|
-
result += `:${milliseconds < 100 ? "0" + milliseconds : milliseconds}ms`;
|
|
152
|
-
}
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
|
-
function normalizeFilePath(filePath) {
|
|
156
|
-
const normalizedPath = import_path2.default.normalize(filePath);
|
|
157
|
-
return import_path2.default.basename(normalizedPath);
|
|
158
|
-
}
|
|
159
|
-
function formatDate(date) {
|
|
160
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
161
|
-
const month = date.toLocaleString("default", { month: "short" });
|
|
162
|
-
const year = date.getFullYear();
|
|
163
|
-
const time = date.toLocaleTimeString();
|
|
164
|
-
return `${day}-${month}-${year} ${time}`;
|
|
165
|
-
}
|
|
166
|
-
function safeStringify(obj, indent = 2) {
|
|
167
|
-
const cache = /* @__PURE__ */ new Set();
|
|
168
|
-
const json = JSON.stringify(
|
|
169
|
-
obj,
|
|
170
|
-
(key, value) => {
|
|
171
|
-
if (typeof value === "object" && value !== null) {
|
|
172
|
-
if (cache.has(value)) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
cache.add(value);
|
|
176
|
-
}
|
|
177
|
-
return value;
|
|
178
|
-
},
|
|
179
|
-
indent
|
|
180
|
-
);
|
|
181
|
-
cache.clear();
|
|
182
|
-
return json;
|
|
183
|
-
}
|
|
184
|
-
function ensureHtmlExtension(filename) {
|
|
185
|
-
const ext = import_path2.default.extname(filename);
|
|
186
|
-
if (ext && ext.toLowerCase() === ".html") {
|
|
187
|
-
return filename;
|
|
188
|
-
}
|
|
189
|
-
return `${filename}.html`;
|
|
190
|
-
}
|
|
191
|
-
function escapeHtml(unsafe) {
|
|
192
|
-
if (typeof unsafe !== "string") {
|
|
193
|
-
return String(unsafe);
|
|
194
|
-
}
|
|
195
|
-
return unsafe.replace(/[&<"']/g, function(match) {
|
|
196
|
-
const escapeMap = {
|
|
197
|
-
"&": "&",
|
|
198
|
-
"<": "<",
|
|
199
|
-
">": ">",
|
|
200
|
-
'"': """,
|
|
201
|
-
"'": "'"
|
|
202
|
-
};
|
|
203
|
-
return escapeMap[match] || match;
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
function formatDateUTC(date) {
|
|
207
|
-
return date.toISOString();
|
|
208
|
-
}
|
|
209
|
-
function formatDateLocal(isoString) {
|
|
210
|
-
const date = new Date(isoString);
|
|
211
|
-
const options = {
|
|
212
|
-
year: "numeric",
|
|
213
|
-
month: "short",
|
|
214
|
-
day: "2-digit",
|
|
215
|
-
hour: "2-digit",
|
|
216
|
-
minute: "2-digit",
|
|
217
|
-
hour12: true,
|
|
218
|
-
timeZoneName: "shortOffset"
|
|
219
|
-
};
|
|
220
|
-
return new Intl.DateTimeFormat(void 0, options).format(date);
|
|
221
|
-
}
|
|
222
|
-
function formatDateNoTimezone(isoString) {
|
|
223
|
-
const date = new Date(isoString);
|
|
224
|
-
return date.toLocaleString("en-US", {
|
|
225
|
-
dateStyle: "medium",
|
|
226
|
-
timeStyle: "short"
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
136
|
// src/helpers/HTMLGenerator.ts
|
|
231
|
-
var import_fs2 = __toESM(require("fs"));
|
|
232
|
-
var import_handlebars = __toESM(require("handlebars"));
|
|
233
137
|
var HTMLGenerator = class {
|
|
234
138
|
constructor(ortoniConfig, dbManager) {
|
|
235
139
|
this.ortoniConfig = ortoniConfig;
|
|
236
|
-
this.registerHandlebarsHelpers();
|
|
237
|
-
this.registerPartials();
|
|
238
140
|
this.dbManager = dbManager;
|
|
239
141
|
}
|
|
240
|
-
async
|
|
142
|
+
async generateFinalReport(filteredResults, totalDuration, results, projectSet) {
|
|
241
143
|
const data = await this.prepareReportData(
|
|
242
144
|
filteredResults,
|
|
243
145
|
totalDuration,
|
|
244
146
|
results,
|
|
245
147
|
projectSet
|
|
246
148
|
);
|
|
247
|
-
|
|
248
|
-
import_path3.default.resolve(__dirname, "views", "main.hbs"),
|
|
249
|
-
"utf-8"
|
|
250
|
-
);
|
|
251
|
-
const template = import_handlebars.default.compile(templateSource);
|
|
252
|
-
return template({ ...data, inlineCss: cssContent });
|
|
149
|
+
return data;
|
|
253
150
|
}
|
|
254
151
|
async getReportData() {
|
|
255
152
|
return {
|
|
@@ -259,18 +156,6 @@ var HTMLGenerator = class {
|
|
|
259
156
|
slowTests: await this.dbManager.getSlowTests()
|
|
260
157
|
};
|
|
261
158
|
}
|
|
262
|
-
async chartTrendData() {
|
|
263
|
-
return {
|
|
264
|
-
labels: (await this.getReportData()).trends.map(
|
|
265
|
-
(t) => formatDateNoTimezone(t.run_date)
|
|
266
|
-
),
|
|
267
|
-
passed: (await this.getReportData()).trends.map((t) => t.passed),
|
|
268
|
-
failed: (await this.getReportData()).trends.map((t) => t.failed),
|
|
269
|
-
avgDuration: (await this.getReportData()).trends.map(
|
|
270
|
-
(t) => t.avg_duration
|
|
271
|
-
)
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
159
|
async prepareReportData(filteredResults, totalDuration, results, projectSet) {
|
|
275
160
|
const totalTests = filteredResults.length;
|
|
276
161
|
const passedTests = results.filter((r) => r.status === "passed").length;
|
|
@@ -288,8 +173,7 @@ var HTMLGenerator = class {
|
|
|
288
173
|
results,
|
|
289
174
|
projectSet
|
|
290
175
|
);
|
|
291
|
-
const
|
|
292
|
-
const localRunDate = formatDateLocal(utcRunDate);
|
|
176
|
+
const lastRunDate = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
293
177
|
const testHistories = await Promise.all(
|
|
294
178
|
results.map(async (result) => {
|
|
295
179
|
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
@@ -301,34 +185,42 @@ var HTMLGenerator = class {
|
|
|
301
185
|
})
|
|
302
186
|
);
|
|
303
187
|
return {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
188
|
+
summary: {
|
|
189
|
+
overAllResult: {
|
|
190
|
+
pass: passedTests,
|
|
191
|
+
fail: failed,
|
|
192
|
+
skip: results.filter((r) => r.status === "skipped").length,
|
|
193
|
+
retry: results.filter((r) => r.retryAttemptCount).length,
|
|
194
|
+
flaky: flakyTests,
|
|
195
|
+
total: filteredResults.length
|
|
196
|
+
},
|
|
197
|
+
successRate,
|
|
198
|
+
lastRunDate,
|
|
199
|
+
totalDuration,
|
|
200
|
+
stats: this.extractProjectStats(projectResults)
|
|
201
|
+
},
|
|
202
|
+
testResult: {
|
|
203
|
+
tests: groupResults(this.ortoniConfig, results),
|
|
204
|
+
testHistories,
|
|
205
|
+
allTags: Array.from(allTags),
|
|
206
|
+
set: projectSet
|
|
207
|
+
},
|
|
208
|
+
userConfig: {
|
|
209
|
+
projectName: this.ortoniConfig.projectName,
|
|
210
|
+
authorName: this.ortoniConfig.authorName,
|
|
211
|
+
type: this.ortoniConfig.testType,
|
|
212
|
+
title: this.ortoniConfig.title
|
|
213
|
+
},
|
|
214
|
+
userMeta: {
|
|
215
|
+
meta: this.ortoniConfig.meta
|
|
216
|
+
},
|
|
217
|
+
preferences: {
|
|
218
|
+
logo: this.ortoniConfig.logo || void 0,
|
|
219
|
+
showProject: this.ortoniConfig.showProject || false
|
|
220
|
+
},
|
|
221
|
+
analytics: {
|
|
222
|
+
reportData: await this.getReportData()
|
|
223
|
+
}
|
|
332
224
|
};
|
|
333
225
|
}
|
|
334
226
|
calculateProjectResults(filteredResults, results, projectSet) {
|
|
@@ -346,7 +238,7 @@ var HTMLGenerator = class {
|
|
|
346
238
|
(r) => r.status === "failed" || r.status === "timedOut"
|
|
347
239
|
).length,
|
|
348
240
|
skippedTests: allProjectTests.filter((r) => r.status === "skipped").length,
|
|
349
|
-
retryTests: allProjectTests.filter((r) => r.
|
|
241
|
+
retryTests: allProjectTests.filter((r) => r.retryAttemptCount).length,
|
|
350
242
|
flakyTests: allProjectTests.filter((r) => r.status === "flaky").length,
|
|
351
243
|
totalTests: projectTests.length
|
|
352
244
|
};
|
|
@@ -363,59 +255,18 @@ var HTMLGenerator = class {
|
|
|
363
255
|
flakyTests: projectResults.map((result) => result.flakyTests)
|
|
364
256
|
};
|
|
365
257
|
}
|
|
366
|
-
registerHandlebarsHelpers() {
|
|
367
|
-
import_handlebars.default.registerHelper("joinWithSpace", (array) => array.join(" "));
|
|
368
|
-
import_handlebars.default.registerHelper("json", (context) => safeStringify(context));
|
|
369
|
-
import_handlebars.default.registerHelper(
|
|
370
|
-
"eq",
|
|
371
|
-
(actualStatus, expectedStatus) => actualStatus === expectedStatus
|
|
372
|
-
);
|
|
373
|
-
import_handlebars.default.registerHelper(
|
|
374
|
-
"includes",
|
|
375
|
-
(actualStatus, expectedStatus) => actualStatus.includes(expectedStatus)
|
|
376
|
-
);
|
|
377
|
-
import_handlebars.default.registerHelper("gr", (count) => count > 0);
|
|
378
|
-
import_handlebars.default.registerHelper("or", function(a3, b2) {
|
|
379
|
-
return a3 || b2;
|
|
380
|
-
});
|
|
381
|
-
import_handlebars.default.registerHelper("concat", function(...args) {
|
|
382
|
-
args.pop();
|
|
383
|
-
return args.join("");
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
registerPartials() {
|
|
387
|
-
[
|
|
388
|
-
"head",
|
|
389
|
-
"sidebar",
|
|
390
|
-
"testPanel",
|
|
391
|
-
"summaryCard",
|
|
392
|
-
"userInfo",
|
|
393
|
-
"project",
|
|
394
|
-
"testStatus",
|
|
395
|
-
"testIcons",
|
|
396
|
-
"analytics"
|
|
397
|
-
].forEach((partialName) => {
|
|
398
|
-
import_handlebars.default.registerPartial(
|
|
399
|
-
partialName,
|
|
400
|
-
import_fs2.default.readFileSync(
|
|
401
|
-
import_path3.default.resolve(__dirname, "views", `${partialName}.hbs`),
|
|
402
|
-
"utf-8"
|
|
403
|
-
)
|
|
404
|
-
);
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
258
|
};
|
|
408
259
|
|
|
409
260
|
// src/helpers/resultProcessor .ts
|
|
410
261
|
var import_ansi_to_html = __toESM(require("ansi-to-html"));
|
|
411
|
-
var
|
|
262
|
+
var import_path4 = __toESM(require("path"));
|
|
412
263
|
|
|
413
264
|
// src/utils/attachFiles.ts
|
|
414
|
-
var
|
|
415
|
-
var
|
|
265
|
+
var import_path2 = __toESM(require("path"));
|
|
266
|
+
var import_fs3 = __toESM(require("fs"));
|
|
416
267
|
|
|
417
268
|
// src/helpers/markdownConverter.ts
|
|
418
|
-
var
|
|
269
|
+
var import_fs2 = __toESM(require("fs"));
|
|
419
270
|
|
|
420
271
|
// node_modules/marked/lib/marked.esm.js
|
|
421
272
|
function M() {
|
|
@@ -1540,108 +1391,44 @@ var Ft = T.parse;
|
|
|
1540
1391
|
var Qt = b.lex;
|
|
1541
1392
|
|
|
1542
1393
|
// src/helpers/markdownConverter.ts
|
|
1543
|
-
function convertMarkdownToHtml(markdownPath, htmlOutputPath
|
|
1544
|
-
const hasMarkdown =
|
|
1545
|
-
const markdownContent = hasMarkdown ?
|
|
1394
|
+
function convertMarkdownToHtml(markdownPath, htmlOutputPath) {
|
|
1395
|
+
const hasMarkdown = import_fs2.default.existsSync(markdownPath);
|
|
1396
|
+
const markdownContent = hasMarkdown ? import_fs2.default.readFileSync(markdownPath, "utf-8") : "";
|
|
1546
1397
|
const markdownHtml = hasMarkdown ? k(markdownContent) : "";
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
<div>
|
|
1550
|
-
<pre><code>${step.snippet}</code></pre>
|
|
1551
|
-
${step.location ? `<p><em>Location: ${escapeHtml(step.location)}</em></p>` : ""}
|
|
1552
|
-
</div>`
|
|
1553
|
-
).join("\n");
|
|
1554
|
-
const errorHtml = resultError.map((error) => `<pre><code>${error}</code></pre>`).join("\n");
|
|
1555
|
-
const fullHtml = `
|
|
1556
|
-
<!DOCTYPE html>
|
|
1557
|
-
<html lang="en">
|
|
1558
|
-
<head>
|
|
1559
|
-
<meta charset="UTF-8" />
|
|
1560
|
-
<title>Ortoni Error Report</title>
|
|
1561
|
-
<style>
|
|
1562
|
-
body { font-family: sans-serif; padding: 2rem; line-height: 1.6; max-width: 900px; margin: auto; }
|
|
1563
|
-
code, pre { background: #f4f4f4; padding: 0.5rem; border-radius: 5px; display: block; overflow-x: auto; }
|
|
1564
|
-
h1, h2, h3 { color: #444; }
|
|
1565
|
-
hr { margin: 2em 0; }
|
|
1566
|
-
#copyBtn {
|
|
1567
|
-
background-color: #007acc;
|
|
1568
|
-
color: white;
|
|
1569
|
-
border: none;
|
|
1570
|
-
padding: 0.5rem 1rem;
|
|
1571
|
-
margin-bottom: 1rem;
|
|
1572
|
-
border-radius: 5px;
|
|
1573
|
-
cursor: pointer;
|
|
1574
|
-
}
|
|
1575
|
-
#copyBtn:hover {
|
|
1576
|
-
background-color: #005fa3;
|
|
1577
|
-
}
|
|
1578
|
-
</style>
|
|
1579
|
-
</head>
|
|
1580
|
-
<body>
|
|
1581
|
-
<button id="copyBtn">\u{1F4CB} Copy All</button>
|
|
1582
|
-
<script>
|
|
1583
|
-
document.getElementById("copyBtn").addEventListener("click", () => {
|
|
1584
|
-
const content = document.getElementById("markdownContent").innerText;
|
|
1585
|
-
navigator.clipboard.writeText(content).then(() => {
|
|
1586
|
-
// change button text to indicate success
|
|
1587
|
-
const button = document.getElementById("copyBtn");
|
|
1588
|
-
button.textContent = "\u2705 Copied!";
|
|
1589
|
-
setTimeout(() => {
|
|
1590
|
-
button.textContent = "\u{1F4CB} Copy All"
|
|
1591
|
-
}, 2000);
|
|
1592
|
-
}).catch(err => {
|
|
1593
|
-
console.error("Failed to copy text: ", err);
|
|
1594
|
-
alert("Failed to copy text. Please try manually.");
|
|
1595
|
-
});
|
|
1596
|
-
});
|
|
1597
|
-
</script>
|
|
1598
|
-
<div id="markdownContent">
|
|
1599
|
-
<h1>Instructions</h1>
|
|
1600
|
-
<ul>
|
|
1601
|
-
<li>Following Playwright test failed.</li>
|
|
1602
|
-
<li>Explain why, be concise, respect Playwright best practices.</li>
|
|
1603
|
-
<li>Provide a snippet of code with the fix, if possible.</li>
|
|
1604
|
-
</ul>
|
|
1605
|
-
<h1>Error Details</h1>
|
|
1606
|
-
${errorHtml || "<p>No errors found.</p>"}
|
|
1607
|
-
${stepsHtml || "<p>No step data available.</p>"}
|
|
1608
|
-
${markdownHtml || ""}
|
|
1609
|
-
</div>
|
|
1610
|
-
</body>
|
|
1611
|
-
</html>
|
|
1612
|
-
`;
|
|
1613
|
-
import_fs3.default.writeFileSync(htmlOutputPath, fullHtml, "utf-8");
|
|
1398
|
+
const drawerHtml = `${markdownHtml || ""}`;
|
|
1399
|
+
import_fs2.default.writeFileSync(htmlOutputPath, drawerHtml.trim(), "utf-8");
|
|
1614
1400
|
if (hasMarkdown) {
|
|
1615
|
-
|
|
1401
|
+
import_fs2.default.unlinkSync(markdownPath);
|
|
1616
1402
|
}
|
|
1617
1403
|
}
|
|
1618
1404
|
|
|
1619
1405
|
// src/utils/attachFiles.ts
|
|
1620
1406
|
function attachFiles(subFolder, result, testResult, config, steps, errors) {
|
|
1621
1407
|
const folderPath = config.folderPath || "ortoni-report";
|
|
1622
|
-
const attachmentsFolder =
|
|
1408
|
+
const attachmentsFolder = import_path2.default.join(
|
|
1623
1409
|
folderPath,
|
|
1624
1410
|
"ortoni-data",
|
|
1625
1411
|
"attachments",
|
|
1626
1412
|
subFolder
|
|
1627
1413
|
);
|
|
1628
|
-
if (!
|
|
1629
|
-
|
|
1414
|
+
if (!import_fs3.default.existsSync(attachmentsFolder)) {
|
|
1415
|
+
import_fs3.default.mkdirSync(attachmentsFolder, { recursive: true });
|
|
1630
1416
|
}
|
|
1631
1417
|
if (!result.attachments) return;
|
|
1632
1418
|
const { base64Image } = config;
|
|
1633
1419
|
testResult.screenshots = [];
|
|
1420
|
+
testResult.videoPath = [];
|
|
1634
1421
|
result.attachments.forEach((attachment) => {
|
|
1635
1422
|
const { contentType, name, path: attachmentPath, body } = attachment;
|
|
1636
1423
|
if (!attachmentPath && !body) return;
|
|
1637
|
-
const fileName = attachmentPath ?
|
|
1638
|
-
const relativePath =
|
|
1424
|
+
const fileName = attachmentPath ? import_path2.default.basename(attachmentPath) : `${name}.${getFileExtension(contentType)}`;
|
|
1425
|
+
const relativePath = import_path2.default.join(
|
|
1639
1426
|
"ortoni-data",
|
|
1640
1427
|
"attachments",
|
|
1641
1428
|
subFolder,
|
|
1642
1429
|
fileName
|
|
1643
1430
|
);
|
|
1644
|
-
const fullPath =
|
|
1431
|
+
const fullPath = import_path2.default.join(attachmentsFolder, fileName);
|
|
1645
1432
|
if (contentType === "image/png") {
|
|
1646
1433
|
handleImage(
|
|
1647
1434
|
attachmentPath,
|
|
@@ -1684,13 +1471,13 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
|
|
|
1684
1471
|
let screenshotPath = "";
|
|
1685
1472
|
if (attachmentPath) {
|
|
1686
1473
|
try {
|
|
1687
|
-
const screenshotContent =
|
|
1474
|
+
const screenshotContent = import_fs3.default.readFileSync(
|
|
1688
1475
|
attachmentPath,
|
|
1689
1476
|
base64Image ? "base64" : void 0
|
|
1690
1477
|
);
|
|
1691
1478
|
screenshotPath = base64Image ? `data:image/png;base64,${screenshotContent}` : relativePath;
|
|
1692
1479
|
if (!base64Image) {
|
|
1693
|
-
|
|
1480
|
+
import_fs3.default.copyFileSync(attachmentPath, fullPath);
|
|
1694
1481
|
}
|
|
1695
1482
|
} catch (error) {
|
|
1696
1483
|
console.error(
|
|
@@ -1707,13 +1494,17 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
|
|
|
1707
1494
|
}
|
|
1708
1495
|
function handleAttachment(attachmentPath, fullPath, relativePath, resultKey, testResult, steps, errors) {
|
|
1709
1496
|
if (attachmentPath) {
|
|
1710
|
-
|
|
1711
|
-
|
|
1497
|
+
import_fs3.default.copyFileSync(attachmentPath, fullPath);
|
|
1498
|
+
if (resultKey === "videoPath") {
|
|
1499
|
+
testResult[resultKey]?.push(relativePath);
|
|
1500
|
+
} else if (resultKey === "tracePath") {
|
|
1501
|
+
testResult[resultKey] = relativePath;
|
|
1502
|
+
}
|
|
1712
1503
|
}
|
|
1713
1504
|
if (resultKey === "markdownPath" && errors) {
|
|
1714
1505
|
const htmlPath = fullPath.replace(/\.md$/, ".html");
|
|
1715
1506
|
const htmlRelativePath = relativePath.replace(/\.md$/, ".html");
|
|
1716
|
-
convertMarkdownToHtml(fullPath, htmlPath
|
|
1507
|
+
convertMarkdownToHtml(fullPath, htmlPath);
|
|
1717
1508
|
testResult[resultKey] = htmlRelativePath;
|
|
1718
1509
|
return;
|
|
1719
1510
|
}
|
|
@@ -1728,6 +1519,61 @@ function getFileExtension(contentType) {
|
|
|
1728
1519
|
return extensions[contentType] || "unknown";
|
|
1729
1520
|
}
|
|
1730
1521
|
|
|
1522
|
+
// src/utils/utils.ts
|
|
1523
|
+
var import_path3 = __toESM(require("path"));
|
|
1524
|
+
function normalizeFilePath(filePath) {
|
|
1525
|
+
const normalizedPath = import_path3.default.normalize(filePath);
|
|
1526
|
+
return import_path3.default.basename(normalizedPath);
|
|
1527
|
+
}
|
|
1528
|
+
function ensureHtmlExtension(filename) {
|
|
1529
|
+
const ext = import_path3.default.extname(filename);
|
|
1530
|
+
if (ext && ext.toLowerCase() === ".html") {
|
|
1531
|
+
return filename;
|
|
1532
|
+
}
|
|
1533
|
+
return `${filename}.html`;
|
|
1534
|
+
}
|
|
1535
|
+
function escapeHtml(unsafe) {
|
|
1536
|
+
if (typeof unsafe !== "string") {
|
|
1537
|
+
return String(unsafe);
|
|
1538
|
+
}
|
|
1539
|
+
return unsafe.replace(/[&<"']/g, function(match) {
|
|
1540
|
+
const escapeMap = {
|
|
1541
|
+
"&": "&",
|
|
1542
|
+
"<": "<",
|
|
1543
|
+
">": ">",
|
|
1544
|
+
'"': """,
|
|
1545
|
+
"'": "'"
|
|
1546
|
+
};
|
|
1547
|
+
return escapeMap[match] || match;
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
function formatDateLocal(dateInput) {
|
|
1551
|
+
const date = typeof dateInput === "string" ? new Date(dateInput) : dateInput;
|
|
1552
|
+
const options = {
|
|
1553
|
+
year: "numeric",
|
|
1554
|
+
month: "short",
|
|
1555
|
+
day: "2-digit",
|
|
1556
|
+
hour: "2-digit",
|
|
1557
|
+
minute: "2-digit",
|
|
1558
|
+
hour12: true,
|
|
1559
|
+
timeZoneName: "short"
|
|
1560
|
+
// or "Asia/Kolkata"
|
|
1561
|
+
};
|
|
1562
|
+
return new Intl.DateTimeFormat(void 0, options).format(date);
|
|
1563
|
+
}
|
|
1564
|
+
function extractSuites(titlePath) {
|
|
1565
|
+
const tagPattern = /@[\w]+/g;
|
|
1566
|
+
const suiteParts = titlePath.slice(3, titlePath.length - 1).map((p) => p.replace(tagPattern, "").trim());
|
|
1567
|
+
return {
|
|
1568
|
+
hierarchy: suiteParts.join(" > "),
|
|
1569
|
+
// full hierarchy
|
|
1570
|
+
topLevelSuite: suiteParts[0] ?? "",
|
|
1571
|
+
// first suite
|
|
1572
|
+
parentSuite: suiteParts[suiteParts.length - 1] ?? ""
|
|
1573
|
+
// last suite
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1731
1577
|
// src/helpers/resultProcessor .ts
|
|
1732
1578
|
var TestResultProcessor = class {
|
|
1733
1579
|
constructor(projectRoot) {
|
|
@@ -1743,19 +1589,21 @@ var TestResultProcessor = class {
|
|
|
1743
1589
|
const tagPattern = /@[\w]+/g;
|
|
1744
1590
|
const title = test.title.replace(tagPattern, "").trim();
|
|
1745
1591
|
const suite = test.titlePath()[3].replace(tagPattern, "").trim();
|
|
1592
|
+
const suiteAndTitle = extractSuites(test.titlePath());
|
|
1593
|
+
const suiteHierarchy = suiteAndTitle.hierarchy;
|
|
1746
1594
|
const testResult = {
|
|
1747
|
-
|
|
1595
|
+
suiteHierarchy,
|
|
1596
|
+
key: test.id,
|
|
1748
1597
|
annotations: test.annotations,
|
|
1749
1598
|
testTags: test.tags,
|
|
1750
1599
|
location: `${filePath}:${location.line}:${location.column}`,
|
|
1751
|
-
|
|
1752
|
-
isRetry: result.retry,
|
|
1600
|
+
retryAttemptCount: result.retry,
|
|
1753
1601
|
projectName,
|
|
1754
1602
|
suite,
|
|
1755
1603
|
title,
|
|
1756
1604
|
status,
|
|
1757
1605
|
flaky: test.outcome(),
|
|
1758
|
-
duration:
|
|
1606
|
+
duration: result.duration,
|
|
1759
1607
|
errors: result.errors.map(
|
|
1760
1608
|
(e) => this.ansiToHtml.toHtml(escapeHtml(e.stack || e.toString()))
|
|
1761
1609
|
),
|
|
@@ -1767,7 +1615,8 @@ var TestResultProcessor = class {
|
|
|
1767
1615
|
),
|
|
1768
1616
|
filePath,
|
|
1769
1617
|
filters: projectSet,
|
|
1770
|
-
base64Image: ortoniConfig.base64Image
|
|
1618
|
+
base64Image: ortoniConfig.base64Image,
|
|
1619
|
+
testId: `${filePath}:${projectName}:${title}`
|
|
1771
1620
|
};
|
|
1772
1621
|
attachFiles(
|
|
1773
1622
|
test.id,
|
|
@@ -1781,7 +1630,7 @@ var TestResultProcessor = class {
|
|
|
1781
1630
|
}
|
|
1782
1631
|
processSteps(steps) {
|
|
1783
1632
|
return steps.map((step) => {
|
|
1784
|
-
const stepLocation = step.location ? `${
|
|
1633
|
+
const stepLocation = step.location ? `${import_path4.default.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
|
|
1785
1634
|
return {
|
|
1786
1635
|
snippet: this.ansiToHtml.toHtml(escapeHtml(step.error?.snippet || "")),
|
|
1787
1636
|
title: step.title,
|
|
@@ -1793,16 +1642,16 @@ var TestResultProcessor = class {
|
|
|
1793
1642
|
|
|
1794
1643
|
// src/utils/expressServer.ts
|
|
1795
1644
|
var import_express = __toESM(require("express"));
|
|
1796
|
-
var
|
|
1645
|
+
var import_path5 = __toESM(require("path"));
|
|
1797
1646
|
var import_child_process = require("child_process");
|
|
1798
1647
|
function startReportServer(reportFolder, reportFilename, port = 2004, open2) {
|
|
1799
1648
|
const app = (0, import_express.default)();
|
|
1800
1649
|
app.use(import_express.default.static(reportFolder));
|
|
1801
1650
|
app.get("/", (_req, res) => {
|
|
1802
1651
|
try {
|
|
1803
|
-
res.sendFile(
|
|
1652
|
+
res.sendFile(import_path5.default.resolve(reportFolder, reportFilename));
|
|
1804
1653
|
} catch (error) {
|
|
1805
|
-
console.error("Ortoni
|
|
1654
|
+
console.error("Ortoni Report: Error sending report file:", error);
|
|
1806
1655
|
res.status(500).send("Error loading report");
|
|
1807
1656
|
}
|
|
1808
1657
|
});
|
|
@@ -1816,21 +1665,21 @@ Press Ctrl+C to stop.`
|
|
|
1816
1665
|
try {
|
|
1817
1666
|
openBrowser(`http://localhost:${port}`);
|
|
1818
1667
|
} catch (error) {
|
|
1819
|
-
console.error("Ortoni
|
|
1668
|
+
console.error("Ortoni Report: Error opening browser:", error);
|
|
1820
1669
|
}
|
|
1821
1670
|
}
|
|
1822
1671
|
});
|
|
1823
1672
|
server.on("error", (error) => {
|
|
1824
1673
|
if (error.code === "EADDRINUSE") {
|
|
1825
1674
|
console.error(
|
|
1826
|
-
`Ortoni
|
|
1675
|
+
`Ortoni Report: Port ${port} is already in use. Trying a different port...`
|
|
1827
1676
|
);
|
|
1828
1677
|
} else {
|
|
1829
|
-
console.error("Ortoni
|
|
1678
|
+
console.error("Ortoni Report: Server error:", error);
|
|
1830
1679
|
}
|
|
1831
1680
|
});
|
|
1832
1681
|
} catch (error) {
|
|
1833
|
-
console.error("Ortoni
|
|
1682
|
+
console.error("Ortoni Report: Error starting the server:", error);
|
|
1834
1683
|
}
|
|
1835
1684
|
}
|
|
1836
1685
|
function openBrowser(url) {
|
|
@@ -1848,7 +1697,7 @@ function openBrowser(url) {
|
|
|
1848
1697
|
(0, import_child_process.spawn)(command, [url]);
|
|
1849
1698
|
}
|
|
1850
1699
|
} catch (error) {
|
|
1851
|
-
console.error("Ortoni
|
|
1700
|
+
console.error("Ortoni Report: Error opening the browser:", error);
|
|
1852
1701
|
}
|
|
1853
1702
|
}
|
|
1854
1703
|
|
|
@@ -1907,7 +1756,7 @@ var DatabaseManager = class {
|
|
|
1907
1756
|
run_id INTEGER,
|
|
1908
1757
|
test_id TEXT,
|
|
1909
1758
|
status TEXT,
|
|
1910
|
-
duration
|
|
1759
|
+
duration INTEGER, -- store duration as raw ms
|
|
1911
1760
|
error_message TEXT,
|
|
1912
1761
|
FOREIGN KEY (run_id) REFERENCES test_runs (id)
|
|
1913
1762
|
);
|
|
@@ -1967,6 +1816,7 @@ var DatabaseManager = class {
|
|
|
1967
1816
|
`${result.filePath}:${result.projectName}:${result.title}`,
|
|
1968
1817
|
result.status,
|
|
1969
1818
|
result.duration,
|
|
1819
|
+
// store raw ms
|
|
1970
1820
|
result.errors.join("\n")
|
|
1971
1821
|
]);
|
|
1972
1822
|
}
|
|
@@ -2033,7 +1883,7 @@ var DatabaseManager = class {
|
|
|
2033
1883
|
(SELECT COUNT(*) FROM test_results) as totalTests,
|
|
2034
1884
|
(SELECT COUNT(*) FROM test_results WHERE status = 'passed') as passed,
|
|
2035
1885
|
(SELECT COUNT(*) FROM test_results WHERE status = 'failed') as failed,
|
|
2036
|
-
(SELECT AVG(
|
|
1886
|
+
(SELECT AVG(duration) FROM test_results) as avgDuration
|
|
2037
1887
|
`);
|
|
2038
1888
|
const passRate = summary.totalTests ? (summary.passed / summary.totalTests * 100).toFixed(2) : 0;
|
|
2039
1889
|
return {
|
|
@@ -2043,6 +1893,7 @@ var DatabaseManager = class {
|
|
|
2043
1893
|
failed: summary.failed,
|
|
2044
1894
|
passRate: parseFloat(passRate.toString()),
|
|
2045
1895
|
avgDuration: Math.round(summary.avgDuration || 0)
|
|
1896
|
+
// raw ms avg
|
|
2046
1897
|
};
|
|
2047
1898
|
} catch (error) {
|
|
2048
1899
|
console.error("OrtoniReport: Error getting summary data:", error);
|
|
@@ -2067,7 +1918,7 @@ var DatabaseManager = class {
|
|
|
2067
1918
|
SELECT trun.run_date,
|
|
2068
1919
|
SUM(CASE WHEN tr.status = 'passed' THEN 1 ELSE 0 END) AS passed,
|
|
2069
1920
|
SUM(CASE WHEN tr.status = 'failed' THEN 1 ELSE 0 END) AS failed,
|
|
2070
|
-
AVG(
|
|
1921
|
+
AVG(tr.duration) AS avg_duration
|
|
2071
1922
|
FROM test_results tr
|
|
2072
1923
|
JOIN test_runs trun ON tr.run_id = trun.id
|
|
2073
1924
|
GROUP BY trun.run_date
|
|
@@ -2080,6 +1931,7 @@ var DatabaseManager = class {
|
|
|
2080
1931
|
...row,
|
|
2081
1932
|
run_date: formatDateLocal(row.run_date),
|
|
2082
1933
|
avg_duration: Math.round(row.avg_duration || 0)
|
|
1934
|
+
// raw ms avg
|
|
2083
1935
|
}));
|
|
2084
1936
|
} catch (error) {
|
|
2085
1937
|
console.error("OrtoniReport: Error getting trends data:", error);
|
|
@@ -2097,7 +1949,8 @@ var DatabaseManager = class {
|
|
|
2097
1949
|
SELECT
|
|
2098
1950
|
test_id,
|
|
2099
1951
|
COUNT(*) AS total,
|
|
2100
|
-
SUM(CASE WHEN status = 'flaky' THEN 1 ELSE 0 END) AS flaky
|
|
1952
|
+
SUM(CASE WHEN status = 'flaky' THEN 1 ELSE 0 END) AS flaky,
|
|
1953
|
+
AVG(duration) AS avg_duration
|
|
2101
1954
|
FROM test_results
|
|
2102
1955
|
GROUP BY test_id
|
|
2103
1956
|
HAVING flaky > 0
|
|
@@ -2121,7 +1974,7 @@ var DatabaseManager = class {
|
|
|
2121
1974
|
`
|
|
2122
1975
|
SELECT
|
|
2123
1976
|
test_id,
|
|
2124
|
-
AVG(
|
|
1977
|
+
AVG(duration) AS avg_duration
|
|
2125
1978
|
FROM test_results
|
|
2126
1979
|
GROUP BY test_id
|
|
2127
1980
|
ORDER BY avg_duration DESC
|
|
@@ -2132,6 +1985,7 @@ var DatabaseManager = class {
|
|
|
2132
1985
|
return rows.map((row) => ({
|
|
2133
1986
|
test_id: row.test_id,
|
|
2134
1987
|
avg_duration: Math.round(row.avg_duration || 0)
|
|
1988
|
+
// raw ms avg
|
|
2135
1989
|
}));
|
|
2136
1990
|
} catch (error) {
|
|
2137
1991
|
console.error("OrtoniReport: Error getting slow tests:", error);
|
|
@@ -2141,7 +1995,7 @@ var DatabaseManager = class {
|
|
|
2141
1995
|
};
|
|
2142
1996
|
|
|
2143
1997
|
// src/ortoni-report.ts
|
|
2144
|
-
var
|
|
1998
|
+
var import_path6 = __toESM(require("path"));
|
|
2145
1999
|
var OrtoniReport = class {
|
|
2146
2000
|
constructor(ortoniConfig = {}) {
|
|
2147
2001
|
this.ortoniConfig = ortoniConfig;
|
|
@@ -2172,8 +2026,9 @@ var OrtoniReport = class {
|
|
|
2172
2026
|
this.testResultProcessor = new TestResultProcessor(config.rootDir);
|
|
2173
2027
|
this.fileManager.ensureReportDirectory();
|
|
2174
2028
|
await this.dbManager.initialize(
|
|
2175
|
-
|
|
2029
|
+
import_path6.default.join(this.folderPath, "ortoni-data-history.sqlite")
|
|
2176
2030
|
);
|
|
2031
|
+
this.shardConfig = config?.shard;
|
|
2177
2032
|
}
|
|
2178
2033
|
onStdOut(chunk, _test, _result) {
|
|
2179
2034
|
if (this.reportsCount == 1 && this.showConsoleLogs) {
|
|
@@ -2190,7 +2045,7 @@ var OrtoniReport = class {
|
|
|
2190
2045
|
);
|
|
2191
2046
|
this.results.push(testResult);
|
|
2192
2047
|
} catch (error) {
|
|
2193
|
-
console.error("
|
|
2048
|
+
console.error("Ortoni Report: Error processing test end:", error);
|
|
2194
2049
|
}
|
|
2195
2050
|
}
|
|
2196
2051
|
printsToStdio() {
|
|
@@ -2206,35 +2061,55 @@ var OrtoniReport = class {
|
|
|
2206
2061
|
this.overAllStatus = result.status;
|
|
2207
2062
|
if (this.shouldGenerateReport) {
|
|
2208
2063
|
const filteredResults = this.results.filter(
|
|
2209
|
-
(r) => r.status !== "skipped"
|
|
2064
|
+
(r) => r.status !== "skipped"
|
|
2210
2065
|
);
|
|
2211
|
-
const totalDuration =
|
|
2212
|
-
|
|
2066
|
+
const totalDuration = result.duration;
|
|
2067
|
+
if (this.shardConfig && this.shardConfig.total > 1) {
|
|
2068
|
+
const shard = this.shardConfig;
|
|
2069
|
+
const shardFile = `ortoni-shard-${shard.current}-of-${shard.total}.json`;
|
|
2070
|
+
const shardData = {
|
|
2071
|
+
status: result.status,
|
|
2072
|
+
duration: totalDuration,
|
|
2073
|
+
results: this.results,
|
|
2074
|
+
projectSet: Array.from(this.projectSet),
|
|
2075
|
+
userConfig: {
|
|
2076
|
+
projectName: this.ortoniConfig.projectName,
|
|
2077
|
+
authorName: this.ortoniConfig.authorName,
|
|
2078
|
+
type: this.ortoniConfig.testType,
|
|
2079
|
+
title: this.ortoniConfig.title
|
|
2080
|
+
},
|
|
2081
|
+
userMeta: {
|
|
2082
|
+
meta: this.ortoniConfig.meta
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
this.fileManager.writeRawFile(shardFile, shardData);
|
|
2086
|
+
this.shouldGenerateReport = false;
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2213
2089
|
const runId = await this.dbManager.saveTestRun();
|
|
2214
2090
|
if (runId !== null) {
|
|
2215
2091
|
await this.dbManager.saveTestResults(runId, this.results);
|
|
2216
|
-
const
|
|
2092
|
+
const finalReportData = await this.htmlGenerator.generateFinalReport(
|
|
2217
2093
|
filteredResults,
|
|
2218
2094
|
totalDuration,
|
|
2219
|
-
cssContent,
|
|
2220
2095
|
this.results,
|
|
2221
2096
|
this.projectSet
|
|
2222
2097
|
);
|
|
2223
2098
|
this.outputPath = this.fileManager.writeReportFile(
|
|
2224
2099
|
this.outputFilename,
|
|
2225
|
-
|
|
2100
|
+
finalReportData
|
|
2226
2101
|
);
|
|
2227
2102
|
} else {
|
|
2228
|
-
console.error("
|
|
2103
|
+
console.error("Ortoni Report: Error saving test run to database");
|
|
2229
2104
|
}
|
|
2230
2105
|
} else {
|
|
2231
2106
|
console.error(
|
|
2232
|
-
"
|
|
2107
|
+
"Ortoni Report: Report generation skipped due to error in Playwright worker!"
|
|
2233
2108
|
);
|
|
2234
2109
|
}
|
|
2235
2110
|
} catch (error) {
|
|
2236
2111
|
this.shouldGenerateReport = false;
|
|
2237
|
-
console.error("
|
|
2112
|
+
console.error("Ortoni Report: Error generating report:", error);
|
|
2238
2113
|
}
|
|
2239
2114
|
}
|
|
2240
2115
|
async onExit() {
|
|
@@ -2242,7 +2117,7 @@ var OrtoniReport = class {
|
|
|
2242
2117
|
await this.dbManager.close();
|
|
2243
2118
|
if (this.shouldGenerateReport) {
|
|
2244
2119
|
this.fileManager.copyTraceViewerAssets(this.skipTraceViewer);
|
|
2245
|
-
console.info(`Ortoni
|
|
2120
|
+
console.info(`Ortoni Report generated at ${this.outputPath}`);
|
|
2246
2121
|
this.serverManager.startServer(
|
|
2247
2122
|
this.folderPath,
|
|
2248
2123
|
this.outputFilename,
|
|
@@ -2252,7 +2127,7 @@ var OrtoniReport = class {
|
|
|
2252
2127
|
});
|
|
2253
2128
|
}
|
|
2254
2129
|
} catch (error) {
|
|
2255
|
-
console.error("
|
|
2130
|
+
console.error("Ortoni Report: Error in onExit:", error);
|
|
2256
2131
|
}
|
|
2257
2132
|
}
|
|
2258
2133
|
};
|