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.
Files changed (62) hide show
  1. package/changelog.md +27 -0
  2. package/dist/{chunk-INS3E7E6.mjs → chunk-4RZ5C7KY.mjs} +402 -296
  3. package/dist/{cli/cli.js → cli.js} +405 -190
  4. package/dist/cli.mjs +208 -0
  5. package/dist/helpers/HTMLGenerator.d.ts +89 -0
  6. package/dist/helpers/HTMLGenerator.js +163 -0
  7. package/dist/helpers/databaseManager.d.ts +35 -0
  8. package/dist/helpers/databaseManager.js +268 -0
  9. package/dist/helpers/fileManager.d.ts +8 -0
  10. package/dist/helpers/fileManager.js +60 -0
  11. package/dist/helpers/markdownConverter.d.ts +1 -0
  12. package/dist/helpers/markdownConverter.js +14 -0
  13. package/dist/helpers/resultProcessor.d.ts +10 -0
  14. package/dist/helpers/resultProcessor.js +60 -0
  15. package/dist/helpers/serverManager.d.ts +6 -0
  16. package/dist/helpers/serverManager.js +15 -0
  17. package/dist/helpers/templateLoader.d.ts +15 -0
  18. package/dist/helpers/templateLoader.js +88 -0
  19. package/dist/index.html +1 -1
  20. package/dist/mergeData.d.ts +13 -0
  21. package/dist/mergeData.js +182 -0
  22. package/dist/ortoni-report.d.mts +8 -1
  23. package/dist/ortoni-report.d.ts +8 -1
  24. package/dist/ortoni-report.js +211 -96
  25. package/dist/ortoni-report.mjs +25 -22
  26. package/dist/{ortoni-report.d.cts → types/reporterConfig.d.ts} +8 -33
  27. package/dist/types/reporterConfig.js +1 -0
  28. package/dist/types/testResults.d.ts +31 -0
  29. package/dist/types/testResults.js +1 -0
  30. package/dist/utils/attachFiles.d.ts +4 -0
  31. package/dist/utils/attachFiles.js +87 -0
  32. package/dist/utils/expressServer.d.ts +1 -0
  33. package/dist/utils/expressServer.js +61 -0
  34. package/dist/utils/groupProjects.d.ts +3 -0
  35. package/dist/utils/groupProjects.js +30 -0
  36. package/dist/utils/utils.d.ts +15 -0
  37. package/dist/utils/utils.js +84 -0
  38. package/package.json +11 -4
  39. package/readme.md +60 -75
  40. package/dist/chunk-45EJSEX2.mjs +0 -632
  41. package/dist/chunk-75EAJL2U.mjs +0 -632
  42. package/dist/chunk-A6HCKATU.mjs +0 -76
  43. package/dist/chunk-FGIYOFIC.mjs +0 -632
  44. package/dist/chunk-FHKWBHU6.mjs +0 -633
  45. package/dist/chunk-GLICR3VS.mjs +0 -637
  46. package/dist/chunk-HFO6XSKC.mjs +0 -633
  47. package/dist/chunk-HOZD6YIV.mjs +0 -634
  48. package/dist/chunk-IJO2YIFE.mjs +0 -637
  49. package/dist/chunk-JEIWNUQY.mjs +0 -632
  50. package/dist/chunk-JPLAGYR7.mjs +0 -632
  51. package/dist/chunk-NM6ULN2O.mjs +0 -632
  52. package/dist/chunk-OZS6QIJS.mjs +0 -638
  53. package/dist/chunk-P57227VN.mjs +0 -633
  54. package/dist/chunk-QMTRYN5N.js +0 -635
  55. package/dist/chunk-TI33PMMQ.mjs +0 -639
  56. package/dist/chunk-Z5NBP5TS.mjs +0 -635
  57. package/dist/cli/cli.cjs +0 -678
  58. package/dist/cli/cli.d.cts +0 -1
  59. package/dist/cli/cli.mjs +0 -103
  60. package/dist/ortoni-report.cjs +0 -2134
  61. /package/dist/{cli/cli.d.mts → cli.d.mts} +0 -0
  62. /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
+ }
@@ -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 config;
106
+ private shardConfig;
100
107
  constructor(ortoniConfig?: OrtoniReportConfig);
101
108
  private reportsCount;
102
109
  onBegin(config: FullConfig, _suite: Suite): Promise<void>;
@@ -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 config;
106
+ private shardConfig;
100
107
  constructor(ortoniConfig?: OrtoniReportConfig);
101
108
  private reportsCount;
102
109
  onBegin(config: FullConfig, _suite: Suite): Promise<void>;