ortoni-report 4.0.2-beta.1 → 5.0.0-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/dist/cli/cli.js DELETED
@@ -1,698 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
- // If the importer is in node compatibility mode or this is not an ESM
19
- // file that has been converted to a CommonJS file using a Babel-
20
- // compatible transform (i.e. "__esModule" has not been set), then set
21
- // "default" to the CommonJS "module.exports" for node compatibility.
22
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
- mod
24
- ));
25
-
26
- // src/cli/cli.ts
27
- var import_commander = require("commander");
28
-
29
- // src/utils/expressServer.ts
30
- var import_express = __toESM(require("express"));
31
- var import_path = __toESM(require("path"));
32
- var import_child_process = require("child_process");
33
- function startReportServer(reportFolder, reportFilename, port = 2004, open2) {
34
- const app = (0, import_express.default)();
35
- app.use(import_express.default.static(reportFolder));
36
- app.get("/", (_req, res) => {
37
- try {
38
- res.sendFile(import_path.default.resolve(reportFolder, reportFilename));
39
- } catch (error) {
40
- console.error("Ortoni-Report: Error sending report file:", error);
41
- res.status(500).send("Error loading report");
42
- }
43
- });
44
- try {
45
- const server = app.listen(port, () => {
46
- console.log(
47
- `Server is running at http://localhost:${port}
48
- Press Ctrl+C to stop.`
49
- );
50
- if (open2 === "always" || open2 === "on-failure") {
51
- try {
52
- openBrowser(`http://localhost:${port}`);
53
- } catch (error) {
54
- console.error("Ortoni-Report: Error opening browser:", error);
55
- }
56
- }
57
- });
58
- server.on("error", (error) => {
59
- if (error.code === "EADDRINUSE") {
60
- console.error(
61
- `Ortoni-Report: Port ${port} is already in use. Trying a different port...`
62
- );
63
- } else {
64
- console.error("Ortoni-Report: Server error:", error);
65
- }
66
- });
67
- } catch (error) {
68
- console.error("Ortoni-Report: Error starting the server:", error);
69
- }
70
- }
71
- function openBrowser(url) {
72
- const platform = process.platform;
73
- let command;
74
- try {
75
- if (platform === "win32") {
76
- command = "cmd";
77
- (0, import_child_process.spawn)(command, ["/c", "start", url]);
78
- } else if (platform === "darwin") {
79
- command = "open";
80
- (0, import_child_process.spawn)(command, [url]);
81
- } else {
82
- command = "xdg-open";
83
- (0, import_child_process.spawn)(command, [url]);
84
- }
85
- } catch (error) {
86
- console.error("Ortoni-Report: Error opening the browser:", error);
87
- }
88
- }
89
-
90
- // src/cli/cli.ts
91
- var fs3 = __toESM(require("fs"));
92
- var path4 = __toESM(require("path"));
93
-
94
- // src/cli/mergeData.ts
95
- var fs2 = __toESM(require("fs"));
96
- var path3 = __toESM(require("path"));
97
-
98
- // src/helpers/databaseManager.ts
99
- var import_sqlite = require("sqlite");
100
- var import_sqlite3 = __toESM(require("sqlite3"));
101
-
102
- // src/utils/utils.ts
103
- function formatDateLocal(dateInput) {
104
- const date = typeof dateInput === "string" ? new Date(dateInput) : dateInput;
105
- const options = {
106
- year: "numeric",
107
- month: "short",
108
- day: "2-digit",
109
- hour: "2-digit",
110
- minute: "2-digit",
111
- hour12: true,
112
- timeZoneName: "short"
113
- // or "Asia/Kolkata"
114
- };
115
- return new Intl.DateTimeFormat(void 0, options).format(date);
116
- }
117
-
118
- // src/helpers/databaseManager.ts
119
- var DatabaseManager = class {
120
- constructor() {
121
- this.db = null;
122
- }
123
- async initialize(dbPath) {
124
- try {
125
- this.db = await (0, import_sqlite.open)({
126
- filename: dbPath,
127
- driver: import_sqlite3.default.Database
128
- });
129
- await this.createTables();
130
- await this.createIndexes();
131
- } catch (error) {
132
- console.error("OrtoniReport: Error initializing database:", error);
133
- }
134
- }
135
- async createTables() {
136
- if (!this.db) {
137
- console.error("OrtoniReport: Database not initialized");
138
- return;
139
- }
140
- try {
141
- await this.db.exec(`
142
- CREATE TABLE IF NOT EXISTS test_runs (
143
- id INTEGER PRIMARY KEY AUTOINCREMENT,
144
- run_date TEXT
145
- );
146
-
147
- CREATE TABLE IF NOT EXISTS test_results (
148
- id INTEGER PRIMARY KEY AUTOINCREMENT,
149
- run_id INTEGER,
150
- test_id TEXT,
151
- status TEXT,
152
- duration INTEGER, -- store duration as raw ms
153
- error_message TEXT,
154
- FOREIGN KEY (run_id) REFERENCES test_runs (id)
155
- );
156
- `);
157
- } catch (error) {
158
- console.error("OrtoniReport: Error creating tables:", error);
159
- }
160
- }
161
- async createIndexes() {
162
- if (!this.db) {
163
- console.error("OrtoniReport: Database not initialized");
164
- return;
165
- }
166
- try {
167
- await this.db.exec(`
168
- CREATE INDEX IF NOT EXISTS idx_test_id ON test_results (test_id);
169
- CREATE INDEX IF NOT EXISTS idx_run_id ON test_results (run_id);
170
- `);
171
- } catch (error) {
172
- console.error("OrtoniReport: Error creating indexes:", error);
173
- }
174
- }
175
- async saveTestRun() {
176
- if (!this.db) {
177
- console.error("OrtoniReport: Database not initialized");
178
- return null;
179
- }
180
- try {
181
- const runDate = (/* @__PURE__ */ new Date()).toISOString();
182
- const { lastID } = await this.db.run(
183
- `
184
- INSERT INTO test_runs (run_date)
185
- VALUES (?)
186
- `,
187
- [runDate]
188
- );
189
- return lastID;
190
- } catch (error) {
191
- console.error("OrtoniReport: Error saving test run:", error);
192
- return null;
193
- }
194
- }
195
- async saveTestResults(runId, results) {
196
- if (!this.db) {
197
- console.error("OrtoniReport: Database not initialized");
198
- return;
199
- }
200
- try {
201
- await this.db.exec("BEGIN TRANSACTION;");
202
- const stmt = await this.db.prepare(`
203
- INSERT INTO test_results (run_id, test_id, status, duration, error_message)
204
- VALUES (?, ?, ?, ?, ?)
205
- `);
206
- for (const result of results) {
207
- await stmt.run([
208
- runId,
209
- `${result.filePath}:${result.projectName}:${result.title}`,
210
- result.status,
211
- result.duration,
212
- // store raw ms
213
- result.errors.join("\n")
214
- ]);
215
- }
216
- await stmt.finalize();
217
- await this.db.exec("COMMIT;");
218
- } catch (error) {
219
- await this.db.exec("ROLLBACK;");
220
- console.error("OrtoniReport: Error saving test results:", error);
221
- }
222
- }
223
- async getTestHistory(testId, limit = 10) {
224
- if (!this.db) {
225
- console.error("OrtoniReport: Database not initialized");
226
- return [];
227
- }
228
- try {
229
- const results = await this.db.all(
230
- `
231
- SELECT tr.status, tr.duration, tr.error_message, trun.run_date
232
- FROM test_results tr
233
- JOIN test_runs trun ON tr.run_id = trun.id
234
- WHERE tr.test_id = ?
235
- ORDER BY trun.run_date DESC
236
- LIMIT ?
237
- `,
238
- [testId, limit]
239
- );
240
- return results.map((result) => ({
241
- ...result,
242
- run_date: formatDateLocal(result.run_date)
243
- }));
244
- } catch (error) {
245
- console.error("OrtoniReport: Error getting test history:", error);
246
- return [];
247
- }
248
- }
249
- async close() {
250
- if (this.db) {
251
- try {
252
- await this.db.close();
253
- } catch (error) {
254
- console.error("OrtoniReport: Error closing database:", error);
255
- } finally {
256
- this.db = null;
257
- }
258
- }
259
- }
260
- async getSummaryData() {
261
- if (!this.db) {
262
- console.error("OrtoniReport: Database not initialized");
263
- return {
264
- totalRuns: 0,
265
- totalTests: 0,
266
- passed: 0,
267
- failed: 0,
268
- passRate: 0,
269
- avgDuration: 0
270
- };
271
- }
272
- try {
273
- const summary = await this.db.get(`
274
- SELECT
275
- (SELECT COUNT(*) FROM test_runs) as totalRuns,
276
- (SELECT COUNT(*) FROM test_results) as totalTests,
277
- (SELECT COUNT(*) FROM test_results WHERE status = 'passed') as passed,
278
- (SELECT COUNT(*) FROM test_results WHERE status = 'failed') as failed,
279
- (SELECT AVG(duration) FROM test_results) as avgDuration
280
- `);
281
- const passRate = summary.totalTests ? (summary.passed / summary.totalTests * 100).toFixed(2) : 0;
282
- return {
283
- totalRuns: summary.totalRuns,
284
- totalTests: summary.totalTests,
285
- passed: summary.passed,
286
- failed: summary.failed,
287
- passRate: parseFloat(passRate.toString()),
288
- avgDuration: Math.round(summary.avgDuration || 0)
289
- // raw ms avg
290
- };
291
- } catch (error) {
292
- console.error("OrtoniReport: Error getting summary data:", error);
293
- return {
294
- totalRuns: 0,
295
- totalTests: 0,
296
- passed: 0,
297
- failed: 0,
298
- passRate: 0,
299
- avgDuration: 0
300
- };
301
- }
302
- }
303
- async getTrends(limit = 100) {
304
- if (!this.db) {
305
- console.error("OrtoniReport: Database not initialized");
306
- return [];
307
- }
308
- try {
309
- const rows = await this.db.all(
310
- `
311
- SELECT trun.run_date,
312
- SUM(CASE WHEN tr.status = 'passed' THEN 1 ELSE 0 END) AS passed,
313
- SUM(CASE WHEN tr.status = 'failed' THEN 1 ELSE 0 END) AS failed,
314
- AVG(tr.duration) AS avg_duration
315
- FROM test_results tr
316
- JOIN test_runs trun ON tr.run_id = trun.id
317
- GROUP BY trun.run_date
318
- ORDER BY trun.run_date DESC
319
- LIMIT ?
320
- `,
321
- [limit]
322
- );
323
- return rows.reverse().map((row) => ({
324
- ...row,
325
- run_date: formatDateLocal(row.run_date),
326
- avg_duration: Math.round(row.avg_duration || 0)
327
- // raw ms avg
328
- }));
329
- } catch (error) {
330
- console.error("OrtoniReport: Error getting trends data:", error);
331
- return [];
332
- }
333
- }
334
- async getFlakyTests(limit = 10) {
335
- if (!this.db) {
336
- console.error("OrtoniReport: Database not initialized");
337
- return [];
338
- }
339
- try {
340
- return await this.db.all(
341
- `
342
- SELECT
343
- test_id,
344
- COUNT(*) AS total,
345
- SUM(CASE WHEN status = 'flaky' THEN 1 ELSE 0 END) AS flaky,
346
- AVG(duration) AS avg_duration
347
- FROM test_results
348
- GROUP BY test_id
349
- HAVING flaky > 0
350
- ORDER BY flaky DESC
351
- LIMIT ?
352
- `,
353
- [limit]
354
- );
355
- } catch (error) {
356
- console.error("OrtoniReport: Error getting flaky tests:", error);
357
- return [];
358
- }
359
- }
360
- async getSlowTests(limit = 10) {
361
- if (!this.db) {
362
- console.error("OrtoniReport: Database not initialized");
363
- return [];
364
- }
365
- try {
366
- const rows = await this.db.all(
367
- `
368
- SELECT
369
- test_id,
370
- AVG(duration) AS avg_duration
371
- FROM test_results
372
- GROUP BY test_id
373
- ORDER BY avg_duration DESC
374
- LIMIT ?
375
- `,
376
- [limit]
377
- );
378
- return rows.map((row) => ({
379
- test_id: row.test_id,
380
- avg_duration: Math.round(row.avg_duration || 0)
381
- // raw ms avg
382
- }));
383
- } catch (error) {
384
- console.error("OrtoniReport: Error getting slow tests:", error);
385
- return [];
386
- }
387
- }
388
- };
389
-
390
- // src/helpers/fileManager.ts
391
- var import_fs = __toESM(require("fs"));
392
- var import_path2 = __toESM(require("path"));
393
- var FileManager = class {
394
- constructor(folderPath) {
395
- this.folderPath = folderPath;
396
- }
397
- ensureReportDirectory() {
398
- const ortoniDataFolder = import_path2.default.join(this.folderPath, "ortoni-data");
399
- if (!import_fs.default.existsSync(this.folderPath)) {
400
- import_fs.default.mkdirSync(this.folderPath, { recursive: true });
401
- } else {
402
- if (import_fs.default.existsSync(ortoniDataFolder)) {
403
- import_fs.default.rmSync(ortoniDataFolder, { recursive: true, force: true });
404
- }
405
- }
406
- }
407
- writeReportFile(filename, data) {
408
- const templatePath = import_path2.default.join(__dirname, "..", "index.html");
409
- console.log("temp path - " + templatePath);
410
- let html = import_fs.default.readFileSync(templatePath, "utf-8");
411
- const reportJSON = JSON.stringify({
412
- data
413
- });
414
- html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
415
- import_fs.default.writeFileSync(filename, html);
416
- return filename;
417
- }
418
- writeRawFile(filename, data) {
419
- const outputPath = import_path2.default.join(process.cwd(), this.folderPath, filename);
420
- import_fs.default.mkdirSync(import_path2.default.dirname(outputPath), { recursive: true });
421
- const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
422
- import_fs.default.writeFileSync(outputPath, content, "utf-8");
423
- return outputPath;
424
- }
425
- copyTraceViewerAssets(skip) {
426
- if (skip) return;
427
- const traceViewerFolder = import_path2.default.join(
428
- require.resolve("playwright-core"),
429
- "..",
430
- "lib",
431
- "vite",
432
- "traceViewer"
433
- );
434
- const traceViewerTargetFolder = import_path2.default.join(this.folderPath, "trace");
435
- const traceViewerAssetsTargetFolder = import_path2.default.join(
436
- traceViewerTargetFolder,
437
- "assets"
438
- );
439
- import_fs.default.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
440
- for (const file of import_fs.default.readdirSync(traceViewerFolder)) {
441
- if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
442
- continue;
443
- import_fs.default.copyFileSync(
444
- import_path2.default.join(traceViewerFolder, file),
445
- import_path2.default.join(traceViewerTargetFolder, file)
446
- );
447
- }
448
- const assetsFolder = import_path2.default.join(traceViewerFolder, "assets");
449
- for (const file of import_fs.default.readdirSync(assetsFolder)) {
450
- if (file.endsWith(".map") || file.includes("xtermModule")) continue;
451
- import_fs.default.copyFileSync(
452
- import_path2.default.join(assetsFolder, file),
453
- import_path2.default.join(traceViewerAssetsTargetFolder, file)
454
- );
455
- }
456
- }
457
- };
458
-
459
- // src/utils/groupProjects.ts
460
- function groupResults(config, results) {
461
- if (config.showProject) {
462
- const groupedResults = results.reduce((acc, result, index) => {
463
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
464
- const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
465
- const { filePath, suite, projectName } = result;
466
- acc[filePath] = acc[filePath] || {};
467
- acc[filePath][suite] = acc[filePath][suite] || {};
468
- acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
469
- acc[filePath][suite][projectName].push({ ...result, index, testId, key });
470
- return acc;
471
- }, {});
472
- return groupedResults;
473
- } else {
474
- const groupedResults = results.reduce((acc, result, index) => {
475
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
476
- const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
477
- const { filePath, suite } = result;
478
- acc[filePath] = acc[filePath] || {};
479
- acc[filePath][suite] = acc[filePath][suite] || [];
480
- acc[filePath][suite].push({ ...result, index, testId, key });
481
- return acc;
482
- }, {});
483
- return groupedResults;
484
- }
485
- }
486
-
487
- // src/helpers/HTMLGenerator.ts
488
- var HTMLGenerator = class {
489
- constructor(ortoniConfig, dbManager) {
490
- this.ortoniConfig = ortoniConfig;
491
- this.dbManager = dbManager;
492
- }
493
- async generateFinalReport(filteredResults, totalDuration, results, projectSet) {
494
- const data = await this.prepareReportData(
495
- filteredResults,
496
- totalDuration,
497
- results,
498
- projectSet
499
- );
500
- return data;
501
- }
502
- async getReportData() {
503
- return {
504
- summary: await this.dbManager.getSummaryData(),
505
- trends: await this.dbManager.getTrends(),
506
- flakyTests: await this.dbManager.getFlakyTests(),
507
- slowTests: await this.dbManager.getSlowTests()
508
- };
509
- }
510
- async prepareReportData(filteredResults, totalDuration, results, projectSet) {
511
- const totalTests = filteredResults.length;
512
- const passedTests = results.filter((r) => r.status === "passed").length;
513
- const flakyTests = results.filter((r) => r.status === "flaky").length;
514
- const failed = filteredResults.filter(
515
- (r) => r.status === "failed" || r.status === "timedOut"
516
- ).length;
517
- const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
518
- const allTags = /* @__PURE__ */ new Set();
519
- results.forEach(
520
- (result) => result.testTags.forEach((tag) => allTags.add(tag))
521
- );
522
- const projectResults = this.calculateProjectResults(
523
- filteredResults,
524
- results,
525
- projectSet
526
- );
527
- const lastRunDate = (/* @__PURE__ */ new Date()).toLocaleString();
528
- const testHistories = await Promise.all(
529
- results.map(async (result) => {
530
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
531
- const history = await this.dbManager.getTestHistory(testId);
532
- return {
533
- testId,
534
- history
535
- };
536
- })
537
- );
538
- return {
539
- summary: {
540
- overAllResult: {
541
- pass: passedTests,
542
- fail: failed,
543
- skip: results.filter((r) => r.status === "skipped").length,
544
- retry: results.filter((r) => r.retryAttemptCount).length,
545
- flaky: flakyTests,
546
- total: filteredResults.length
547
- },
548
- successRate,
549
- lastRunDate,
550
- totalDuration,
551
- stats: this.extractProjectStats(projectResults)
552
- },
553
- testResult: {
554
- tests: groupResults(this.ortoniConfig, results),
555
- testHistories,
556
- allTags: Array.from(allTags),
557
- set: projectSet
558
- },
559
- userConfig: {
560
- projectName: this.ortoniConfig.projectName,
561
- authorName: this.ortoniConfig.authorName,
562
- type: this.ortoniConfig.testType,
563
- title: this.ortoniConfig.title
564
- },
565
- userMeta: {
566
- meta: this.ortoniConfig.meta
567
- },
568
- preferences: {
569
- logo: this.ortoniConfig.logo || void 0,
570
- showProject: this.ortoniConfig.showProject || false
571
- },
572
- analytics: {
573
- reportData: await this.getReportData()
574
- }
575
- };
576
- }
577
- calculateProjectResults(filteredResults, results, projectSet) {
578
- return Array.from(projectSet).map((projectName) => {
579
- const projectTests = filteredResults.filter(
580
- (r) => r.projectName === projectName
581
- );
582
- const allProjectTests = results.filter(
583
- (r) => r.projectName === projectName
584
- );
585
- return {
586
- projectName,
587
- passedTests: projectTests.filter((r) => r.status === "passed").length,
588
- failedTests: projectTests.filter(
589
- (r) => r.status === "failed" || r.status === "timedOut"
590
- ).length,
591
- skippedTests: allProjectTests.filter((r) => r.status === "skipped").length,
592
- retryTests: allProjectTests.filter((r) => r.retryAttemptCount).length,
593
- flakyTests: allProjectTests.filter((r) => r.status === "flaky").length,
594
- totalTests: projectTests.length
595
- };
596
- });
597
- }
598
- extractProjectStats(projectResults) {
599
- return {
600
- projectNames: projectResults.map((result) => result.projectName),
601
- totalTests: projectResults.map((result) => result.totalTests),
602
- passedTests: projectResults.map((result) => result.passedTests),
603
- failedTests: projectResults.map((result) => result.failedTests),
604
- skippedTests: projectResults.map((result) => result.skippedTests),
605
- retryTests: projectResults.map((result) => result.retryTests),
606
- flakyTests: projectResults.map((result) => result.flakyTests)
607
- };
608
- }
609
- };
610
-
611
- // src/cli/mergeData.ts
612
- async function mergerData(options) {
613
- const projectRoot = process.cwd();
614
- const folderPath = path3.resolve(projectRoot, options.dir);
615
- console.info(`Ortoni Report: Merging shard files in folder: ${folderPath}`);
616
- const shardFiles = fs2.readdirSync(folderPath).filter((f) => f.startsWith("ortoni-shard-") && f.endsWith(".json"));
617
- if (shardFiles.length === 0) {
618
- console.error("Ortoni Report: \u274C No shard files found to merge.");
619
- process.exit(1);
620
- }
621
- let allResults = [];
622
- let projectSet = /* @__PURE__ */ new Set();
623
- let totalDuration = 0;
624
- let mergedUserConfig = null;
625
- let mergedUserMeta = null;
626
- for (const file of shardFiles) {
627
- const shardData = JSON.parse(
628
- fs2.readFileSync(path3.join(folderPath, file), "utf-8")
629
- );
630
- allResults.push(...shardData.results);
631
- shardData.projectSet.forEach((p) => projectSet.add(p));
632
- totalDuration += shardData.duration;
633
- if (!mergedUserConfig && shardData.userConfig)
634
- mergedUserConfig = shardData.userConfig;
635
- if (!mergedUserMeta && shardData.userMeta)
636
- mergedUserMeta = shardData.userMeta;
637
- }
638
- const dbManager = new DatabaseManager();
639
- await dbManager.initialize(
640
- path3.join(folderPath, "ortoni-data-history.sqlite")
641
- );
642
- const runId = await dbManager.saveTestRun();
643
- if (typeof runId === "number") {
644
- await dbManager.saveTestResults(runId, allResults);
645
- } else {
646
- console.error("Ortoni Report: \u274C Failed to save test run to database.");
647
- }
648
- const htmlGenerator = new HTMLGenerator(
649
- { ...mergedUserConfig, meta: mergedUserMeta?.meta },
650
- dbManager
651
- );
652
- const finalReportData = await htmlGenerator.generateFinalReport(
653
- allResults.filter((r) => r.status !== "skipped"),
654
- totalDuration,
655
- allResults,
656
- projectSet
657
- );
658
- const fileManager = new FileManager(folderPath);
659
- const outputFileName = options.file || "ortoni-report.html";
660
- const outputFilenamePath = path3.join(folderPath, outputFileName);
661
- const outputPath = fileManager.writeReportFile(
662
- outputFilenamePath,
663
- finalReportData
664
- );
665
- console.log(`\u2705 Final merged report generated at ${outputPath}`);
666
- }
667
-
668
- // src/cli/cli.ts
669
- import_commander.program.version("4.0.1").description("Ortoni Report - CLI");
670
- import_commander.program.command("show-report").description("Open Ortoni Report").option(
671
- "-d, --dir <path>",
672
- "Path to the folder containing the report",
673
- "ortoni-report"
674
- ).option(
675
- "-f, --file <filename>",
676
- "Name of the report file",
677
- "ortoni-report.html"
678
- ).option("-p, --port <port>", "Port to run the server", "2004").action((options) => {
679
- const projectRoot = process.cwd();
680
- const folderPath = path4.resolve(projectRoot, options.dir);
681
- const filePath = path4.resolve(folderPath, options.file);
682
- const port = parseInt(options.port) || 2004;
683
- if (!fs3.existsSync(filePath)) {
684
- console.error(
685
- `\u274C Error: The file "${filePath}" does not exist in "${folderPath}".`
686
- );
687
- process.exit(1);
688
- }
689
- startReportServer(folderPath, path4.basename(filePath), port, "always");
690
- });
691
- import_commander.program.command("merge-report").description("Merge sharded reports into one final report").option(
692
- "-d, --dir <path>",
693
- "Path to the folder containing shard files",
694
- "ortoni-report"
695
- ).option("-f, --file <filename>", "Output report file", "ortoni-report.html").action(async (options) => {
696
- await mergerData(options);
697
- });
698
- import_commander.program.parse(process.argv);