ortoni-report 4.0.2-beta.1 → 4.1.0-test.1

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/cli.mjs ADDED
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DatabaseManager,
4
+ FileManager,
5
+ HTMLGenerator,
6
+ startReportServer
7
+ } from "./chunk-4RZ5C7KY.mjs";
8
+
9
+ // src/cli.ts
10
+ import { program } from "commander";
11
+ import * as fs2 from "fs";
12
+ import * as path2 from "path";
13
+
14
+ // src/mergeData.ts
15
+ import * as fs from "fs";
16
+ import * as path from "path";
17
+ async function mergeAllData(options = {}) {
18
+ const folderPath = options.dir || "ortoni-report";
19
+ console.info(`Ortoni Report: Merging shard files in folder: ${folderPath}`);
20
+ if (!fs.existsSync(folderPath)) {
21
+ console.error(`Ortoni Report: folder "${folderPath}" does not exist.`);
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+ const filenames = fs.readdirSync(folderPath).filter((f) => f.startsWith("ortoni-shard-") && f.endsWith(".json"));
26
+ if (filenames.length === 0) {
27
+ console.error("Ortoni Report: \u274C No shard files found to merge.");
28
+ process.exitCode = 1;
29
+ return;
30
+ }
31
+ const shardFileIndex = (name) => {
32
+ const m = name.match(/ortoni-shard-(\d+)-of-(\d+)\.json$/);
33
+ return m ? parseInt(m[1], 10) : null;
34
+ };
35
+ const sortedFiles = filenames.map((f) => ({ f, idx: shardFileIndex(f) })).sort((a, b) => {
36
+ if (a.idx === null && b.idx === null) return a.f.localeCompare(b.f);
37
+ if (a.idx === null) return 1;
38
+ if (b.idx === null) return -1;
39
+ return a.idx - b.idx;
40
+ }).map((x) => x.f);
41
+ const allResults = [];
42
+ const projectSet = /* @__PURE__ */ new Set();
43
+ let totalDurationSum = 0;
44
+ let totalDurationMax = 0;
45
+ let mergedUserConfig = null;
46
+ let mergedUserMeta = null;
47
+ const badShards = [];
48
+ const shardCounts = {};
49
+ const shardDurMap = {};
50
+ for (const file of sortedFiles) {
51
+ const fullPath = path.join(folderPath, file);
52
+ try {
53
+ const shardRaw = fs.readFileSync(fullPath, "utf-8");
54
+ const shardData = JSON.parse(shardRaw);
55
+ if (!Array.isArray(shardData.results)) {
56
+ console.warn(
57
+ `Ortoni Report: Shard ${file} missing results array \u2014 skipping.`
58
+ );
59
+ badShards.push(file);
60
+ continue;
61
+ }
62
+ shardData.results.forEach((r) => allResults.push(r));
63
+ shardCounts[file] = shardData.results.length;
64
+ if (Array.isArray(shardData.projectSet)) {
65
+ shardData.projectSet.forEach((p) => projectSet.add(p));
66
+ }
67
+ const rawShardDur = shardData.duration;
68
+ let durToAdd = 0;
69
+ if (typeof rawShardDur === "number") {
70
+ durToAdd = rawShardDur;
71
+ } else {
72
+ durToAdd = Array.isArray(shardData.results) ? shardData.results.reduce(
73
+ (acc, t) => acc + (Number(t?.duration) || 0),
74
+ 0
75
+ ) : 0;
76
+ }
77
+ shardDurMap[file] = durToAdd;
78
+ totalDurationSum += durToAdd;
79
+ if (durToAdd > totalDurationMax) totalDurationMax = durToAdd;
80
+ if (shardData.userConfig) {
81
+ if (!mergedUserConfig) mergedUserConfig = { ...shardData.userConfig };
82
+ else {
83
+ Object.keys(shardData.userConfig).forEach((k) => {
84
+ if (mergedUserConfig[k] === void 0 || mergedUserConfig[k] === null || mergedUserConfig[k] === "") {
85
+ mergedUserConfig[k] = shardData.userConfig[k];
86
+ } else if (shardData.userConfig[k] !== mergedUserConfig[k]) {
87
+ console.warn(
88
+ `Ortoni Report: userConfig mismatch for key "${k}" between shards. Using first value "${mergedUserConfig[k]}".`
89
+ );
90
+ }
91
+ });
92
+ }
93
+ }
94
+ if (shardData.userMeta) {
95
+ if (!mergedUserMeta) mergedUserMeta = { ...shardData.userMeta };
96
+ else {
97
+ mergedUserMeta.meta = {
98
+ ...mergedUserMeta.meta || {},
99
+ ...shardData.userMeta.meta || {}
100
+ };
101
+ }
102
+ }
103
+ } catch (err) {
104
+ console.error(`Ortoni Report: Failed to parse shard ${file}:`, err);
105
+ badShards.push(file);
106
+ continue;
107
+ }
108
+ }
109
+ if (badShards.length > 0) {
110
+ console.warn(
111
+ `Ortoni Report: Completed merge with ${badShards.length} bad shard(s) skipped:`,
112
+ badShards
113
+ );
114
+ }
115
+ const totalDuration = totalDurationSum;
116
+ const saveHistoryFromOptions = typeof options.saveHistory === "boolean" ? options.saveHistory : void 0;
117
+ const saveHistoryFromShard = mergedUserConfig && typeof mergedUserConfig.saveHistory === "boolean" ? mergedUserConfig.saveHistory : void 0;
118
+ const saveHistory = saveHistoryFromOptions ?? saveHistoryFromShard ?? true;
119
+ let dbManager;
120
+ let runId;
121
+ if (saveHistory) {
122
+ try {
123
+ dbManager = new DatabaseManager();
124
+ const dbPath = path.join(folderPath, "ortoni-data-history.sqlite");
125
+ await dbManager.initialize(dbPath);
126
+ runId = await dbManager.saveTestRun();
127
+ if (typeof runId === "number") {
128
+ await dbManager.saveTestResults(runId, allResults);
129
+ console.info(
130
+ `Ortoni Report: Saved ${allResults.length} results to DB (runId=${runId}).`
131
+ );
132
+ } else {
133
+ console.warn(
134
+ "Ortoni Report: Failed to create test run in DB; proceeding without saving results."
135
+ );
136
+ }
137
+ } catch (err) {
138
+ console.error(
139
+ "Ortoni Report: Error while saving history to DB. Proceeding without DB:",
140
+ err
141
+ );
142
+ dbManager = void 0;
143
+ runId = void 0;
144
+ }
145
+ } else {
146
+ console.info(
147
+ "Ortoni Report: Skipping history save (saveHistory=false). (Typical for CI runs)"
148
+ );
149
+ }
150
+ const htmlGenerator = new HTMLGenerator(
151
+ { ...mergedUserConfig || {}, meta: mergedUserMeta?.meta },
152
+ dbManager
153
+ );
154
+ const finalReportData = await htmlGenerator.generateFinalReport(
155
+ // filteredResults: typically filter out skipped for display (keeps existing behavior)
156
+ allResults.filter((r) => r.status !== "skipped"),
157
+ totalDuration,
158
+ allResults,
159
+ projectSet
160
+ // pass Set<string> as original generateFinalReport expects
161
+ );
162
+ const fileManager = new FileManager(folderPath);
163
+ const outputFileName = options.file || "ortoni-report.html";
164
+ const outputPath = fileManager.writeReportFile(
165
+ outputFileName,
166
+ finalReportData
167
+ );
168
+ console.log(`\u2705 Final merged report generated at ${outputPath}`);
169
+ console.log(`\u2705 Shards merged: ${sortedFiles.length}`);
170
+ console.log(`\u2705 Tests per shard:`, shardCounts);
171
+ console.log(`\u2705 Total tests merged ${allResults.length}`);
172
+ }
173
+
174
+ // src/cli.ts
175
+ program.version("4.0.1").description("Ortoni Report - CLI");
176
+ program.command("show-report").description("Open Ortoni Report").option(
177
+ "-d, --dir <path>",
178
+ "Path to the folder containing the report",
179
+ "ortoni-report"
180
+ ).option(
181
+ "-f, --file <filename>",
182
+ "Name of the report file",
183
+ "ortoni-report.html"
184
+ ).option("-p, --port <port>", "Port to run the server", "2004").action((options) => {
185
+ const projectRoot = process.cwd();
186
+ const folderPath = path2.resolve(projectRoot, options.dir);
187
+ const filePath = path2.resolve(folderPath, options.file);
188
+ const port = parseInt(options.port) || 2004;
189
+ if (!fs2.existsSync(filePath)) {
190
+ console.error(
191
+ `\u274C Error: The file "${filePath}" does not exist in "${folderPath}".`
192
+ );
193
+ process.exit(1);
194
+ }
195
+ startReportServer(folderPath, path2.basename(filePath), port, "always");
196
+ });
197
+ program.command("merge-report").description("Merge sharded reports into one final report").option(
198
+ "-d, --dir <path>",
199
+ "Path to the folder containing shard files",
200
+ "ortoni-report"
201
+ ).option("-f, --file <filename>", "Output report file", "ortoni-report.html").action(async (options) => {
202
+ await mergeAllData({
203
+ dir: options.dir,
204
+ file: options.file,
205
+ saveHistory: false
206
+ });
207
+ });
208
+ program.parse(process.argv);