ortoni-report 4.0.2-beta.0 → 4.0.2

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.
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
5
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -23,72 +22,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
22
  mod
24
23
  ));
25
24
 
26
- // src/cli/cli.ts
25
+ // src/cli.ts
27
26
  var import_commander = require("commander");
27
+ var fs4 = __toESM(require("fs"));
28
+ var path5 = __toESM(require("path"));
28
29
 
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 fs2 = __toESM(require("fs"));
30
+ // src/mergeData.ts
31
+ var fs3 = __toESM(require("fs"));
92
32
  var path3 = __toESM(require("path"));
93
33
 
94
34
  // src/helpers/databaseManager.ts
@@ -426,13 +366,46 @@ var HTMLGenerator = class {
426
366
  );
427
367
  return data;
428
368
  }
369
+ /**
370
+ * Return safe analytics/report data.
371
+ * If no dbManager is provided, return empty defaults and a note explaining why.
372
+ */
429
373
  async getReportData() {
430
- return {
431
- summary: await this.dbManager.getSummaryData(),
432
- trends: await this.dbManager.getTrends(),
433
- flakyTests: await this.dbManager.getFlakyTests(),
434
- slowTests: await this.dbManager.getSlowTests()
435
- };
374
+ if (!this.dbManager) {
375
+ return {
376
+ summary: {},
377
+ trends: {},
378
+ flakyTests: [],
379
+ slowTests: [],
380
+ note: "Test history/trends are unavailable (saveHistory disabled or DB not initialized)."
381
+ };
382
+ }
383
+ try {
384
+ const [summary, trends, flakyTests, slowTests] = await Promise.all([
385
+ this.dbManager.getSummaryData ? this.dbManager.getSummaryData() : Promise.resolve({}),
386
+ this.dbManager.getTrends ? this.dbManager.getTrends() : Promise.resolve({}),
387
+ this.dbManager.getFlakyTests ? this.dbManager.getFlakyTests() : Promise.resolve([]),
388
+ this.dbManager.getSlowTests ? this.dbManager.getSlowTests() : Promise.resolve([])
389
+ ]);
390
+ return {
391
+ summary: summary ?? {},
392
+ trends: trends ?? {},
393
+ flakyTests: flakyTests ?? [],
394
+ slowTests: slowTests ?? []
395
+ };
396
+ } catch (err) {
397
+ console.warn(
398
+ "HTMLGenerator: failed to read analytics from DB, continuing without history.",
399
+ err
400
+ );
401
+ return {
402
+ summary: {},
403
+ trends: {},
404
+ flakyTests: [],
405
+ slowTests: [],
406
+ note: "Test history/trends could not be loaded due to a DB error."
407
+ };
408
+ }
436
409
  }
437
410
  async prepareReportData(filteredResults, totalDuration, results, projectSet) {
438
411
  const totalTests = filteredResults.length;
@@ -441,10 +414,10 @@ var HTMLGenerator = class {
441
414
  const failed = filteredResults.filter(
442
415
  (r) => r.status === "failed" || r.status === "timedOut"
443
416
  ).length;
444
- const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
417
+ const successRate = totalTests === 0 ? "0.00" : ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
445
418
  const allTags = /* @__PURE__ */ new Set();
446
419
  results.forEach(
447
- (result) => result.testTags.forEach((tag) => allTags.add(tag))
420
+ (result) => (result.testTags || []).forEach((tag) => allTags.add(tag))
448
421
  );
449
422
  const projectResults = this.calculateProjectResults(
450
423
  filteredResults,
@@ -455,13 +428,31 @@ var HTMLGenerator = class {
455
428
  const testHistories = await Promise.all(
456
429
  results.map(async (result) => {
457
430
  const testId = `${result.filePath}:${result.projectName}:${result.title}`;
458
- const history = await this.dbManager.getTestHistory(testId);
459
- return {
460
- testId,
461
- history
462
- };
431
+ if (!this.dbManager || !this.dbManager.getTestHistory) {
432
+ return {
433
+ testId,
434
+ history: []
435
+ };
436
+ }
437
+ try {
438
+ const history = await this.dbManager.getTestHistory(testId);
439
+ return {
440
+ testId,
441
+ history: history ?? []
442
+ };
443
+ } catch (err) {
444
+ console.warn(
445
+ `HTMLGenerator: failed to read history for ${testId}`,
446
+ err
447
+ );
448
+ return {
449
+ testId,
450
+ history: []
451
+ };
452
+ }
463
453
  })
464
454
  );
455
+ const reportData = await this.getReportData();
465
456
  return {
466
457
  summary: {
467
458
  overAllResult: {
@@ -497,7 +488,7 @@ var HTMLGenerator = class {
497
488
  showProject: this.ortoniConfig.showProject || false
498
489
  },
499
490
  analytics: {
500
- reportData: await this.getReportData()
491
+ reportData
501
492
  }
502
493
  };
503
494
  }
@@ -536,38 +527,100 @@ var HTMLGenerator = class {
536
527
  };
537
528
 
538
529
  // src/helpers/fileManager.ts
539
- var import_fs = __toESM(require("fs"));
530
+ var import_fs2 = __toESM(require("fs"));
540
531
  var import_path2 = __toESM(require("path"));
532
+
533
+ // src/helpers/templateLoader.ts
534
+ var import_fs = __toESM(require("fs"));
535
+ var import_path = __toESM(require("path"));
536
+ var import_meta = {};
537
+ async function readBundledTemplate(pkgName = "ortoni-report") {
538
+ const packagedRel = "dist/index.html";
539
+ try {
540
+ if (typeof require === "function") {
541
+ const resolved = require.resolve(`${pkgName}/${packagedRel}`);
542
+ if (import_fs.default.existsSync(resolved)) {
543
+ return import_fs.default.readFileSync(resolved, "utf-8");
544
+ }
545
+ }
546
+ } catch {
547
+ }
548
+ try {
549
+ const moduleNS = await import("module");
550
+ if (moduleNS && typeof moduleNS.createRequire === "function") {
551
+ const createRequire = moduleNS.createRequire;
552
+ const req = createRequire(
553
+ // @ts-ignore
554
+ typeof __filename !== "undefined" ? __filename : import_meta.url
555
+ );
556
+ const resolved = req.resolve(`${pkgName}/${packagedRel}`);
557
+ if (import_fs.default.existsSync(resolved)) {
558
+ return import_fs.default.readFileSync(resolved, "utf-8");
559
+ }
560
+ }
561
+ } catch {
562
+ }
563
+ try {
564
+ const here = import_path.default.resolve(__dirname, "../dist/index.html");
565
+ if (import_fs.default.existsSync(here)) return import_fs.default.readFileSync(here, "utf-8");
566
+ } catch {
567
+ }
568
+ try {
569
+ const nm = import_path.default.join(process.cwd(), "node_modules", pkgName, packagedRel);
570
+ if (import_fs.default.existsSync(nm)) return import_fs.default.readFileSync(nm, "utf-8");
571
+ } catch {
572
+ }
573
+ try {
574
+ const alt = import_path.default.join(process.cwd(), "dist", "index.html");
575
+ if (import_fs.default.existsSync(alt)) return import_fs.default.readFileSync(alt, "utf-8");
576
+ } catch {
577
+ }
578
+ throw new Error(
579
+ `ortoni-report template not found (tried:
580
+ - require.resolve('${pkgName}/${packagedRel}')
581
+ - import('module').createRequire(...).resolve('${pkgName}/${packagedRel}')
582
+ - relative ../dist/index.html
583
+ - ${import_path.default.join(
584
+ process.cwd(),
585
+ "node_modules",
586
+ pkgName,
587
+ packagedRel
588
+ )}
589
+ - ${import_path.default.join(process.cwd(), "dist", "index.html")}
590
+ Ensure 'dist/index.html' is present in the published package and package.json 'files' includes 'dist/'.`
591
+ );
592
+ }
593
+
594
+ // src/helpers/fileManager.ts
541
595
  var FileManager = class {
542
596
  constructor(folderPath) {
543
597
  this.folderPath = folderPath;
544
598
  }
545
599
  ensureReportDirectory() {
546
600
  const ortoniDataFolder = import_path2.default.join(this.folderPath, "ortoni-data");
547
- if (!import_fs.default.existsSync(this.folderPath)) {
548
- import_fs.default.mkdirSync(this.folderPath, { recursive: true });
601
+ if (!import_fs2.default.existsSync(this.folderPath)) {
602
+ import_fs2.default.mkdirSync(this.folderPath, { recursive: true });
549
603
  } else {
550
- if (import_fs.default.existsSync(ortoniDataFolder)) {
551
- import_fs.default.rmSync(ortoniDataFolder, { recursive: true, force: true });
604
+ if (import_fs2.default.existsSync(ortoniDataFolder)) {
605
+ import_fs2.default.rmSync(ortoniDataFolder, { recursive: true, force: true });
552
606
  }
553
607
  }
554
608
  }
555
- writeReportFile(filename, data) {
556
- const templatePath = import_path2.default.join(__dirname, "..", "index.html");
557
- console.log("temp path - " + templatePath);
558
- let html = import_fs.default.readFileSync(templatePath, "utf-8");
609
+ async writeReportFile(filename, data) {
610
+ let html = await readBundledTemplate();
559
611
  const reportJSON = JSON.stringify({
560
612
  data
561
613
  });
562
614
  html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
563
- import_fs.default.writeFileSync(filename, html);
564
- return filename;
615
+ const outputPath = import_path2.default.join(process.cwd(), this.folderPath, filename);
616
+ import_fs2.default.writeFileSync(outputPath, html);
617
+ return outputPath;
565
618
  }
566
619
  writeRawFile(filename, data) {
567
620
  const outputPath = import_path2.default.join(process.cwd(), this.folderPath, filename);
568
- import_fs.default.mkdirSync(import_path2.default.dirname(outputPath), { recursive: true });
621
+ import_fs2.default.mkdirSync(import_path2.default.dirname(outputPath), { recursive: true });
569
622
  const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
570
- import_fs.default.writeFileSync(outputPath, content, "utf-8");
623
+ import_fs2.default.writeFileSync(outputPath, content, "utf-8");
571
624
  return outputPath;
572
625
  }
573
626
  copyTraceViewerAssets(skip) {
@@ -584,19 +637,19 @@ var FileManager = class {
584
637
  traceViewerTargetFolder,
585
638
  "assets"
586
639
  );
587
- import_fs.default.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
588
- for (const file of import_fs.default.readdirSync(traceViewerFolder)) {
640
+ import_fs2.default.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
641
+ for (const file of import_fs2.default.readdirSync(traceViewerFolder)) {
589
642
  if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
590
643
  continue;
591
- import_fs.default.copyFileSync(
644
+ import_fs2.default.copyFileSync(
592
645
  import_path2.default.join(traceViewerFolder, file),
593
646
  import_path2.default.join(traceViewerTargetFolder, file)
594
647
  );
595
648
  }
596
649
  const assetsFolder = import_path2.default.join(traceViewerFolder, "assets");
597
- for (const file of import_fs.default.readdirSync(assetsFolder)) {
650
+ for (const file of import_fs2.default.readdirSync(assetsFolder)) {
598
651
  if (file.endsWith(".map") || file.includes("xtermModule")) continue;
599
- import_fs.default.copyFileSync(
652
+ import_fs2.default.copyFileSync(
600
653
  import_path2.default.join(assetsFolder, file),
601
654
  import_path2.default.join(traceViewerAssetsTargetFolder, file)
602
655
  );
@@ -604,9 +657,228 @@ var FileManager = class {
604
657
  }
605
658
  };
606
659
 
607
- // src/cli/cli.ts
608
- import_commander.program.version("2.0.9").description("Ortoni Playwright Test Report CLI");
609
- import_commander.program.command("show-report").description("Open the generated Ortoni report").option(
660
+ // src/mergeData.ts
661
+ async function mergeAllData(options = {}) {
662
+ const folderPath = options.dir || "ortoni-report";
663
+ console.info(`Ortoni Report: Merging shard files in folder: ${folderPath}`);
664
+ if (!fs3.existsSync(folderPath)) {
665
+ console.error(`Ortoni Report: folder "${folderPath}" does not exist.`);
666
+ process.exitCode = 1;
667
+ return;
668
+ }
669
+ const filenames = fs3.readdirSync(folderPath).filter((f) => f.startsWith("ortoni-shard-") && f.endsWith(".json"));
670
+ if (filenames.length === 0) {
671
+ console.error("Ortoni Report: \u274C No shard files found to merge.");
672
+ process.exitCode = 1;
673
+ return;
674
+ }
675
+ const shardFileIndex = (name) => {
676
+ const m = name.match(/ortoni-shard-(\d+)-of-(\d+)\.json$/);
677
+ return m ? parseInt(m[1], 10) : null;
678
+ };
679
+ const sortedFiles = filenames.map((f) => ({ f, idx: shardFileIndex(f) })).sort((a, b) => {
680
+ if (a.idx === null && b.idx === null) return a.f.localeCompare(b.f);
681
+ if (a.idx === null) return 1;
682
+ if (b.idx === null) return -1;
683
+ return a.idx - b.idx;
684
+ }).map((x) => x.f);
685
+ const allResults = [];
686
+ const projectSet = /* @__PURE__ */ new Set();
687
+ let totalDurationSum = 0;
688
+ let mergedUserConfig = null;
689
+ let mergedUserMeta = null;
690
+ const badShards = [];
691
+ const shardCounts = {};
692
+ const shardDurMap = {};
693
+ for (const file of sortedFiles) {
694
+ const fullPath = path3.join(folderPath, file);
695
+ try {
696
+ const shardRaw = fs3.readFileSync(fullPath, "utf-8");
697
+ const shardData = JSON.parse(shardRaw);
698
+ if (!Array.isArray(shardData.results)) {
699
+ console.warn(
700
+ `Ortoni Report: Shard ${file} missing results array \u2014 skipping.`
701
+ );
702
+ badShards.push(file);
703
+ continue;
704
+ }
705
+ shardData.results.forEach((r) => allResults.push(r));
706
+ shardCounts[file] = shardData.results.length;
707
+ if (Array.isArray(shardData.projectSet)) {
708
+ shardData.projectSet.forEach((p) => projectSet.add(p));
709
+ }
710
+ const rawShardDur = shardData.totalDuration;
711
+ let durToAdd = 0;
712
+ let perTestSum = 0;
713
+ if (typeof rawShardDur === "number") {
714
+ durToAdd = rawShardDur;
715
+ } else {
716
+ perTestSum = Array.isArray(shardData.results) ? shardData.results.reduce(
717
+ (acc, t) => acc + (Number(t?.duration) || 0),
718
+ 0
719
+ ) : 0;
720
+ durToAdd = perTestSum;
721
+ }
722
+ totalDurationSum += durToAdd;
723
+ shardDurMap[file] = durToAdd;
724
+ if (shardData.userConfig) {
725
+ if (!mergedUserConfig) mergedUserConfig = { ...shardData.userConfig };
726
+ else {
727
+ Object.keys(shardData.userConfig).forEach((k) => {
728
+ if (mergedUserConfig[k] === void 0 || mergedUserConfig[k] === null || mergedUserConfig[k] === "") {
729
+ mergedUserConfig[k] = shardData.userConfig[k];
730
+ } else if (shardData.userConfig[k] !== mergedUserConfig[k]) {
731
+ console.warn(
732
+ `Ortoni Report: userConfig mismatch for key "${k}" between shards. Using first value "${mergedUserConfig[k]}".`
733
+ );
734
+ }
735
+ });
736
+ }
737
+ }
738
+ if (shardData.userMeta) {
739
+ if (!mergedUserMeta) mergedUserMeta = { ...shardData.userMeta };
740
+ else {
741
+ mergedUserMeta.meta = {
742
+ ...mergedUserMeta.meta || {},
743
+ ...shardData.userMeta.meta || {}
744
+ };
745
+ }
746
+ }
747
+ } catch (err) {
748
+ console.error(`Ortoni Report: Failed to parse shard ${file}:`, err);
749
+ badShards.push(file);
750
+ continue;
751
+ }
752
+ }
753
+ if (badShards.length > 0) {
754
+ console.warn(
755
+ `Ortoni Report: Completed merge with ${badShards.length} bad shard(s) skipped:`,
756
+ badShards
757
+ );
758
+ }
759
+ const totalDuration = totalDurationSum;
760
+ const saveHistoryFromOptions = typeof options.saveHistory === "boolean" ? options.saveHistory : void 0;
761
+ const saveHistoryFromShard = mergedUserConfig && typeof mergedUserConfig.saveHistory === "boolean" ? mergedUserConfig.saveHistory : void 0;
762
+ const saveHistory = saveHistoryFromOptions ?? saveHistoryFromShard ?? true;
763
+ let dbManager;
764
+ let runId;
765
+ if (saveHistory) {
766
+ try {
767
+ dbManager = new DatabaseManager();
768
+ const dbPath = path3.join(folderPath, "ortoni-data-history.sqlite");
769
+ await dbManager.initialize(dbPath);
770
+ runId = await dbManager.saveTestRun();
771
+ if (typeof runId === "number") {
772
+ await dbManager.saveTestResults(runId, allResults);
773
+ console.info(
774
+ `Ortoni Report: Saved ${allResults.length} results to DB (runId=${runId}).`
775
+ );
776
+ } else {
777
+ console.warn(
778
+ "Ortoni Report: Failed to create test run in DB; proceeding without saving results."
779
+ );
780
+ }
781
+ } catch (err) {
782
+ console.error(
783
+ "Ortoni Report: Error while saving history to DB. Proceeding without DB:",
784
+ err
785
+ );
786
+ dbManager = void 0;
787
+ runId = void 0;
788
+ }
789
+ } else {
790
+ console.info(
791
+ "Ortoni Report: Skipping history save (saveHistory=false). (Typical for CI runs)"
792
+ );
793
+ }
794
+ const htmlGenerator = new HTMLGenerator(
795
+ { ...mergedUserConfig || {}, meta: mergedUserMeta?.meta },
796
+ dbManager
797
+ );
798
+ const finalReportData = await htmlGenerator.generateFinalReport(
799
+ // filteredResults: typically filter out skipped for display (keeps existing behavior)
800
+ allResults.filter((r) => r.status !== "skipped"),
801
+ totalDuration,
802
+ allResults,
803
+ projectSet
804
+ // pass Set<string> as original generateFinalReport expects
805
+ );
806
+ const fileManager = new FileManager(folderPath);
807
+ const outputFileName = options.file || "ortoni-report.html";
808
+ const outputPath = fileManager.writeReportFile(
809
+ outputFileName,
810
+ finalReportData
811
+ );
812
+ console.log(`\u2705 Final merged report generated at ${await outputPath}`);
813
+ console.log(`\u2705 Shards merged: ${sortedFiles.length}`);
814
+ console.log(`\u2705 Tests per shard:`, shardCounts);
815
+ console.log(`\u2705 Total tests merged ${allResults.length}`);
816
+ }
817
+
818
+ // src/utils/expressServer.ts
819
+ var import_express = __toESM(require("express"));
820
+ var import_path3 = __toESM(require("path"));
821
+ var import_child_process = require("child_process");
822
+ function startReportServer(reportFolder, reportFilename, port = 2004, open2) {
823
+ const app = (0, import_express.default)();
824
+ app.use(import_express.default.static(reportFolder, { index: false }));
825
+ app.get("/", (_req, res) => {
826
+ try {
827
+ res.sendFile(import_path3.default.resolve(reportFolder, reportFilename));
828
+ } catch (error) {
829
+ console.error("Ortoni Report: Error sending report file:", error);
830
+ res.status(500).send("Error loading report");
831
+ }
832
+ });
833
+ try {
834
+ const server = app.listen(port, () => {
835
+ console.log(
836
+ `Server is running at http://localhost:${port}
837
+ Press Ctrl+C to stop.`
838
+ );
839
+ if (open2 === "always" || open2 === "on-failure") {
840
+ try {
841
+ openBrowser(`http://localhost:${port}`);
842
+ } catch (error) {
843
+ console.error("Ortoni Report: Error opening browser:", error);
844
+ }
845
+ }
846
+ });
847
+ server.on("error", (error) => {
848
+ if (error.code === "EADDRINUSE") {
849
+ console.error(
850
+ `Ortoni Report: Port ${port} is already in use. Trying a different port...`
851
+ );
852
+ } else {
853
+ console.error("Ortoni Report: Server error:", error);
854
+ }
855
+ });
856
+ } catch (error) {
857
+ console.error("Ortoni Report: Error starting the server:", error);
858
+ }
859
+ }
860
+ function openBrowser(url) {
861
+ const platform = process.platform;
862
+ let command;
863
+ try {
864
+ if (platform === "win32") {
865
+ command = "cmd";
866
+ (0, import_child_process.spawn)(command, ["/c", "start", url]);
867
+ } else if (platform === "darwin") {
868
+ command = "open";
869
+ (0, import_child_process.spawn)(command, [url]);
870
+ } else {
871
+ command = "xdg-open";
872
+ (0, import_child_process.spawn)(command, [url]);
873
+ }
874
+ } catch (error) {
875
+ console.error("Ortoni Report: Error opening the browser:", error);
876
+ }
877
+ }
878
+
879
+ // src/cli.ts
880
+ import_commander.program.version("4.0.1").description("Ortoni Report - CLI");
881
+ import_commander.program.command("show-report").description("Open Ortoni Report").option(
610
882
  "-d, --dir <path>",
611
883
  "Path to the folder containing the report",
612
884
  "ortoni-report"
@@ -616,60 +888,26 @@ import_commander.program.command("show-report").description("Open the generated
616
888
  "ortoni-report.html"
617
889
  ).option("-p, --port <port>", "Port to run the server", "2004").action((options) => {
618
890
  const projectRoot = process.cwd();
619
- const folderPath = path3.resolve(projectRoot, options.dir);
620
- const filePath = path3.resolve(folderPath, options.file);
891
+ const folderPath = path5.resolve(projectRoot, options.dir);
892
+ const filePath = path5.resolve(folderPath, options.file);
621
893
  const port = parseInt(options.port) || 2004;
622
- if (!fs2.existsSync(filePath)) {
894
+ if (!fs4.existsSync(filePath)) {
623
895
  console.error(
624
896
  `\u274C Error: The file "${filePath}" does not exist in "${folderPath}".`
625
897
  );
626
898
  process.exit(1);
627
899
  }
628
- startReportServer(folderPath, path3.basename(filePath), port, "always");
900
+ startReportServer(folderPath, path5.basename(filePath), port, "always");
629
901
  });
630
- import_commander.program.command("merge-shards").description("Merge sharded reports into one final report").option(
902
+ import_commander.program.command("merge-report").description("Merge sharded reports into one final report").option(
631
903
  "-d, --dir <path>",
632
904
  "Path to the folder containing shard files",
633
905
  "ortoni-report"
634
906
  ).option("-f, --file <filename>", "Output report file", "ortoni-report.html").action(async (options) => {
635
- const projectRoot = process.cwd();
636
- const folderPath = path3.resolve(projectRoot, options.dir);
637
- console.log("folder - " + folderPath);
638
- console.log(`Merging shard files in folder: ${folderPath}`);
639
- const filePath = path3.resolve(folderPath, options.file);
640
- const shardFiles = fs2.readdirSync(folderPath).filter((f) => f.startsWith("ortoni-shard-") && f.endsWith(".json"));
641
- if (shardFiles.length === 0) {
642
- console.error("\u274C No shard files found to merge.");
643
- process.exit(1);
644
- }
645
- let allResults = [];
646
- let projectSet = /* @__PURE__ */ new Set();
647
- let totalDuration = 0;
648
- for (const file of shardFiles) {
649
- const shardData = JSON.parse(
650
- fs2.readFileSync(path3.join(folderPath, file), "utf-8")
651
- );
652
- allResults.push(...shardData.results);
653
- shardData.projectSet.forEach((p) => projectSet.add(p));
654
- totalDuration += shardData.duration;
655
- }
656
- const dbManager = new DatabaseManager();
657
- await dbManager.initialize(
658
- path3.join(folderPath, "ortoni-data-history.sqlite")
659
- );
660
- const runId = await dbManager.saveTestRun();
661
- if (typeof runId === "number") {
662
- await dbManager.saveTestResults(runId, allResults);
663
- }
664
- const htmlGenerator = new HTMLGenerator({}, dbManager);
665
- const finalReportData = await htmlGenerator.generateFinalReport(
666
- allResults.filter((r) => r.status !== "skipped"),
667
- totalDuration,
668
- allResults,
669
- projectSet
670
- );
671
- const fileManager = new FileManager(folderPath);
672
- const outputPath = fileManager.writeReportFile(filePath, finalReportData);
673
- console.log(`\u2705 Final merged report generated at ${outputPath}`);
907
+ await mergeAllData({
908
+ dir: options.dir,
909
+ file: options.file,
910
+ saveHistory: false
911
+ });
674
912
  });
675
913
  import_commander.program.parse(process.argv);