ortoni-report 4.0.1-beta.0 → 4.0.1-beta.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/dist/chunk-4RZ5C7KY.mjs +744 -0
- package/dist/{chunk-MPZLDOCN.mjs → chunk-HOOC3MDY.mjs} +305 -304
- package/dist/chunk-ISCRDMPY.mjs +693 -0
- package/dist/{chunk-INS3E7E6.mjs → chunk-P72FKFLZ.mjs} +352 -298
- package/dist/chunk-S45BZGXX.mjs +744 -0
- package/dist/{chunk-OZS6QIJS.mjs → chunk-ZG4JPYLC.mjs} +352 -298
- package/dist/{chunk-TI33PMMQ.mjs → chunk-ZSPTPISU.mjs} +352 -299
- package/dist/{cli/cli.js → cli.js} +403 -189
- package/dist/cli.mjs +206 -0
- package/dist/index.html +2 -2
- package/dist/ortoni-report.d.mts +7 -0
- package/dist/ortoni-report.d.ts +7 -0
- package/dist/ortoni-report.js +189 -72
- package/dist/ortoni-report.mjs +9 -5
- package/package.json +11 -4
- package/dist/chunk-45EJSEX2.mjs +0 -632
- package/dist/chunk-75EAJL2U.mjs +0 -632
- package/dist/chunk-A6HCKATU.mjs +0 -76
- package/dist/chunk-FGIYOFIC.mjs +0 -632
- package/dist/chunk-FHKWBHU6.mjs +0 -633
- package/dist/chunk-GLICR3VS.mjs +0 -637
- package/dist/chunk-HFO6XSKC.mjs +0 -633
- package/dist/chunk-HOZD6YIV.mjs +0 -634
- package/dist/chunk-IJO2YIFE.mjs +0 -637
- package/dist/chunk-JEIWNUQY.mjs +0 -632
- package/dist/chunk-JPLAGYR7.mjs +0 -632
- package/dist/chunk-NM6ULN2O.mjs +0 -632
- package/dist/chunk-P57227VN.mjs +0 -633
- package/dist/chunk-QMTRYN5N.js +0 -635
- package/dist/chunk-Z5NBP5TS.mjs +0 -635
- package/dist/cli/cli.cjs +0 -678
- package/dist/cli/cli.d.cts +0 -1
- package/dist/cli/cli.mjs +0 -103
- package/dist/ortoni-report.cjs +0 -2134
- package/dist/ortoni-report.d.cts +0 -111
- /package/dist/{cli/cli.d.mts → cli.d.mts} +0 -0
- /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
|
@@ -8,234 +8,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
});
|
|
9
9
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
10
10
|
|
|
11
|
-
// src/helpers/fileManager.ts
|
|
12
|
-
import fs from "fs";
|
|
13
|
-
import path from "path";
|
|
14
|
-
var FileManager = class {
|
|
15
|
-
constructor(folderPath) {
|
|
16
|
-
this.folderPath = folderPath;
|
|
17
|
-
}
|
|
18
|
-
ensureReportDirectory() {
|
|
19
|
-
const ortoniDataFolder = path.join(this.folderPath, "ortoni-data");
|
|
20
|
-
if (!fs.existsSync(this.folderPath)) {
|
|
21
|
-
fs.mkdirSync(this.folderPath, { recursive: true });
|
|
22
|
-
} else {
|
|
23
|
-
if (fs.existsSync(ortoniDataFolder)) {
|
|
24
|
-
fs.rmSync(ortoniDataFolder, { recursive: true, force: true });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
writeReportFile(filename, data) {
|
|
29
|
-
const templatePath = path.join(__dirname, "..", "index.html");
|
|
30
|
-
let html = fs.readFileSync(templatePath, "utf-8");
|
|
31
|
-
const reportJSON = JSON.stringify({
|
|
32
|
-
data
|
|
33
|
-
});
|
|
34
|
-
html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
|
|
35
|
-
fs.writeFileSync(filename, html);
|
|
36
|
-
return filename;
|
|
37
|
-
}
|
|
38
|
-
writeRawFile(filename, data) {
|
|
39
|
-
const outputPath = path.join(process.cwd(), this.folderPath, filename);
|
|
40
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
41
|
-
const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
42
|
-
fs.writeFileSync(outputPath, content, "utf-8");
|
|
43
|
-
return outputPath;
|
|
44
|
-
}
|
|
45
|
-
copyTraceViewerAssets(skip) {
|
|
46
|
-
if (skip) return;
|
|
47
|
-
const traceViewerFolder = path.join(
|
|
48
|
-
__require.resolve("playwright-core"),
|
|
49
|
-
"..",
|
|
50
|
-
"lib",
|
|
51
|
-
"vite",
|
|
52
|
-
"traceViewer"
|
|
53
|
-
);
|
|
54
|
-
const traceViewerTargetFolder = path.join(this.folderPath, "trace");
|
|
55
|
-
const traceViewerAssetsTargetFolder = path.join(
|
|
56
|
-
traceViewerTargetFolder,
|
|
57
|
-
"assets"
|
|
58
|
-
);
|
|
59
|
-
fs.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
|
|
60
|
-
for (const file of fs.readdirSync(traceViewerFolder)) {
|
|
61
|
-
if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
|
|
62
|
-
continue;
|
|
63
|
-
fs.copyFileSync(
|
|
64
|
-
path.join(traceViewerFolder, file),
|
|
65
|
-
path.join(traceViewerTargetFolder, file)
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
const assetsFolder = path.join(traceViewerFolder, "assets");
|
|
69
|
-
for (const file of fs.readdirSync(assetsFolder)) {
|
|
70
|
-
if (file.endsWith(".map") || file.includes("xtermModule")) continue;
|
|
71
|
-
fs.copyFileSync(
|
|
72
|
-
path.join(assetsFolder, file),
|
|
73
|
-
path.join(traceViewerAssetsTargetFolder, file)
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// src/utils/groupProjects.ts
|
|
80
|
-
function groupResults(config, results) {
|
|
81
|
-
if (config.showProject) {
|
|
82
|
-
const groupedResults = results.reduce((acc, result, index) => {
|
|
83
|
-
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
84
|
-
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
85
|
-
const { filePath, suite, projectName } = result;
|
|
86
|
-
acc[filePath] = acc[filePath] || {};
|
|
87
|
-
acc[filePath][suite] = acc[filePath][suite] || {};
|
|
88
|
-
acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
|
|
89
|
-
acc[filePath][suite][projectName].push({ ...result, index, testId, key });
|
|
90
|
-
return acc;
|
|
91
|
-
}, {});
|
|
92
|
-
return groupedResults;
|
|
93
|
-
} else {
|
|
94
|
-
const groupedResults = results.reduce((acc, result, index) => {
|
|
95
|
-
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
96
|
-
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
97
|
-
const { filePath, suite } = result;
|
|
98
|
-
acc[filePath] = acc[filePath] || {};
|
|
99
|
-
acc[filePath][suite] = acc[filePath][suite] || [];
|
|
100
|
-
acc[filePath][suite].push({ ...result, index, testId, key });
|
|
101
|
-
return acc;
|
|
102
|
-
}, {});
|
|
103
|
-
return groupedResults;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// src/helpers/HTMLGenerator.ts
|
|
108
|
-
var HTMLGenerator = class {
|
|
109
|
-
constructor(ortoniConfig, dbManager) {
|
|
110
|
-
this.ortoniConfig = ortoniConfig;
|
|
111
|
-
this.dbManager = dbManager;
|
|
112
|
-
}
|
|
113
|
-
async generateFinalReport(filteredResults, totalDuration, results, projectSet) {
|
|
114
|
-
const data = await this.prepareReportData(
|
|
115
|
-
filteredResults,
|
|
116
|
-
totalDuration,
|
|
117
|
-
results,
|
|
118
|
-
projectSet
|
|
119
|
-
);
|
|
120
|
-
return data;
|
|
121
|
-
}
|
|
122
|
-
async getReportData() {
|
|
123
|
-
return {
|
|
124
|
-
summary: await this.dbManager.getSummaryData(),
|
|
125
|
-
trends: await this.dbManager.getTrends(),
|
|
126
|
-
flakyTests: await this.dbManager.getFlakyTests(),
|
|
127
|
-
slowTests: await this.dbManager.getSlowTests()
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
async prepareReportData(filteredResults, totalDuration, results, projectSet) {
|
|
131
|
-
const totalTests = filteredResults.length;
|
|
132
|
-
const passedTests = results.filter((r) => r.status === "passed").length;
|
|
133
|
-
const flakyTests = results.filter((r) => r.status === "flaky").length;
|
|
134
|
-
const failed = filteredResults.filter(
|
|
135
|
-
(r) => r.status === "failed" || r.status === "timedOut"
|
|
136
|
-
).length;
|
|
137
|
-
const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
|
|
138
|
-
const allTags = /* @__PURE__ */ new Set();
|
|
139
|
-
results.forEach(
|
|
140
|
-
(result) => result.testTags.forEach((tag) => allTags.add(tag))
|
|
141
|
-
);
|
|
142
|
-
const projectResults = this.calculateProjectResults(
|
|
143
|
-
filteredResults,
|
|
144
|
-
results,
|
|
145
|
-
projectSet
|
|
146
|
-
);
|
|
147
|
-
const lastRunDate = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
148
|
-
const testHistories = await Promise.all(
|
|
149
|
-
results.map(async (result) => {
|
|
150
|
-
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
151
|
-
const history = await this.dbManager.getTestHistory(testId);
|
|
152
|
-
return {
|
|
153
|
-
testId,
|
|
154
|
-
history
|
|
155
|
-
};
|
|
156
|
-
})
|
|
157
|
-
);
|
|
158
|
-
return {
|
|
159
|
-
summary: {
|
|
160
|
-
overAllResult: {
|
|
161
|
-
pass: passedTests,
|
|
162
|
-
fail: failed,
|
|
163
|
-
skip: results.filter((r) => r.status === "skipped").length,
|
|
164
|
-
retry: results.filter((r) => r.retryAttemptCount).length,
|
|
165
|
-
flaky: flakyTests,
|
|
166
|
-
total: filteredResults.length
|
|
167
|
-
},
|
|
168
|
-
successRate,
|
|
169
|
-
lastRunDate,
|
|
170
|
-
totalDuration,
|
|
171
|
-
stats: this.extractProjectStats(projectResults)
|
|
172
|
-
},
|
|
173
|
-
testResult: {
|
|
174
|
-
tests: groupResults(this.ortoniConfig, results),
|
|
175
|
-
testHistories,
|
|
176
|
-
allTags: Array.from(allTags),
|
|
177
|
-
set: projectSet
|
|
178
|
-
},
|
|
179
|
-
userConfig: {
|
|
180
|
-
projectName: this.ortoniConfig.projectName,
|
|
181
|
-
authorName: this.ortoniConfig.authorName,
|
|
182
|
-
type: this.ortoniConfig.testType,
|
|
183
|
-
title: this.ortoniConfig.title
|
|
184
|
-
},
|
|
185
|
-
userMeta: {
|
|
186
|
-
meta: this.ortoniConfig.meta
|
|
187
|
-
},
|
|
188
|
-
preferences: {
|
|
189
|
-
logo: this.ortoniConfig.logo || void 0,
|
|
190
|
-
showProject: this.ortoniConfig.showProject || false
|
|
191
|
-
},
|
|
192
|
-
analytics: {
|
|
193
|
-
reportData: await this.getReportData()
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
calculateProjectResults(filteredResults, results, projectSet) {
|
|
198
|
-
return Array.from(projectSet).map((projectName) => {
|
|
199
|
-
const projectTests = filteredResults.filter(
|
|
200
|
-
(r) => r.projectName === projectName
|
|
201
|
-
);
|
|
202
|
-
const allProjectTests = results.filter(
|
|
203
|
-
(r) => r.projectName === projectName
|
|
204
|
-
);
|
|
205
|
-
return {
|
|
206
|
-
projectName,
|
|
207
|
-
passedTests: projectTests.filter((r) => r.status === "passed").length,
|
|
208
|
-
failedTests: projectTests.filter(
|
|
209
|
-
(r) => r.status === "failed" || r.status === "timedOut"
|
|
210
|
-
).length,
|
|
211
|
-
skippedTests: allProjectTests.filter((r) => r.status === "skipped").length,
|
|
212
|
-
retryTests: allProjectTests.filter((r) => r.retryAttemptCount).length,
|
|
213
|
-
flakyTests: allProjectTests.filter((r) => r.status === "flaky").length,
|
|
214
|
-
totalTests: projectTests.length
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
extractProjectStats(projectResults) {
|
|
219
|
-
return {
|
|
220
|
-
projectNames: projectResults.map((result) => result.projectName),
|
|
221
|
-
totalTests: projectResults.map((result) => result.totalTests),
|
|
222
|
-
passedTests: projectResults.map((result) => result.passedTests),
|
|
223
|
-
failedTests: projectResults.map((result) => result.failedTests),
|
|
224
|
-
skippedTests: projectResults.map((result) => result.skippedTests),
|
|
225
|
-
retryTests: projectResults.map((result) => result.retryTests),
|
|
226
|
-
flakyTests: projectResults.map((result) => result.flakyTests)
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
11
|
// src/utils/utils.ts
|
|
232
|
-
import
|
|
12
|
+
import path from "path";
|
|
233
13
|
function normalizeFilePath(filePath) {
|
|
234
|
-
const normalizedPath =
|
|
235
|
-
return
|
|
14
|
+
const normalizedPath = path.normalize(filePath);
|
|
15
|
+
return path.basename(normalizedPath);
|
|
236
16
|
}
|
|
237
17
|
function ensureHtmlExtension(filename) {
|
|
238
|
-
const ext =
|
|
18
|
+
const ext = path.extname(filename);
|
|
239
19
|
if (ext && ext.toLowerCase() === ".html") {
|
|
240
20
|
return filename;
|
|
241
21
|
}
|
|
@@ -283,84 +63,23 @@ function extractSuites(titlePath) {
|
|
|
283
63
|
};
|
|
284
64
|
}
|
|
285
65
|
|
|
286
|
-
// src/
|
|
287
|
-
import
|
|
288
|
-
import
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
66
|
+
// src/helpers/databaseManager.ts
|
|
67
|
+
import { open } from "sqlite";
|
|
68
|
+
import sqlite3 from "sqlite3";
|
|
69
|
+
var DatabaseManager = class {
|
|
70
|
+
constructor() {
|
|
71
|
+
this.db = null;
|
|
72
|
+
}
|
|
73
|
+
async initialize(dbPath) {
|
|
294
74
|
try {
|
|
295
|
-
|
|
75
|
+
this.db = await open({
|
|
76
|
+
filename: dbPath,
|
|
77
|
+
driver: sqlite3.Database
|
|
78
|
+
});
|
|
79
|
+
await this.createTables();
|
|
80
|
+
await this.createIndexes();
|
|
296
81
|
} catch (error) {
|
|
297
|
-
console.error("
|
|
298
|
-
res.status(500).send("Error loading report");
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
try {
|
|
302
|
-
const server = app.listen(port, () => {
|
|
303
|
-
console.log(
|
|
304
|
-
`Server is running at http://localhost:${port}
|
|
305
|
-
Press Ctrl+C to stop.`
|
|
306
|
-
);
|
|
307
|
-
if (open2 === "always" || open2 === "on-failure") {
|
|
308
|
-
try {
|
|
309
|
-
openBrowser(`http://localhost:${port}`);
|
|
310
|
-
} catch (error) {
|
|
311
|
-
console.error("Ortoni Report: Error opening browser:", error);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
server.on("error", (error) => {
|
|
316
|
-
if (error.code === "EADDRINUSE") {
|
|
317
|
-
console.error(
|
|
318
|
-
`Ortoni Report: Port ${port} is already in use. Trying a different port...`
|
|
319
|
-
);
|
|
320
|
-
} else {
|
|
321
|
-
console.error("Ortoni Report: Server error:", error);
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
} catch (error) {
|
|
325
|
-
console.error("Ortoni Report: Error starting the server:", error);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function openBrowser(url) {
|
|
329
|
-
const platform = process.platform;
|
|
330
|
-
let command;
|
|
331
|
-
try {
|
|
332
|
-
if (platform === "win32") {
|
|
333
|
-
command = "cmd";
|
|
334
|
-
spawn(command, ["/c", "start", url]);
|
|
335
|
-
} else if (platform === "darwin") {
|
|
336
|
-
command = "open";
|
|
337
|
-
spawn(command, [url]);
|
|
338
|
-
} else {
|
|
339
|
-
command = "xdg-open";
|
|
340
|
-
spawn(command, [url]);
|
|
341
|
-
}
|
|
342
|
-
} catch (error) {
|
|
343
|
-
console.error("Ortoni Report: Error opening the browser:", error);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// src/helpers/databaseManager.ts
|
|
348
|
-
import { open } from "sqlite";
|
|
349
|
-
import sqlite3 from "sqlite3";
|
|
350
|
-
var DatabaseManager = class {
|
|
351
|
-
constructor() {
|
|
352
|
-
this.db = null;
|
|
353
|
-
}
|
|
354
|
-
async initialize(dbPath) {
|
|
355
|
-
try {
|
|
356
|
-
this.db = await open({
|
|
357
|
-
filename: dbPath,
|
|
358
|
-
driver: sqlite3.Database
|
|
359
|
-
});
|
|
360
|
-
await this.createTables();
|
|
361
|
-
await this.createIndexes();
|
|
362
|
-
} catch (error) {
|
|
363
|
-
console.error("OrtoniReport: Error initializing database:", error);
|
|
82
|
+
console.error("OrtoniReport: Error initializing database:", error);
|
|
364
83
|
}
|
|
365
84
|
}
|
|
366
85
|
async createTables() {
|
|
@@ -618,14 +337,296 @@ var DatabaseManager = class {
|
|
|
618
337
|
}
|
|
619
338
|
};
|
|
620
339
|
|
|
340
|
+
// src/utils/groupProjects.ts
|
|
341
|
+
function groupResults(config, results) {
|
|
342
|
+
if (config.showProject) {
|
|
343
|
+
const groupedResults = results.reduce((acc, result, index) => {
|
|
344
|
+
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
345
|
+
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
346
|
+
const { filePath, suite, projectName } = result;
|
|
347
|
+
acc[filePath] = acc[filePath] || {};
|
|
348
|
+
acc[filePath][suite] = acc[filePath][suite] || {};
|
|
349
|
+
acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
|
|
350
|
+
acc[filePath][suite][projectName].push({ ...result, index, testId, key });
|
|
351
|
+
return acc;
|
|
352
|
+
}, {});
|
|
353
|
+
return groupedResults;
|
|
354
|
+
} else {
|
|
355
|
+
const groupedResults = results.reduce((acc, result, index) => {
|
|
356
|
+
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
357
|
+
const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
|
|
358
|
+
const { filePath, suite } = result;
|
|
359
|
+
acc[filePath] = acc[filePath] || {};
|
|
360
|
+
acc[filePath][suite] = acc[filePath][suite] || [];
|
|
361
|
+
acc[filePath][suite].push({ ...result, index, testId, key });
|
|
362
|
+
return acc;
|
|
363
|
+
}, {});
|
|
364
|
+
return groupedResults;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/helpers/HTMLGenerator.ts
|
|
369
|
+
var HTMLGenerator = class {
|
|
370
|
+
constructor(ortoniConfig, dbManager) {
|
|
371
|
+
this.ortoniConfig = ortoniConfig;
|
|
372
|
+
this.dbManager = dbManager;
|
|
373
|
+
}
|
|
374
|
+
async generateFinalReport(filteredResults, totalDuration, results, projectSet) {
|
|
375
|
+
const data = await this.prepareReportData(
|
|
376
|
+
filteredResults,
|
|
377
|
+
totalDuration,
|
|
378
|
+
results,
|
|
379
|
+
projectSet
|
|
380
|
+
);
|
|
381
|
+
return data;
|
|
382
|
+
}
|
|
383
|
+
async getReportData() {
|
|
384
|
+
return {
|
|
385
|
+
summary: await this.dbManager.getSummaryData(),
|
|
386
|
+
trends: await this.dbManager.getTrends(),
|
|
387
|
+
flakyTests: await this.dbManager.getFlakyTests(),
|
|
388
|
+
slowTests: await this.dbManager.getSlowTests()
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
async prepareReportData(filteredResults, totalDuration, results, projectSet) {
|
|
392
|
+
const totalTests = filteredResults.length;
|
|
393
|
+
const passedTests = results.filter((r) => r.status === "passed").length;
|
|
394
|
+
const flakyTests = results.filter((r) => r.status === "flaky").length;
|
|
395
|
+
const failed = filteredResults.filter(
|
|
396
|
+
(r) => r.status === "failed" || r.status === "timedOut"
|
|
397
|
+
).length;
|
|
398
|
+
const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
|
|
399
|
+
const allTags = /* @__PURE__ */ new Set();
|
|
400
|
+
results.forEach(
|
|
401
|
+
(result) => result.testTags.forEach((tag) => allTags.add(tag))
|
|
402
|
+
);
|
|
403
|
+
const projectResults = this.calculateProjectResults(
|
|
404
|
+
filteredResults,
|
|
405
|
+
results,
|
|
406
|
+
projectSet
|
|
407
|
+
);
|
|
408
|
+
const lastRunDate = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
409
|
+
const testHistories = await Promise.all(
|
|
410
|
+
results.map(async (result) => {
|
|
411
|
+
const testId = `${result.filePath}:${result.projectName}:${result.title}`;
|
|
412
|
+
const history = await this.dbManager.getTestHistory(testId);
|
|
413
|
+
return {
|
|
414
|
+
testId,
|
|
415
|
+
history
|
|
416
|
+
};
|
|
417
|
+
})
|
|
418
|
+
);
|
|
419
|
+
return {
|
|
420
|
+
summary: {
|
|
421
|
+
overAllResult: {
|
|
422
|
+
pass: passedTests,
|
|
423
|
+
fail: failed,
|
|
424
|
+
skip: results.filter((r) => r.status === "skipped").length,
|
|
425
|
+
retry: results.filter((r) => r.retryAttemptCount).length,
|
|
426
|
+
flaky: flakyTests,
|
|
427
|
+
total: filteredResults.length
|
|
428
|
+
},
|
|
429
|
+
successRate,
|
|
430
|
+
lastRunDate,
|
|
431
|
+
totalDuration,
|
|
432
|
+
stats: this.extractProjectStats(projectResults)
|
|
433
|
+
},
|
|
434
|
+
testResult: {
|
|
435
|
+
tests: groupResults(this.ortoniConfig, results),
|
|
436
|
+
testHistories,
|
|
437
|
+
allTags: Array.from(allTags),
|
|
438
|
+
set: projectSet
|
|
439
|
+
},
|
|
440
|
+
userConfig: {
|
|
441
|
+
projectName: this.ortoniConfig.projectName,
|
|
442
|
+
authorName: this.ortoniConfig.authorName,
|
|
443
|
+
type: this.ortoniConfig.testType,
|
|
444
|
+
title: this.ortoniConfig.title
|
|
445
|
+
},
|
|
446
|
+
userMeta: {
|
|
447
|
+
meta: this.ortoniConfig.meta
|
|
448
|
+
},
|
|
449
|
+
preferences: {
|
|
450
|
+
logo: this.ortoniConfig.logo || void 0,
|
|
451
|
+
showProject: this.ortoniConfig.showProject || false
|
|
452
|
+
},
|
|
453
|
+
analytics: {
|
|
454
|
+
reportData: await this.getReportData()
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
calculateProjectResults(filteredResults, results, projectSet) {
|
|
459
|
+
return Array.from(projectSet).map((projectName) => {
|
|
460
|
+
const projectTests = filteredResults.filter(
|
|
461
|
+
(r) => r.projectName === projectName
|
|
462
|
+
);
|
|
463
|
+
const allProjectTests = results.filter(
|
|
464
|
+
(r) => r.projectName === projectName
|
|
465
|
+
);
|
|
466
|
+
return {
|
|
467
|
+
projectName,
|
|
468
|
+
passedTests: projectTests.filter((r) => r.status === "passed").length,
|
|
469
|
+
failedTests: projectTests.filter(
|
|
470
|
+
(r) => r.status === "failed" || r.status === "timedOut"
|
|
471
|
+
).length,
|
|
472
|
+
skippedTests: allProjectTests.filter((r) => r.status === "skipped").length,
|
|
473
|
+
retryTests: allProjectTests.filter((r) => r.retryAttemptCount).length,
|
|
474
|
+
flakyTests: allProjectTests.filter((r) => r.status === "flaky").length,
|
|
475
|
+
totalTests: projectTests.length
|
|
476
|
+
};
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
extractProjectStats(projectResults) {
|
|
480
|
+
return {
|
|
481
|
+
projectNames: projectResults.map((result) => result.projectName),
|
|
482
|
+
totalTests: projectResults.map((result) => result.totalTests),
|
|
483
|
+
passedTests: projectResults.map((result) => result.passedTests),
|
|
484
|
+
failedTests: projectResults.map((result) => result.failedTests),
|
|
485
|
+
skippedTests: projectResults.map((result) => result.skippedTests),
|
|
486
|
+
retryTests: projectResults.map((result) => result.retryTests),
|
|
487
|
+
flakyTests: projectResults.map((result) => result.flakyTests)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/helpers/fileManager.ts
|
|
493
|
+
import fs from "fs";
|
|
494
|
+
import path2 from "path";
|
|
495
|
+
var FileManager = class {
|
|
496
|
+
constructor(folderPath) {
|
|
497
|
+
this.folderPath = folderPath;
|
|
498
|
+
}
|
|
499
|
+
ensureReportDirectory() {
|
|
500
|
+
const ortoniDataFolder = path2.join(this.folderPath, "ortoni-data");
|
|
501
|
+
if (!fs.existsSync(this.folderPath)) {
|
|
502
|
+
fs.mkdirSync(this.folderPath, { recursive: true });
|
|
503
|
+
} else {
|
|
504
|
+
if (fs.existsSync(ortoniDataFolder)) {
|
|
505
|
+
fs.rmSync(ortoniDataFolder, { recursive: true, force: true });
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
writeReportFile(filename, data) {
|
|
510
|
+
const templatePath = path2.resolve(__dirname, "index.html");
|
|
511
|
+
let html = fs.readFileSync(templatePath, "utf-8");
|
|
512
|
+
const reportJSON = JSON.stringify({
|
|
513
|
+
data
|
|
514
|
+
});
|
|
515
|
+
html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
|
|
516
|
+
const outputPath = path2.join(process.cwd(), this.folderPath, filename);
|
|
517
|
+
fs.writeFileSync(outputPath, html);
|
|
518
|
+
return outputPath;
|
|
519
|
+
}
|
|
520
|
+
writeRawFile(filename, data) {
|
|
521
|
+
const outputPath = path2.join(process.cwd(), this.folderPath, filename);
|
|
522
|
+
fs.mkdirSync(path2.dirname(outputPath), { recursive: true });
|
|
523
|
+
const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
524
|
+
fs.writeFileSync(outputPath, content, "utf-8");
|
|
525
|
+
return outputPath;
|
|
526
|
+
}
|
|
527
|
+
copyTraceViewerAssets(skip) {
|
|
528
|
+
if (skip) return;
|
|
529
|
+
const traceViewerFolder = path2.join(
|
|
530
|
+
__require.resolve("playwright-core"),
|
|
531
|
+
"..",
|
|
532
|
+
"lib",
|
|
533
|
+
"vite",
|
|
534
|
+
"traceViewer"
|
|
535
|
+
);
|
|
536
|
+
const traceViewerTargetFolder = path2.join(this.folderPath, "trace");
|
|
537
|
+
const traceViewerAssetsTargetFolder = path2.join(
|
|
538
|
+
traceViewerTargetFolder,
|
|
539
|
+
"assets"
|
|
540
|
+
);
|
|
541
|
+
fs.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
|
|
542
|
+
for (const file of fs.readdirSync(traceViewerFolder)) {
|
|
543
|
+
if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
|
|
544
|
+
continue;
|
|
545
|
+
fs.copyFileSync(
|
|
546
|
+
path2.join(traceViewerFolder, file),
|
|
547
|
+
path2.join(traceViewerTargetFolder, file)
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
const assetsFolder = path2.join(traceViewerFolder, "assets");
|
|
551
|
+
for (const file of fs.readdirSync(assetsFolder)) {
|
|
552
|
+
if (file.endsWith(".map") || file.includes("xtermModule")) continue;
|
|
553
|
+
fs.copyFileSync(
|
|
554
|
+
path2.join(assetsFolder, file),
|
|
555
|
+
path2.join(traceViewerAssetsTargetFolder, file)
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
// src/utils/expressServer.ts
|
|
562
|
+
import express from "express";
|
|
563
|
+
import path3 from "path";
|
|
564
|
+
import { spawn } from "child_process";
|
|
565
|
+
function startReportServer(reportFolder, reportFilename, port = 2004, open2) {
|
|
566
|
+
const app = express();
|
|
567
|
+
app.use(express.static(reportFolder));
|
|
568
|
+
app.get("/", (_req, res) => {
|
|
569
|
+
try {
|
|
570
|
+
res.sendFile(path3.resolve(reportFolder, reportFilename));
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error("Ortoni Report: Error sending report file:", error);
|
|
573
|
+
res.status(500).send("Error loading report");
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
try {
|
|
577
|
+
const server = app.listen(port, () => {
|
|
578
|
+
console.log(
|
|
579
|
+
`Server is running at http://localhost:${port}
|
|
580
|
+
Press Ctrl+C to stop.`
|
|
581
|
+
);
|
|
582
|
+
if (open2 === "always" || open2 === "on-failure") {
|
|
583
|
+
try {
|
|
584
|
+
openBrowser(`http://localhost:${port}`);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
console.error("Ortoni Report: Error opening browser:", error);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
server.on("error", (error) => {
|
|
591
|
+
if (error.code === "EADDRINUSE") {
|
|
592
|
+
console.error(
|
|
593
|
+
`Ortoni Report: Port ${port} is already in use. Trying a different port...`
|
|
594
|
+
);
|
|
595
|
+
} else {
|
|
596
|
+
console.error("Ortoni Report: Server error:", error);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error("Ortoni Report: Error starting the server:", error);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function openBrowser(url) {
|
|
604
|
+
const platform = process.platform;
|
|
605
|
+
let command;
|
|
606
|
+
try {
|
|
607
|
+
if (platform === "win32") {
|
|
608
|
+
command = "cmd";
|
|
609
|
+
spawn(command, ["/c", "start", url]);
|
|
610
|
+
} else if (platform === "darwin") {
|
|
611
|
+
command = "open";
|
|
612
|
+
spawn(command, [url]);
|
|
613
|
+
} else {
|
|
614
|
+
command = "xdg-open";
|
|
615
|
+
spawn(command, [url]);
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
console.error("Ortoni Report: Error opening the browser:", error);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
621
622
|
export {
|
|
622
623
|
__publicField,
|
|
623
|
-
FileManager,
|
|
624
|
-
HTMLGenerator,
|
|
625
624
|
normalizeFilePath,
|
|
626
625
|
ensureHtmlExtension,
|
|
627
626
|
escapeHtml,
|
|
628
627
|
extractSuites,
|
|
629
|
-
|
|
630
|
-
|
|
628
|
+
DatabaseManager,
|
|
629
|
+
HTMLGenerator,
|
|
630
|
+
FileManager,
|
|
631
|
+
startReportServer
|
|
631
632
|
};
|