ortoni-report 4.0.2-beta.1 → 4.0.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 +27 -0
- package/dist/{chunk-INS3E7E6.mjs → chunk-4RZ5C7KY.mjs} +402 -296
- package/dist/{cli/cli.js → cli.js} +405 -190
- package/dist/cli.mjs +208 -0
- package/dist/helpers/HTMLGenerator.d.ts +89 -0
- package/dist/helpers/HTMLGenerator.js +163 -0
- package/dist/helpers/databaseManager.d.ts +35 -0
- package/dist/helpers/databaseManager.js +268 -0
- package/dist/helpers/fileManager.d.ts +8 -0
- package/dist/helpers/fileManager.js +60 -0
- package/dist/helpers/markdownConverter.d.ts +1 -0
- package/dist/helpers/markdownConverter.js +14 -0
- package/dist/helpers/resultProcessor.d.ts +10 -0
- package/dist/helpers/resultProcessor.js +60 -0
- package/dist/helpers/serverManager.d.ts +6 -0
- package/dist/helpers/serverManager.js +15 -0
- package/dist/helpers/templateLoader.d.ts +15 -0
- package/dist/helpers/templateLoader.js +88 -0
- package/dist/index.html +1 -1
- package/dist/mergeData.d.ts +13 -0
- package/dist/mergeData.js +182 -0
- package/dist/ortoni-report.d.mts +8 -1
- package/dist/ortoni-report.d.ts +8 -1
- package/dist/ortoni-report.js +211 -96
- package/dist/ortoni-report.mjs +25 -22
- package/dist/{ortoni-report.d.cts → types/reporterConfig.d.ts} +8 -33
- package/dist/types/reporterConfig.js +1 -0
- package/dist/types/testResults.d.ts +31 -0
- package/dist/types/testResults.js +1 -0
- package/dist/utils/attachFiles.d.ts +4 -0
- package/dist/utils/attachFiles.js +87 -0
- package/dist/utils/expressServer.d.ts +1 -0
- package/dist/utils/expressServer.js +61 -0
- package/dist/utils/groupProjects.d.ts +3 -0
- package/dist/utils/groupProjects.js +30 -0
- package/dist/utils/utils.d.ts +15 -0
- package/dist/utils/utils.js +84 -0
- package/package.json +11 -4
- package/readme.md +60 -75
- 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-OZS6QIJS.mjs +0 -638
- package/dist/chunk-P57227VN.mjs +0 -633
- package/dist/chunk-QMTRYN5N.js +0 -635
- package/dist/chunk-TI33PMMQ.mjs +0 -639
- 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/{cli/cli.d.mts → cli.d.mts} +0 -0
- /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge shard JSON files into a single report while keeping ALL test results.
|
|
3
|
+
*
|
|
4
|
+
* options:
|
|
5
|
+
* - dir?: folder where shard files exist (default: "ortoni-report")
|
|
6
|
+
* - file?: output file name for final HTML (default: "ortoni-report.html")
|
|
7
|
+
* - saveHistory?: boolean | undefined -> if provided, overrides shard/userConfig
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeAllData(options?: {
|
|
10
|
+
dir?: string;
|
|
11
|
+
file?: string;
|
|
12
|
+
saveHistory?: boolean;
|
|
13
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// src/mergeAllData.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { DatabaseManager } from "./helpers/databaseManager";
|
|
5
|
+
import { HTMLGenerator } from "./helpers/HTMLGenerator";
|
|
6
|
+
import { FileManager } from "./helpers/fileManager";
|
|
7
|
+
/**
|
|
8
|
+
* Merge shard JSON files into a single report while keeping ALL test results.
|
|
9
|
+
*
|
|
10
|
+
* options:
|
|
11
|
+
* - dir?: folder where shard files exist (default: "ortoni-report")
|
|
12
|
+
* - file?: output file name for final HTML (default: "ortoni-report.html")
|
|
13
|
+
* - saveHistory?: boolean | undefined -> if provided, overrides shard/userConfig
|
|
14
|
+
*/
|
|
15
|
+
export async function mergeAllData(options = {}) {
|
|
16
|
+
const folderPath = options.dir || "ortoni-report";
|
|
17
|
+
console.info(`Ortoni Report: Merging shard files in folder: ${folderPath}`);
|
|
18
|
+
if (!fs.existsSync(folderPath)) {
|
|
19
|
+
console.error(`Ortoni Report: folder "${folderPath}" does not exist.`);
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const filenames = fs
|
|
24
|
+
.readdirSync(folderPath)
|
|
25
|
+
.filter((f) => f.startsWith("ortoni-shard-") && f.endsWith(".json"));
|
|
26
|
+
if (filenames.length === 0) {
|
|
27
|
+
console.error("Ortoni Report: ❌ No shard files found to merge.");
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// deterministic sort by numeric shard index if available (ortoni-shard-<current>-of-<total>.json)
|
|
32
|
+
const shardFileIndex = (name) => {
|
|
33
|
+
const m = name.match(/ortoni-shard-(\d+)-of-(\d+)\.json$/);
|
|
34
|
+
return m ? parseInt(m[1], 10) : null;
|
|
35
|
+
};
|
|
36
|
+
const sortedFiles = filenames
|
|
37
|
+
.map((f) => ({ f, idx: shardFileIndex(f) }))
|
|
38
|
+
.sort((a, b) => {
|
|
39
|
+
if (a.idx === null && b.idx === null)
|
|
40
|
+
return a.f.localeCompare(b.f);
|
|
41
|
+
if (a.idx === null)
|
|
42
|
+
return 1;
|
|
43
|
+
if (b.idx === null)
|
|
44
|
+
return -1;
|
|
45
|
+
return a.idx - b.idx;
|
|
46
|
+
})
|
|
47
|
+
.map((x) => x.f);
|
|
48
|
+
// Merge state
|
|
49
|
+
const allResults = []; // keep every test entry from every shard
|
|
50
|
+
const projectSet = new Set();
|
|
51
|
+
let totalDurationSum = 0; // sum of shard durations (ms)
|
|
52
|
+
let mergedUserConfig = null;
|
|
53
|
+
let mergedUserMeta = null;
|
|
54
|
+
const badShards = [];
|
|
55
|
+
const shardCounts = {};
|
|
56
|
+
const shardDurMap = {}; // used duration per shard (ms)
|
|
57
|
+
for (const file of sortedFiles) {
|
|
58
|
+
const fullPath = path.join(folderPath, file);
|
|
59
|
+
try {
|
|
60
|
+
const shardRaw = fs.readFileSync(fullPath, "utf-8");
|
|
61
|
+
const shardData = JSON.parse(shardRaw);
|
|
62
|
+
// Validate results array
|
|
63
|
+
if (!Array.isArray(shardData.results)) {
|
|
64
|
+
console.warn(`Ortoni Report: Shard ${file} missing results array — skipping.`);
|
|
65
|
+
badShards.push(file);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Append all results (keep duplicates)
|
|
69
|
+
shardData.results.forEach((r) => allResults.push(r));
|
|
70
|
+
shardCounts[file] = shardData.results.length;
|
|
71
|
+
// Merge project sets
|
|
72
|
+
if (Array.isArray(shardData.projectSet)) {
|
|
73
|
+
shardData.projectSet.forEach((p) => projectSet.add(p));
|
|
74
|
+
}
|
|
75
|
+
// Duration handling:
|
|
76
|
+
// Accept shardData.totalDuration when it is a number (including 0).
|
|
77
|
+
// Fallback: if shardData.totalDuration is missing or not a number, sum per-test durations inside the shard.
|
|
78
|
+
const rawShardDur = shardData.totalDuration;
|
|
79
|
+
let durToAdd = 0;
|
|
80
|
+
let perTestSum = 0;
|
|
81
|
+
if (typeof rawShardDur === "number") {
|
|
82
|
+
// Accept numeric durations (including 0)
|
|
83
|
+
durToAdd = rawShardDur;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// fallback: sum per-test durations (coerce to Number)
|
|
87
|
+
perTestSum = Array.isArray(shardData.results)
|
|
88
|
+
? shardData.results.reduce((acc, t) => acc + (Number(t?.duration) || 0), 0)
|
|
89
|
+
: 0;
|
|
90
|
+
durToAdd = perTestSum;
|
|
91
|
+
}
|
|
92
|
+
// accumulate
|
|
93
|
+
totalDurationSum += durToAdd;
|
|
94
|
+
shardDurMap[file] = durToAdd;
|
|
95
|
+
// Merge userConfig/userMeta conservatively (prefer first non-empty value)
|
|
96
|
+
if (shardData.userConfig) {
|
|
97
|
+
if (!mergedUserConfig)
|
|
98
|
+
mergedUserConfig = { ...shardData.userConfig };
|
|
99
|
+
else {
|
|
100
|
+
Object.keys(shardData.userConfig).forEach((k) => {
|
|
101
|
+
if (mergedUserConfig[k] === undefined ||
|
|
102
|
+
mergedUserConfig[k] === null ||
|
|
103
|
+
mergedUserConfig[k] === "") {
|
|
104
|
+
mergedUserConfig[k] = shardData.userConfig[k];
|
|
105
|
+
}
|
|
106
|
+
else if (shardData.userConfig[k] !== mergedUserConfig[k]) {
|
|
107
|
+
console.warn(`Ortoni Report: userConfig mismatch for key "${k}" between shards. Using first value "${mergedUserConfig[k]}".`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (shardData.userMeta) {
|
|
113
|
+
if (!mergedUserMeta)
|
|
114
|
+
mergedUserMeta = { ...shardData.userMeta };
|
|
115
|
+
else {
|
|
116
|
+
mergedUserMeta.meta = {
|
|
117
|
+
...(mergedUserMeta.meta || {}),
|
|
118
|
+
...(shardData.userMeta.meta || {}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error(`Ortoni Report: Failed to parse shard ${file}:`, err);
|
|
125
|
+
badShards.push(file);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
} // end for each shard
|
|
129
|
+
if (badShards.length > 0) {
|
|
130
|
+
console.warn(`Ortoni Report: Completed merge with ${badShards.length} bad shard(s) skipped:`, badShards);
|
|
131
|
+
}
|
|
132
|
+
// final results preserved with duplicates
|
|
133
|
+
const totalDuration = totalDurationSum; // in ms
|
|
134
|
+
// Determine whether to persist history:
|
|
135
|
+
// Priority: explicit options.saveHistory -> mergedUserConfig.saveHistory -> default true
|
|
136
|
+
const saveHistoryFromOptions = typeof options.saveHistory === "boolean" ? options.saveHistory : undefined;
|
|
137
|
+
const saveHistoryFromShard = mergedUserConfig && typeof mergedUserConfig.saveHistory === "boolean"
|
|
138
|
+
? mergedUserConfig.saveHistory
|
|
139
|
+
: undefined;
|
|
140
|
+
const saveHistory = saveHistoryFromOptions ?? saveHistoryFromShard ?? true;
|
|
141
|
+
let dbManager;
|
|
142
|
+
let runId;
|
|
143
|
+
if (saveHistory) {
|
|
144
|
+
try {
|
|
145
|
+
dbManager = new DatabaseManager();
|
|
146
|
+
const dbPath = path.join(folderPath, "ortoni-data-history.sqlite");
|
|
147
|
+
await dbManager.initialize(dbPath);
|
|
148
|
+
runId = await dbManager.saveTestRun();
|
|
149
|
+
if (typeof runId === "number") {
|
|
150
|
+
// Save all results (your saveTestResults may batch internally)
|
|
151
|
+
await dbManager.saveTestResults(runId, allResults);
|
|
152
|
+
console.info(`Ortoni Report: Saved ${allResults.length} results to DB (runId=${runId}).`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.warn("Ortoni Report: Failed to create test run in DB; proceeding without saving results.");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
console.error("Ortoni Report: Error while saving history to DB. Proceeding without DB:", err);
|
|
160
|
+
dbManager = undefined;
|
|
161
|
+
runId = undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.info("Ortoni Report: Skipping history save (saveHistory=false). (Typical for CI runs)");
|
|
166
|
+
}
|
|
167
|
+
// Generate final report
|
|
168
|
+
const htmlGenerator = new HTMLGenerator({ ...(mergedUserConfig || {}), meta: mergedUserMeta?.meta }, dbManager);
|
|
169
|
+
const finalReportData = await htmlGenerator.generateFinalReport(
|
|
170
|
+
// filteredResults: typically filter out skipped for display (keeps existing behavior)
|
|
171
|
+
allResults.filter((r) => r.status !== "skipped"), totalDuration, allResults, projectSet // pass Set<string> as original generateFinalReport expects
|
|
172
|
+
);
|
|
173
|
+
// Write final HTML file
|
|
174
|
+
const fileManager = new FileManager(folderPath);
|
|
175
|
+
const outputFileName = options.file || "ortoni-report.html";
|
|
176
|
+
const outputPath = fileManager.writeReportFile(outputFileName, finalReportData);
|
|
177
|
+
// Logs & debugging summary
|
|
178
|
+
console.log(`✅ Final merged report generated at ${await outputPath}`);
|
|
179
|
+
console.log(`✅ Shards merged: ${sortedFiles.length}`);
|
|
180
|
+
console.log(`✅ Tests per shard:`, shardCounts);
|
|
181
|
+
console.log(`✅ Total tests merged ${allResults.length}`);
|
|
182
|
+
}
|
package/dist/ortoni-report.d.mts
CHANGED
|
@@ -78,6 +78,13 @@ interface OrtoniReportConfig {
|
|
|
78
78
|
* @example { "key": "value" } as string
|
|
79
79
|
*/
|
|
80
80
|
meta?: Record<string, string>;
|
|
81
|
+
/**
|
|
82
|
+
* Save the history of the reports in a SQL file to be used in future reports.
|
|
83
|
+
* The history file will be saved in the report folder.
|
|
84
|
+
* @default true
|
|
85
|
+
* @example false (to disable)
|
|
86
|
+
*/
|
|
87
|
+
saveHistory?: boolean;
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
declare class OrtoniReport implements Reporter {
|
|
@@ -96,7 +103,7 @@ declare class OrtoniReport implements Reporter {
|
|
|
96
103
|
private shouldGenerateReport;
|
|
97
104
|
private showConsoleLogs;
|
|
98
105
|
private skipTraceViewer;
|
|
99
|
-
private
|
|
106
|
+
private shardConfig;
|
|
100
107
|
constructor(ortoniConfig?: OrtoniReportConfig);
|
|
101
108
|
private reportsCount;
|
|
102
109
|
onBegin(config: FullConfig, _suite: Suite): Promise<void>;
|
package/dist/ortoni-report.d.ts
CHANGED
|
@@ -78,6 +78,13 @@ interface OrtoniReportConfig {
|
|
|
78
78
|
* @example { "key": "value" } as string
|
|
79
79
|
*/
|
|
80
80
|
meta?: Record<string, string>;
|
|
81
|
+
/**
|
|
82
|
+
* Save the history of the reports in a SQL file to be used in future reports.
|
|
83
|
+
* The history file will be saved in the report folder.
|
|
84
|
+
* @default true
|
|
85
|
+
* @example false (to disable)
|
|
86
|
+
*/
|
|
87
|
+
saveHistory?: boolean;
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
declare class OrtoniReport implements Reporter {
|
|
@@ -96,7 +103,7 @@ declare class OrtoniReport implements Reporter {
|
|
|
96
103
|
private shouldGenerateReport;
|
|
97
104
|
private showConsoleLogs;
|
|
98
105
|
private skipTraceViewer;
|
|
99
|
-
private
|
|
106
|
+
private shardConfig;
|
|
100
107
|
constructor(ortoniConfig?: OrtoniReportConfig);
|
|
101
108
|
private reportsCount;
|
|
102
109
|
onBegin(config: FullConfig, _suite: Suite): Promise<void>;
|