ortoni-report 4.0.0 → 4.0.1-beta.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.
@@ -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,6 +103,7 @@ declare class OrtoniReport implements Reporter {
96
103
  private shouldGenerateReport;
97
104
  private showConsoleLogs;
98
105
  private skipTraceViewer;
106
+ private shardConfig;
99
107
  constructor(ortoniConfig?: OrtoniReportConfig);
100
108
  private reportsCount;
101
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,6 +103,7 @@ declare class OrtoniReport implements Reporter {
96
103
  private shouldGenerateReport;
97
104
  private showConsoleLogs;
98
105
  private skipTraceViewer;
106
+ private shardConfig;
99
107
  constructor(ortoniConfig?: OrtoniReportConfig);
100
108
  private reportsCount;
101
109
  onBegin(config: FullConfig, _suite: Suite): Promise<void>;
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -38,62 +37,131 @@ __export(ortoni_report_exports, {
38
37
  module.exports = __toCommonJS(ortoni_report_exports);
39
38
 
40
39
  // src/helpers/fileManager.ts
40
+ var import_fs2 = __toESM(require("fs"));
41
+ var import_path2 = __toESM(require("path"));
42
+
43
+ // src/helpers/templateLoader.ts
41
44
  var import_fs = __toESM(require("fs"));
42
45
  var import_path = __toESM(require("path"));
46
+ var import_meta = {};
47
+ async function readBundledTemplate(pkgName = "ortoni-report") {
48
+ const packagedRel = "dist/index.html";
49
+ try {
50
+ if (typeof require === "function") {
51
+ const resolved = require.resolve(`${pkgName}/${packagedRel}`);
52
+ if (import_fs.default.existsSync(resolved)) {
53
+ return import_fs.default.readFileSync(resolved, "utf-8");
54
+ }
55
+ }
56
+ } catch {
57
+ }
58
+ try {
59
+ const moduleNS = await import("module");
60
+ if (moduleNS && typeof moduleNS.createRequire === "function") {
61
+ const createRequire = moduleNS.createRequire;
62
+ const req = createRequire(
63
+ // @ts-ignore
64
+ typeof __filename !== "undefined" ? __filename : import_meta.url
65
+ );
66
+ const resolved = req.resolve(`${pkgName}/${packagedRel}`);
67
+ if (import_fs.default.existsSync(resolved)) {
68
+ return import_fs.default.readFileSync(resolved, "utf-8");
69
+ }
70
+ }
71
+ } catch {
72
+ }
73
+ try {
74
+ const here = import_path.default.resolve(__dirname, "../dist/index.html");
75
+ if (import_fs.default.existsSync(here)) return import_fs.default.readFileSync(here, "utf-8");
76
+ } catch {
77
+ }
78
+ try {
79
+ const nm = import_path.default.join(process.cwd(), "node_modules", pkgName, packagedRel);
80
+ if (import_fs.default.existsSync(nm)) return import_fs.default.readFileSync(nm, "utf-8");
81
+ } catch {
82
+ }
83
+ try {
84
+ const alt = import_path.default.join(process.cwd(), "dist", "index.html");
85
+ if (import_fs.default.existsSync(alt)) return import_fs.default.readFileSync(alt, "utf-8");
86
+ } catch {
87
+ }
88
+ throw new Error(
89
+ `ortoni-report template not found (tried:
90
+ - require.resolve('${pkgName}/${packagedRel}')
91
+ - import('module').createRequire(...).resolve('${pkgName}/${packagedRel}')
92
+ - relative ../dist/index.html
93
+ - ${import_path.default.join(
94
+ process.cwd(),
95
+ "node_modules",
96
+ pkgName,
97
+ packagedRel
98
+ )}
99
+ - ${import_path.default.join(process.cwd(), "dist", "index.html")}
100
+ Ensure 'dist/index.html' is present in the published package and package.json 'files' includes 'dist/'.`
101
+ );
102
+ }
103
+
104
+ // src/helpers/fileManager.ts
43
105
  var FileManager = class {
44
106
  constructor(folderPath) {
45
107
  this.folderPath = folderPath;
46
108
  }
47
109
  ensureReportDirectory() {
48
- const ortoniDataFolder = import_path.default.join(this.folderPath, "ortoni-data");
49
- if (!import_fs.default.existsSync(this.folderPath)) {
50
- import_fs.default.mkdirSync(this.folderPath, { recursive: true });
110
+ const ortoniDataFolder = import_path2.default.join(this.folderPath, "ortoni-data");
111
+ if (!import_fs2.default.existsSync(this.folderPath)) {
112
+ import_fs2.default.mkdirSync(this.folderPath, { recursive: true });
51
113
  } else {
52
- if (import_fs.default.existsSync(ortoniDataFolder)) {
53
- import_fs.default.rmSync(ortoniDataFolder, { recursive: true, force: true });
114
+ if (import_fs2.default.existsSync(ortoniDataFolder)) {
115
+ import_fs2.default.rmSync(ortoniDataFolder, { recursive: true, force: true });
54
116
  }
55
117
  }
56
118
  }
57
- writeReportFile(filename, data) {
58
- const templatePath = import_path.default.resolve(__dirname, "index.html");
59
- let html = import_fs.default.readFileSync(templatePath, "utf-8");
119
+ async writeReportFile(filename, data) {
120
+ let html = await readBundledTemplate();
60
121
  const reportJSON = JSON.stringify({
61
122
  data
62
123
  });
63
124
  html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
64
- const outputPath = import_path.default.join(process.cwd(), this.folderPath, filename);
65
- import_fs.default.writeFileSync(outputPath, html);
125
+ const outputPath = import_path2.default.join(process.cwd(), this.folderPath, filename);
126
+ import_fs2.default.writeFileSync(outputPath, html);
127
+ return outputPath;
128
+ }
129
+ writeRawFile(filename, data) {
130
+ const outputPath = import_path2.default.join(process.cwd(), this.folderPath, filename);
131
+ import_fs2.default.mkdirSync(import_path2.default.dirname(outputPath), { recursive: true });
132
+ const content = typeof data === "string" ? data : JSON.stringify(data, null, 2);
133
+ import_fs2.default.writeFileSync(outputPath, content, "utf-8");
66
134
  return outputPath;
67
135
  }
68
136
  copyTraceViewerAssets(skip) {
69
137
  if (skip) return;
70
- const traceViewerFolder = import_path.default.join(
138
+ const traceViewerFolder = import_path2.default.join(
71
139
  require.resolve("playwright-core"),
72
140
  "..",
73
141
  "lib",
74
142
  "vite",
75
143
  "traceViewer"
76
144
  );
77
- const traceViewerTargetFolder = import_path.default.join(this.folderPath, "trace");
78
- const traceViewerAssetsTargetFolder = import_path.default.join(
145
+ const traceViewerTargetFolder = import_path2.default.join(this.folderPath, "trace");
146
+ const traceViewerAssetsTargetFolder = import_path2.default.join(
79
147
  traceViewerTargetFolder,
80
148
  "assets"
81
149
  );
82
- import_fs.default.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
83
- for (const file of import_fs.default.readdirSync(traceViewerFolder)) {
150
+ import_fs2.default.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
151
+ for (const file of import_fs2.default.readdirSync(traceViewerFolder)) {
84
152
  if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
85
153
  continue;
86
- import_fs.default.copyFileSync(
87
- import_path.default.join(traceViewerFolder, file),
88
- import_path.default.join(traceViewerTargetFolder, file)
154
+ import_fs2.default.copyFileSync(
155
+ import_path2.default.join(traceViewerFolder, file),
156
+ import_path2.default.join(traceViewerTargetFolder, file)
89
157
  );
90
158
  }
91
- const assetsFolder = import_path.default.join(traceViewerFolder, "assets");
92
- for (const file of import_fs.default.readdirSync(assetsFolder)) {
159
+ const assetsFolder = import_path2.default.join(traceViewerFolder, "assets");
160
+ for (const file of import_fs2.default.readdirSync(assetsFolder)) {
93
161
  if (file.endsWith(".map") || file.includes("xtermModule")) continue;
94
- import_fs.default.copyFileSync(
95
- import_path.default.join(assetsFolder, file),
96
- import_path.default.join(traceViewerAssetsTargetFolder, file)
162
+ import_fs2.default.copyFileSync(
163
+ import_path2.default.join(assetsFolder, file),
164
+ import_path2.default.join(traceViewerAssetsTargetFolder, file)
97
165
  );
98
166
  }
99
167
  }
@@ -142,13 +210,46 @@ var HTMLGenerator = class {
142
210
  );
143
211
  return data;
144
212
  }
213
+ /**
214
+ * Return safe analytics/report data.
215
+ * If no dbManager is provided, return empty defaults and a note explaining why.
216
+ */
145
217
  async getReportData() {
146
- return {
147
- summary: await this.dbManager.getSummaryData(),
148
- trends: await this.dbManager.getTrends(),
149
- flakyTests: await this.dbManager.getFlakyTests(),
150
- slowTests: await this.dbManager.getSlowTests()
151
- };
218
+ if (!this.dbManager) {
219
+ return {
220
+ summary: {},
221
+ trends: {},
222
+ flakyTests: [],
223
+ slowTests: [],
224
+ note: "Test history/trends are unavailable (saveHistory disabled or DB not initialized)."
225
+ };
226
+ }
227
+ try {
228
+ const [summary, trends, flakyTests, slowTests] = await Promise.all([
229
+ this.dbManager.getSummaryData ? this.dbManager.getSummaryData() : Promise.resolve({}),
230
+ this.dbManager.getTrends ? this.dbManager.getTrends() : Promise.resolve({}),
231
+ this.dbManager.getFlakyTests ? this.dbManager.getFlakyTests() : Promise.resolve([]),
232
+ this.dbManager.getSlowTests ? this.dbManager.getSlowTests() : Promise.resolve([])
233
+ ]);
234
+ return {
235
+ summary: summary ?? {},
236
+ trends: trends ?? {},
237
+ flakyTests: flakyTests ?? [],
238
+ slowTests: slowTests ?? []
239
+ };
240
+ } catch (err) {
241
+ console.warn(
242
+ "HTMLGenerator: failed to read analytics from DB, continuing without history.",
243
+ err
244
+ );
245
+ return {
246
+ summary: {},
247
+ trends: {},
248
+ flakyTests: [],
249
+ slowTests: [],
250
+ note: "Test history/trends could not be loaded due to a DB error."
251
+ };
252
+ }
152
253
  }
153
254
  async prepareReportData(filteredResults, totalDuration, results, projectSet) {
154
255
  const totalTests = filteredResults.length;
@@ -157,10 +258,10 @@ var HTMLGenerator = class {
157
258
  const failed = filteredResults.filter(
158
259
  (r) => r.status === "failed" || r.status === "timedOut"
159
260
  ).length;
160
- const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
261
+ const successRate = totalTests === 0 ? "0.00" : ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
161
262
  const allTags = /* @__PURE__ */ new Set();
162
263
  results.forEach(
163
- (result) => result.testTags.forEach((tag) => allTags.add(tag))
264
+ (result) => (result.testTags || []).forEach((tag) => allTags.add(tag))
164
265
  );
165
266
  const projectResults = this.calculateProjectResults(
166
267
  filteredResults,
@@ -171,13 +272,31 @@ var HTMLGenerator = class {
171
272
  const testHistories = await Promise.all(
172
273
  results.map(async (result) => {
173
274
  const testId = `${result.filePath}:${result.projectName}:${result.title}`;
174
- const history = await this.dbManager.getTestHistory(testId);
175
- return {
176
- testId,
177
- history
178
- };
275
+ if (!this.dbManager || !this.dbManager.getTestHistory) {
276
+ return {
277
+ testId,
278
+ history: []
279
+ };
280
+ }
281
+ try {
282
+ const history = await this.dbManager.getTestHistory(testId);
283
+ return {
284
+ testId,
285
+ history: history ?? []
286
+ };
287
+ } catch (err) {
288
+ console.warn(
289
+ `HTMLGenerator: failed to read history for ${testId}`,
290
+ err
291
+ );
292
+ return {
293
+ testId,
294
+ history: []
295
+ };
296
+ }
179
297
  })
180
298
  );
299
+ const reportData = await this.getReportData();
181
300
  return {
182
301
  summary: {
183
302
  overAllResult: {
@@ -209,13 +328,11 @@ var HTMLGenerator = class {
209
328
  meta: this.ortoniConfig.meta
210
329
  },
211
330
  preferences: {
212
- theme: this.ortoniConfig.preferredTheme,
213
331
  logo: this.ortoniConfig.logo || void 0,
214
332
  showProject: this.ortoniConfig.showProject || false
215
333
  },
216
334
  analytics: {
217
- reportData: await this.getReportData()
218
- // chartTrendData: await this.chartTrendData(),
335
+ reportData
219
336
  }
220
337
  };
221
338
  }
@@ -253,16 +370,16 @@ var HTMLGenerator = class {
253
370
  }
254
371
  };
255
372
 
256
- // src/helpers/resultProcessor .ts
373
+ // src/helpers/resultProcessor.ts
257
374
  var import_ansi_to_html = __toESM(require("ansi-to-html"));
258
- var import_path4 = __toESM(require("path"));
375
+ var import_path5 = __toESM(require("path"));
259
376
 
260
377
  // src/utils/attachFiles.ts
261
- var import_path2 = __toESM(require("path"));
262
- var import_fs3 = __toESM(require("fs"));
378
+ var import_path3 = __toESM(require("path"));
379
+ var import_fs4 = __toESM(require("fs"));
263
380
 
264
381
  // src/helpers/markdownConverter.ts
265
- var import_fs2 = __toESM(require("fs"));
382
+ var import_fs3 = __toESM(require("fs"));
266
383
 
267
384
  // node_modules/marked/lib/marked.esm.js
268
385
  function M() {
@@ -1388,27 +1505,27 @@ var Qt = b.lex;
1388
1505
 
1389
1506
  // src/helpers/markdownConverter.ts
1390
1507
  function convertMarkdownToHtml(markdownPath, htmlOutputPath) {
1391
- const hasMarkdown = import_fs2.default.existsSync(markdownPath);
1392
- const markdownContent = hasMarkdown ? import_fs2.default.readFileSync(markdownPath, "utf-8") : "";
1508
+ const hasMarkdown = import_fs3.default.existsSync(markdownPath);
1509
+ const markdownContent = hasMarkdown ? import_fs3.default.readFileSync(markdownPath, "utf-8") : "";
1393
1510
  const markdownHtml = hasMarkdown ? k(markdownContent) : "";
1394
1511
  const drawerHtml = `${markdownHtml || ""}`;
1395
- import_fs2.default.writeFileSync(htmlOutputPath, drawerHtml.trim(), "utf-8");
1512
+ import_fs3.default.writeFileSync(htmlOutputPath, drawerHtml.trim(), "utf-8");
1396
1513
  if (hasMarkdown) {
1397
- import_fs2.default.unlinkSync(markdownPath);
1514
+ import_fs3.default.unlinkSync(markdownPath);
1398
1515
  }
1399
1516
  }
1400
1517
 
1401
1518
  // src/utils/attachFiles.ts
1402
1519
  function attachFiles(subFolder, result, testResult, config, steps, errors) {
1403
1520
  const folderPath = config.folderPath || "ortoni-report";
1404
- const attachmentsFolder = import_path2.default.join(
1521
+ const attachmentsFolder = import_path3.default.join(
1405
1522
  folderPath,
1406
1523
  "ortoni-data",
1407
1524
  "attachments",
1408
1525
  subFolder
1409
1526
  );
1410
- if (!import_fs3.default.existsSync(attachmentsFolder)) {
1411
- import_fs3.default.mkdirSync(attachmentsFolder, { recursive: true });
1527
+ if (!import_fs4.default.existsSync(attachmentsFolder)) {
1528
+ import_fs4.default.mkdirSync(attachmentsFolder, { recursive: true });
1412
1529
  }
1413
1530
  if (!result.attachments) return;
1414
1531
  const { base64Image } = config;
@@ -1417,14 +1534,14 @@ function attachFiles(subFolder, result, testResult, config, steps, errors) {
1417
1534
  result.attachments.forEach((attachment) => {
1418
1535
  const { contentType, name, path: attachmentPath, body } = attachment;
1419
1536
  if (!attachmentPath && !body) return;
1420
- const fileName = attachmentPath ? import_path2.default.basename(attachmentPath) : `${name}.${getFileExtension(contentType)}`;
1421
- const relativePath = import_path2.default.join(
1537
+ const fileName = attachmentPath ? import_path3.default.basename(attachmentPath) : `${name}.${getFileExtension(contentType)}`;
1538
+ const relativePath = import_path3.default.join(
1422
1539
  "ortoni-data",
1423
1540
  "attachments",
1424
1541
  subFolder,
1425
1542
  fileName
1426
1543
  );
1427
- const fullPath = import_path2.default.join(attachmentsFolder, fileName);
1544
+ const fullPath = import_path3.default.join(attachmentsFolder, fileName);
1428
1545
  if (contentType === "image/png") {
1429
1546
  handleImage(
1430
1547
  attachmentPath,
@@ -1467,13 +1584,13 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
1467
1584
  let screenshotPath = "";
1468
1585
  if (attachmentPath) {
1469
1586
  try {
1470
- const screenshotContent = import_fs3.default.readFileSync(
1587
+ const screenshotContent = import_fs4.default.readFileSync(
1471
1588
  attachmentPath,
1472
1589
  base64Image ? "base64" : void 0
1473
1590
  );
1474
1591
  screenshotPath = base64Image ? `data:image/png;base64,${screenshotContent}` : relativePath;
1475
1592
  if (!base64Image) {
1476
- import_fs3.default.copyFileSync(attachmentPath, fullPath);
1593
+ import_fs4.default.copyFileSync(attachmentPath, fullPath);
1477
1594
  }
1478
1595
  } catch (error) {
1479
1596
  console.error(
@@ -1490,7 +1607,7 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
1490
1607
  }
1491
1608
  function handleAttachment(attachmentPath, fullPath, relativePath, resultKey, testResult, steps, errors) {
1492
1609
  if (attachmentPath) {
1493
- import_fs3.default.copyFileSync(attachmentPath, fullPath);
1610
+ import_fs4.default.copyFileSync(attachmentPath, fullPath);
1494
1611
  if (resultKey === "videoPath") {
1495
1612
  testResult[resultKey]?.push(relativePath);
1496
1613
  } else if (resultKey === "tracePath") {
@@ -1516,13 +1633,13 @@ function getFileExtension(contentType) {
1516
1633
  }
1517
1634
 
1518
1635
  // src/utils/utils.ts
1519
- var import_path3 = __toESM(require("path"));
1636
+ var import_path4 = __toESM(require("path"));
1520
1637
  function normalizeFilePath(filePath) {
1521
- const normalizedPath = import_path3.default.normalize(filePath);
1522
- return import_path3.default.basename(normalizedPath);
1638
+ const normalizedPath = import_path4.default.normalize(filePath);
1639
+ return import_path4.default.basename(normalizedPath);
1523
1640
  }
1524
1641
  function ensureHtmlExtension(filename) {
1525
- const ext = import_path3.default.extname(filename);
1642
+ const ext = import_path4.default.extname(filename);
1526
1643
  if (ext && ext.toLowerCase() === ".html") {
1527
1644
  return filename;
1528
1645
  }
@@ -1570,7 +1687,7 @@ function extractSuites(titlePath) {
1570
1687
  };
1571
1688
  }
1572
1689
 
1573
- // src/helpers/resultProcessor .ts
1690
+ // src/helpers/resultProcessor.ts
1574
1691
  var TestResultProcessor = class {
1575
1692
  constructor(projectRoot) {
1576
1693
  this.ansiToHtml = new import_ansi_to_html.default({ fg: "var(--snippet-color)" });
@@ -1626,7 +1743,7 @@ var TestResultProcessor = class {
1626
1743
  }
1627
1744
  processSteps(steps) {
1628
1745
  return steps.map((step) => {
1629
- const stepLocation = step.location ? `${import_path4.default.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
1746
+ const stepLocation = step.location ? `${import_path5.default.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
1630
1747
  return {
1631
1748
  snippet: this.ansiToHtml.toHtml(escapeHtml(step.error?.snippet || "")),
1632
1749
  title: step.title,
@@ -1638,16 +1755,16 @@ var TestResultProcessor = class {
1638
1755
 
1639
1756
  // src/utils/expressServer.ts
1640
1757
  var import_express = __toESM(require("express"));
1641
- var import_path5 = __toESM(require("path"));
1758
+ var import_path6 = __toESM(require("path"));
1642
1759
  var import_child_process = require("child_process");
1643
1760
  function startReportServer(reportFolder, reportFilename, port = 2004, open2) {
1644
1761
  const app = (0, import_express.default)();
1645
- app.use(import_express.default.static(reportFolder));
1762
+ app.use(import_express.default.static(reportFolder, { index: false }));
1646
1763
  app.get("/", (_req, res) => {
1647
1764
  try {
1648
- res.sendFile(import_path5.default.resolve(reportFolder, reportFilename));
1765
+ res.sendFile(import_path6.default.resolve(reportFolder, reportFilename));
1649
1766
  } catch (error) {
1650
- console.error("Ortoni-Report: Error sending report file:", error);
1767
+ console.error("Ortoni Report: Error sending report file:", error);
1651
1768
  res.status(500).send("Error loading report");
1652
1769
  }
1653
1770
  });
@@ -1661,21 +1778,21 @@ Press Ctrl+C to stop.`
1661
1778
  try {
1662
1779
  openBrowser(`http://localhost:${port}`);
1663
1780
  } catch (error) {
1664
- console.error("Ortoni-Report: Error opening browser:", error);
1781
+ console.error("Ortoni Report: Error opening browser:", error);
1665
1782
  }
1666
1783
  }
1667
1784
  });
1668
1785
  server.on("error", (error) => {
1669
1786
  if (error.code === "EADDRINUSE") {
1670
1787
  console.error(
1671
- `Ortoni-Report: Port ${port} is already in use. Trying a different port...`
1788
+ `Ortoni Report: Port ${port} is already in use. Trying a different port...`
1672
1789
  );
1673
1790
  } else {
1674
- console.error("Ortoni-Report: Server error:", error);
1791
+ console.error("Ortoni Report: Server error:", error);
1675
1792
  }
1676
1793
  });
1677
1794
  } catch (error) {
1678
- console.error("Ortoni-Report: Error starting the server:", error);
1795
+ console.error("Ortoni Report: Error starting the server:", error);
1679
1796
  }
1680
1797
  }
1681
1798
  function openBrowser(url) {
@@ -1693,7 +1810,7 @@ function openBrowser(url) {
1693
1810
  (0, import_child_process.spawn)(command, [url]);
1694
1811
  }
1695
1812
  } catch (error) {
1696
- console.error("Ortoni-Report: Error opening the browser:", error);
1813
+ console.error("Ortoni Report: Error opening the browser:", error);
1697
1814
  }
1698
1815
  }
1699
1816
 
@@ -1991,7 +2108,7 @@ var DatabaseManager = class {
1991
2108
  };
1992
2109
 
1993
2110
  // src/ortoni-report.ts
1994
- var import_path6 = __toESM(require("path"));
2111
+ var import_path7 = __toESM(require("path"));
1995
2112
  var OrtoniReport = class {
1996
2113
  constructor(ortoniConfig = {}) {
1997
2114
  this.ortoniConfig = ortoniConfig;
@@ -2022,8 +2139,9 @@ var OrtoniReport = class {
2022
2139
  this.testResultProcessor = new TestResultProcessor(config.rootDir);
2023
2140
  this.fileManager.ensureReportDirectory();
2024
2141
  await this.dbManager.initialize(
2025
- import_path6.default.join(this.folderPath, "ortoni-data-history.sqlite")
2142
+ import_path7.default.join(this.folderPath, "ortoni-data-history.sqlite")
2026
2143
  );
2144
+ this.shardConfig = config?.shard;
2027
2145
  }
2028
2146
  onStdOut(chunk, _test, _result) {
2029
2147
  if (this.reportsCount == 1 && this.showConsoleLogs) {
@@ -2040,7 +2158,7 @@ var OrtoniReport = class {
2040
2158
  );
2041
2159
  this.results.push(testResult);
2042
2160
  } catch (error) {
2043
- console.error("OrtoniReport: Error processing test end:", error);
2161
+ console.error("Ortoni Report: Error processing test end:", error);
2044
2162
  }
2045
2163
  }
2046
2164
  printsToStdio() {
@@ -2059,6 +2177,32 @@ var OrtoniReport = class {
2059
2177
  (r) => r.status !== "skipped"
2060
2178
  );
2061
2179
  const totalDuration = result.duration;
2180
+ if (this.shardConfig && this.shardConfig.total > 1) {
2181
+ const shard = this.shardConfig;
2182
+ const shardFile = `ortoni-shard-${shard.current}-of-${shard.total}.json`;
2183
+ const shardData = {
2184
+ status: result.status,
2185
+ duration: totalDuration,
2186
+ results: this.results,
2187
+ projectSet: Array.from(this.projectSet),
2188
+ userConfig: {
2189
+ projectName: this.ortoniConfig.projectName,
2190
+ authorName: this.ortoniConfig.authorName,
2191
+ type: this.ortoniConfig.testType,
2192
+ title: this.ortoniConfig.title
2193
+ },
2194
+ userMeta: {
2195
+ meta: this.ortoniConfig.meta
2196
+ }
2197
+ };
2198
+ const shardFilePath = this.fileManager.writeRawFile(
2199
+ shardFile,
2200
+ shardData
2201
+ );
2202
+ console.info(`Ortoni Report: Shard data written to ${shardFilePath}`);
2203
+ this.shouldGenerateReport = false;
2204
+ return;
2205
+ }
2062
2206
  const runId = await this.dbManager.saveTestRun();
2063
2207
  if (runId !== null) {
2064
2208
  await this.dbManager.saveTestResults(runId, this.results);
@@ -2068,21 +2212,21 @@ var OrtoniReport = class {
2068
2212
  this.results,
2069
2213
  this.projectSet
2070
2214
  );
2071
- this.outputPath = this.fileManager.writeReportFile(
2215
+ this.outputPath = await this.fileManager.writeReportFile(
2072
2216
  this.outputFilename,
2073
2217
  finalReportData
2074
2218
  );
2075
2219
  } else {
2076
- console.error("OrtoniReport: Error saving test run to database");
2220
+ console.error("Ortoni Report: Error saving test run to database");
2077
2221
  }
2078
2222
  } else {
2079
2223
  console.error(
2080
- "OrtoniReport: Report generation skipped due to error in Playwright worker!"
2224
+ "Ortoni Report: Report generation skipped due to error in Playwright worker!"
2081
2225
  );
2082
2226
  }
2083
2227
  } catch (error) {
2084
2228
  this.shouldGenerateReport = false;
2085
- console.error("OrtoniReport: Error generating report:", error);
2229
+ console.error("Ortoni Report: Error generating report:", error);
2086
2230
  }
2087
2231
  }
2088
2232
  async onExit() {
@@ -2090,7 +2234,7 @@ var OrtoniReport = class {
2090
2234
  await this.dbManager.close();
2091
2235
  if (this.shouldGenerateReport) {
2092
2236
  this.fileManager.copyTraceViewerAssets(this.skipTraceViewer);
2093
- console.info(`Ortoni HTML report generated at ${this.outputPath}`);
2237
+ console.info(`Ortoni Report generated at ${this.outputPath}`);
2094
2238
  this.serverManager.startServer(
2095
2239
  this.folderPath,
2096
2240
  this.outputFilename,
@@ -2100,7 +2244,7 @@ var OrtoniReport = class {
2100
2244
  });
2101
2245
  }
2102
2246
  } catch (error) {
2103
- console.error("OrtoniReport: Error in onExit:", error);
2247
+ console.error("Ortoni Report: Error in onExit:", error);
2104
2248
  }
2105
2249
  }
2106
2250
  };