ortoni-report 4.0.0 → 4.0.2-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.
@@ -1,235 +1,25 @@
1
1
  import {
2
+ DatabaseManager,
3
+ FileManager,
4
+ HTMLGenerator,
2
5
  __publicField,
3
- __require,
6
+ ensureHtmlExtension,
7
+ escapeHtml,
8
+ extractSuites,
9
+ normalizeFilePath,
4
10
  startReportServer
5
- } from "./chunk-A6HCKATU.mjs";
6
-
7
- // src/helpers/fileManager.ts
8
- import fs from "fs";
9
- import path from "path";
10
- var FileManager = class {
11
- constructor(folderPath) {
12
- this.folderPath = folderPath;
13
- }
14
- ensureReportDirectory() {
15
- const ortoniDataFolder = path.join(this.folderPath, "ortoni-data");
16
- if (!fs.existsSync(this.folderPath)) {
17
- fs.mkdirSync(this.folderPath, { recursive: true });
18
- } else {
19
- if (fs.existsSync(ortoniDataFolder)) {
20
- fs.rmSync(ortoniDataFolder, { recursive: true, force: true });
21
- }
22
- }
23
- }
24
- writeReportFile(filename, data) {
25
- const templatePath = path.resolve(__dirname, "index.html");
26
- let html = fs.readFileSync(templatePath, "utf-8");
27
- const reportJSON = JSON.stringify({
28
- data
29
- });
30
- html = html.replace("__ORTONI_TEST_REPORTDATA__", reportJSON);
31
- const outputPath = path.join(process.cwd(), this.folderPath, filename);
32
- fs.writeFileSync(outputPath, html);
33
- return outputPath;
34
- }
35
- copyTraceViewerAssets(skip) {
36
- if (skip) return;
37
- const traceViewerFolder = path.join(
38
- __require.resolve("playwright-core"),
39
- "..",
40
- "lib",
41
- "vite",
42
- "traceViewer"
43
- );
44
- const traceViewerTargetFolder = path.join(this.folderPath, "trace");
45
- const traceViewerAssetsTargetFolder = path.join(
46
- traceViewerTargetFolder,
47
- "assets"
48
- );
49
- fs.mkdirSync(traceViewerAssetsTargetFolder, { recursive: true });
50
- for (const file of fs.readdirSync(traceViewerFolder)) {
51
- if (file.endsWith(".map") || file.includes("watch") || file.includes("assets"))
52
- continue;
53
- fs.copyFileSync(
54
- path.join(traceViewerFolder, file),
55
- path.join(traceViewerTargetFolder, file)
56
- );
57
- }
58
- const assetsFolder = path.join(traceViewerFolder, "assets");
59
- for (const file of fs.readdirSync(assetsFolder)) {
60
- if (file.endsWith(".map") || file.includes("xtermModule")) continue;
61
- fs.copyFileSync(
62
- path.join(assetsFolder, file),
63
- path.join(traceViewerAssetsTargetFolder, file)
64
- );
65
- }
66
- }
67
- };
68
-
69
- // src/utils/groupProjects.ts
70
- function groupResults(config, results) {
71
- if (config.showProject) {
72
- const groupedResults = results.reduce((acc, result, index) => {
73
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
74
- const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
75
- const { filePath, suite, projectName } = result;
76
- acc[filePath] = acc[filePath] || {};
77
- acc[filePath][suite] = acc[filePath][suite] || {};
78
- acc[filePath][suite][projectName] = acc[filePath][suite][projectName] || [];
79
- acc[filePath][suite][projectName].push({ ...result, index, testId, key });
80
- return acc;
81
- }, {});
82
- return groupedResults;
83
- } else {
84
- const groupedResults = results.reduce((acc, result, index) => {
85
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
86
- const key = `${testId}-${result.key}-${result.retryAttemptCount}`;
87
- const { filePath, suite } = result;
88
- acc[filePath] = acc[filePath] || {};
89
- acc[filePath][suite] = acc[filePath][suite] || [];
90
- acc[filePath][suite].push({ ...result, index, testId, key });
91
- return acc;
92
- }, {});
93
- return groupedResults;
94
- }
95
- }
96
-
97
- // src/helpers/HTMLGenerator.ts
98
- var HTMLGenerator = class {
99
- constructor(ortoniConfig, dbManager) {
100
- this.ortoniConfig = ortoniConfig;
101
- this.dbManager = dbManager;
102
- }
103
- async generateFinalReport(filteredResults, totalDuration, results, projectSet) {
104
- const data = await this.prepareReportData(
105
- filteredResults,
106
- totalDuration,
107
- results,
108
- projectSet
109
- );
110
- return data;
111
- }
112
- async getReportData() {
113
- return {
114
- summary: await this.dbManager.getSummaryData(),
115
- trends: await this.dbManager.getTrends(),
116
- flakyTests: await this.dbManager.getFlakyTests(),
117
- slowTests: await this.dbManager.getSlowTests()
118
- };
119
- }
120
- async prepareReportData(filteredResults, totalDuration, results, projectSet) {
121
- const totalTests = filteredResults.length;
122
- const passedTests = results.filter((r) => r.status === "passed").length;
123
- const flakyTests = results.filter((r) => r.status === "flaky").length;
124
- const failed = filteredResults.filter(
125
- (r) => r.status === "failed" || r.status === "timedOut"
126
- ).length;
127
- const successRate = ((passedTests + flakyTests) / totalTests * 100).toFixed(2);
128
- const allTags = /* @__PURE__ */ new Set();
129
- results.forEach(
130
- (result) => result.testTags.forEach((tag) => allTags.add(tag))
131
- );
132
- const projectResults = this.calculateProjectResults(
133
- filteredResults,
134
- results,
135
- projectSet
136
- );
137
- const lastRunDate = (/* @__PURE__ */ new Date()).toLocaleString();
138
- const testHistories = await Promise.all(
139
- results.map(async (result) => {
140
- const testId = `${result.filePath}:${result.projectName}:${result.title}`;
141
- const history = await this.dbManager.getTestHistory(testId);
142
- return {
143
- testId,
144
- history
145
- };
146
- })
147
- );
148
- return {
149
- summary: {
150
- overAllResult: {
151
- pass: passedTests,
152
- fail: failed,
153
- skip: results.filter((r) => r.status === "skipped").length,
154
- retry: results.filter((r) => r.retryAttemptCount).length,
155
- flaky: flakyTests,
156
- total: filteredResults.length
157
- },
158
- successRate,
159
- lastRunDate,
160
- totalDuration,
161
- stats: this.extractProjectStats(projectResults)
162
- },
163
- testResult: {
164
- tests: groupResults(this.ortoniConfig, results),
165
- testHistories,
166
- allTags: Array.from(allTags),
167
- set: projectSet
168
- },
169
- userConfig: {
170
- projectName: this.ortoniConfig.projectName,
171
- authorName: this.ortoniConfig.authorName,
172
- type: this.ortoniConfig.testType,
173
- title: this.ortoniConfig.title
174
- },
175
- userMeta: {
176
- meta: this.ortoniConfig.meta
177
- },
178
- preferences: {
179
- theme: this.ortoniConfig.preferredTheme,
180
- logo: this.ortoniConfig.logo || void 0,
181
- showProject: this.ortoniConfig.showProject || false
182
- },
183
- analytics: {
184
- reportData: await this.getReportData()
185
- // chartTrendData: await this.chartTrendData(),
186
- }
187
- };
188
- }
189
- calculateProjectResults(filteredResults, results, projectSet) {
190
- return Array.from(projectSet).map((projectName) => {
191
- const projectTests = filteredResults.filter(
192
- (r) => r.projectName === projectName
193
- );
194
- const allProjectTests = results.filter(
195
- (r) => r.projectName === projectName
196
- );
197
- return {
198
- projectName,
199
- passedTests: projectTests.filter((r) => r.status === "passed").length,
200
- failedTests: projectTests.filter(
201
- (r) => r.status === "failed" || r.status === "timedOut"
202
- ).length,
203
- skippedTests: allProjectTests.filter((r) => r.status === "skipped").length,
204
- retryTests: allProjectTests.filter((r) => r.retryAttemptCount).length,
205
- flakyTests: allProjectTests.filter((r) => r.status === "flaky").length,
206
- totalTests: projectTests.length
207
- };
208
- });
209
- }
210
- extractProjectStats(projectResults) {
211
- return {
212
- projectNames: projectResults.map((result) => result.projectName),
213
- totalTests: projectResults.map((result) => result.totalTests),
214
- passedTests: projectResults.map((result) => result.passedTests),
215
- failedTests: projectResults.map((result) => result.failedTests),
216
- skippedTests: projectResults.map((result) => result.skippedTests),
217
- retryTests: projectResults.map((result) => result.retryTests),
218
- flakyTests: projectResults.map((result) => result.flakyTests)
219
- };
220
- }
221
- };
11
+ } from "./chunk-JEIWNUQY.mjs";
222
12
 
223
13
  // src/helpers/resultProcessor .ts
224
14
  import AnsiToHtml from "ansi-to-html";
225
- import path4 from "path";
15
+ import path2 from "path";
226
16
 
227
17
  // src/utils/attachFiles.ts
228
- import path2 from "path";
229
- import fs3 from "fs";
18
+ import path from "path";
19
+ import fs2 from "fs";
230
20
 
231
21
  // src/helpers/markdownConverter.ts
232
- import fs2 from "fs";
22
+ import fs from "fs";
233
23
 
234
24
  // node_modules/marked/lib/marked.esm.js
235
25
  function M() {
@@ -1355,27 +1145,27 @@ var Qt = b.lex;
1355
1145
 
1356
1146
  // src/helpers/markdownConverter.ts
1357
1147
  function convertMarkdownToHtml(markdownPath, htmlOutputPath) {
1358
- const hasMarkdown = fs2.existsSync(markdownPath);
1359
- const markdownContent = hasMarkdown ? fs2.readFileSync(markdownPath, "utf-8") : "";
1148
+ const hasMarkdown = fs.existsSync(markdownPath);
1149
+ const markdownContent = hasMarkdown ? fs.readFileSync(markdownPath, "utf-8") : "";
1360
1150
  const markdownHtml = hasMarkdown ? k(markdownContent) : "";
1361
1151
  const drawerHtml = `${markdownHtml || ""}`;
1362
- fs2.writeFileSync(htmlOutputPath, drawerHtml.trim(), "utf-8");
1152
+ fs.writeFileSync(htmlOutputPath, drawerHtml.trim(), "utf-8");
1363
1153
  if (hasMarkdown) {
1364
- fs2.unlinkSync(markdownPath);
1154
+ fs.unlinkSync(markdownPath);
1365
1155
  }
1366
1156
  }
1367
1157
 
1368
1158
  // src/utils/attachFiles.ts
1369
1159
  function attachFiles(subFolder, result, testResult, config, steps, errors) {
1370
1160
  const folderPath = config.folderPath || "ortoni-report";
1371
- const attachmentsFolder = path2.join(
1161
+ const attachmentsFolder = path.join(
1372
1162
  folderPath,
1373
1163
  "ortoni-data",
1374
1164
  "attachments",
1375
1165
  subFolder
1376
1166
  );
1377
- if (!fs3.existsSync(attachmentsFolder)) {
1378
- fs3.mkdirSync(attachmentsFolder, { recursive: true });
1167
+ if (!fs2.existsSync(attachmentsFolder)) {
1168
+ fs2.mkdirSync(attachmentsFolder, { recursive: true });
1379
1169
  }
1380
1170
  if (!result.attachments) return;
1381
1171
  const { base64Image } = config;
@@ -1384,14 +1174,14 @@ function attachFiles(subFolder, result, testResult, config, steps, errors) {
1384
1174
  result.attachments.forEach((attachment) => {
1385
1175
  const { contentType, name, path: attachmentPath, body } = attachment;
1386
1176
  if (!attachmentPath && !body) return;
1387
- const fileName = attachmentPath ? path2.basename(attachmentPath) : `${name}.${getFileExtension(contentType)}`;
1388
- const relativePath = path2.join(
1177
+ const fileName = attachmentPath ? path.basename(attachmentPath) : `${name}.${getFileExtension(contentType)}`;
1178
+ const relativePath = path.join(
1389
1179
  "ortoni-data",
1390
1180
  "attachments",
1391
1181
  subFolder,
1392
1182
  fileName
1393
1183
  );
1394
- const fullPath = path2.join(attachmentsFolder, fileName);
1184
+ const fullPath = path.join(attachmentsFolder, fileName);
1395
1185
  if (contentType === "image/png") {
1396
1186
  handleImage(
1397
1187
  attachmentPath,
@@ -1434,13 +1224,13 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
1434
1224
  let screenshotPath = "";
1435
1225
  if (attachmentPath) {
1436
1226
  try {
1437
- const screenshotContent = fs3.readFileSync(
1227
+ const screenshotContent = fs2.readFileSync(
1438
1228
  attachmentPath,
1439
1229
  base64Image ? "base64" : void 0
1440
1230
  );
1441
1231
  screenshotPath = base64Image ? `data:image/png;base64,${screenshotContent}` : relativePath;
1442
1232
  if (!base64Image) {
1443
- fs3.copyFileSync(attachmentPath, fullPath);
1233
+ fs2.copyFileSync(attachmentPath, fullPath);
1444
1234
  }
1445
1235
  } catch (error) {
1446
1236
  console.error(
@@ -1457,7 +1247,7 @@ function handleImage(attachmentPath, body, base64Image, fullPath, relativePath,
1457
1247
  }
1458
1248
  function handleAttachment(attachmentPath, fullPath, relativePath, resultKey, testResult, steps, errors) {
1459
1249
  if (attachmentPath) {
1460
- fs3.copyFileSync(attachmentPath, fullPath);
1250
+ fs2.copyFileSync(attachmentPath, fullPath);
1461
1251
  if (resultKey === "videoPath") {
1462
1252
  testResult[resultKey]?.push(relativePath);
1463
1253
  } else if (resultKey === "tracePath") {
@@ -1482,61 +1272,6 @@ function getFileExtension(contentType) {
1482
1272
  return extensions[contentType] || "unknown";
1483
1273
  }
1484
1274
 
1485
- // src/utils/utils.ts
1486
- import path3 from "path";
1487
- function normalizeFilePath(filePath) {
1488
- const normalizedPath = path3.normalize(filePath);
1489
- return path3.basename(normalizedPath);
1490
- }
1491
- function ensureHtmlExtension(filename) {
1492
- const ext = path3.extname(filename);
1493
- if (ext && ext.toLowerCase() === ".html") {
1494
- return filename;
1495
- }
1496
- return `${filename}.html`;
1497
- }
1498
- function escapeHtml(unsafe) {
1499
- if (typeof unsafe !== "string") {
1500
- return String(unsafe);
1501
- }
1502
- return unsafe.replace(/[&<"']/g, function(match) {
1503
- const escapeMap = {
1504
- "&": "&amp;",
1505
- "<": "&lt;",
1506
- ">": "&gt;",
1507
- '"': "&quot;",
1508
- "'": "&#039;"
1509
- };
1510
- return escapeMap[match] || match;
1511
- });
1512
- }
1513
- function formatDateLocal(dateInput) {
1514
- const date = typeof dateInput === "string" ? new Date(dateInput) : dateInput;
1515
- const options = {
1516
- year: "numeric",
1517
- month: "short",
1518
- day: "2-digit",
1519
- hour: "2-digit",
1520
- minute: "2-digit",
1521
- hour12: true,
1522
- timeZoneName: "short"
1523
- // or "Asia/Kolkata"
1524
- };
1525
- return new Intl.DateTimeFormat(void 0, options).format(date);
1526
- }
1527
- function extractSuites(titlePath) {
1528
- const tagPattern = /@[\w]+/g;
1529
- const suiteParts = titlePath.slice(3, titlePath.length - 1).map((p) => p.replace(tagPattern, "").trim());
1530
- return {
1531
- hierarchy: suiteParts.join(" > "),
1532
- // full hierarchy
1533
- topLevelSuite: suiteParts[0] ?? "",
1534
- // first suite
1535
- parentSuite: suiteParts[suiteParts.length - 1] ?? ""
1536
- // last suite
1537
- };
1538
- }
1539
-
1540
1275
  // src/helpers/resultProcessor .ts
1541
1276
  var TestResultProcessor = class {
1542
1277
  constructor(projectRoot) {
@@ -1593,7 +1328,7 @@ var TestResultProcessor = class {
1593
1328
  }
1594
1329
  processSteps(steps) {
1595
1330
  return steps.map((step) => {
1596
- const stepLocation = step.location ? `${path4.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
1331
+ const stepLocation = step.location ? `${path2.relative(this.projectRoot, step.location.file)}:${step.location.line}:${step.location.column}` : "";
1597
1332
  return {
1598
1333
  snippet: this.ansiToHtml.toHtml(escapeHtml(step.error?.snippet || "")),
1599
1334
  title: step.title,
@@ -1622,282 +1357,8 @@ var ServerManager = class {
1622
1357
  }
1623
1358
  };
1624
1359
 
1625
- // src/helpers/databaseManager.ts
1626
- import { open } from "sqlite";
1627
- import sqlite3 from "sqlite3";
1628
- var DatabaseManager = class {
1629
- constructor() {
1630
- this.db = null;
1631
- }
1632
- async initialize(dbPath) {
1633
- try {
1634
- this.db = await open({
1635
- filename: dbPath,
1636
- driver: sqlite3.Database
1637
- });
1638
- await this.createTables();
1639
- await this.createIndexes();
1640
- } catch (error) {
1641
- console.error("OrtoniReport: Error initializing database:", error);
1642
- }
1643
- }
1644
- async createTables() {
1645
- if (!this.db) {
1646
- console.error("OrtoniReport: Database not initialized");
1647
- return;
1648
- }
1649
- try {
1650
- await this.db.exec(`
1651
- CREATE TABLE IF NOT EXISTS test_runs (
1652
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1653
- run_date TEXT
1654
- );
1655
-
1656
- CREATE TABLE IF NOT EXISTS test_results (
1657
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1658
- run_id INTEGER,
1659
- test_id TEXT,
1660
- status TEXT,
1661
- duration INTEGER, -- store duration as raw ms
1662
- error_message TEXT,
1663
- FOREIGN KEY (run_id) REFERENCES test_runs (id)
1664
- );
1665
- `);
1666
- } catch (error) {
1667
- console.error("OrtoniReport: Error creating tables:", error);
1668
- }
1669
- }
1670
- async createIndexes() {
1671
- if (!this.db) {
1672
- console.error("OrtoniReport: Database not initialized");
1673
- return;
1674
- }
1675
- try {
1676
- await this.db.exec(`
1677
- CREATE INDEX IF NOT EXISTS idx_test_id ON test_results (test_id);
1678
- CREATE INDEX IF NOT EXISTS idx_run_id ON test_results (run_id);
1679
- `);
1680
- } catch (error) {
1681
- console.error("OrtoniReport: Error creating indexes:", error);
1682
- }
1683
- }
1684
- async saveTestRun() {
1685
- if (!this.db) {
1686
- console.error("OrtoniReport: Database not initialized");
1687
- return null;
1688
- }
1689
- try {
1690
- const runDate = (/* @__PURE__ */ new Date()).toISOString();
1691
- const { lastID } = await this.db.run(
1692
- `
1693
- INSERT INTO test_runs (run_date)
1694
- VALUES (?)
1695
- `,
1696
- [runDate]
1697
- );
1698
- return lastID;
1699
- } catch (error) {
1700
- console.error("OrtoniReport: Error saving test run:", error);
1701
- return null;
1702
- }
1703
- }
1704
- async saveTestResults(runId, results) {
1705
- if (!this.db) {
1706
- console.error("OrtoniReport: Database not initialized");
1707
- return;
1708
- }
1709
- try {
1710
- await this.db.exec("BEGIN TRANSACTION;");
1711
- const stmt = await this.db.prepare(`
1712
- INSERT INTO test_results (run_id, test_id, status, duration, error_message)
1713
- VALUES (?, ?, ?, ?, ?)
1714
- `);
1715
- for (const result of results) {
1716
- await stmt.run([
1717
- runId,
1718
- `${result.filePath}:${result.projectName}:${result.title}`,
1719
- result.status,
1720
- result.duration,
1721
- // store raw ms
1722
- result.errors.join("\n")
1723
- ]);
1724
- }
1725
- await stmt.finalize();
1726
- await this.db.exec("COMMIT;");
1727
- } catch (error) {
1728
- await this.db.exec("ROLLBACK;");
1729
- console.error("OrtoniReport: Error saving test results:", error);
1730
- }
1731
- }
1732
- async getTestHistory(testId, limit = 10) {
1733
- if (!this.db) {
1734
- console.error("OrtoniReport: Database not initialized");
1735
- return [];
1736
- }
1737
- try {
1738
- const results = await this.db.all(
1739
- `
1740
- SELECT tr.status, tr.duration, tr.error_message, trun.run_date
1741
- FROM test_results tr
1742
- JOIN test_runs trun ON tr.run_id = trun.id
1743
- WHERE tr.test_id = ?
1744
- ORDER BY trun.run_date DESC
1745
- LIMIT ?
1746
- `,
1747
- [testId, limit]
1748
- );
1749
- return results.map((result) => ({
1750
- ...result,
1751
- run_date: formatDateLocal(result.run_date)
1752
- }));
1753
- } catch (error) {
1754
- console.error("OrtoniReport: Error getting test history:", error);
1755
- return [];
1756
- }
1757
- }
1758
- async close() {
1759
- if (this.db) {
1760
- try {
1761
- await this.db.close();
1762
- } catch (error) {
1763
- console.error("OrtoniReport: Error closing database:", error);
1764
- } finally {
1765
- this.db = null;
1766
- }
1767
- }
1768
- }
1769
- async getSummaryData() {
1770
- if (!this.db) {
1771
- console.error("OrtoniReport: Database not initialized");
1772
- return {
1773
- totalRuns: 0,
1774
- totalTests: 0,
1775
- passed: 0,
1776
- failed: 0,
1777
- passRate: 0,
1778
- avgDuration: 0
1779
- };
1780
- }
1781
- try {
1782
- const summary = await this.db.get(`
1783
- SELECT
1784
- (SELECT COUNT(*) FROM test_runs) as totalRuns,
1785
- (SELECT COUNT(*) FROM test_results) as totalTests,
1786
- (SELECT COUNT(*) FROM test_results WHERE status = 'passed') as passed,
1787
- (SELECT COUNT(*) FROM test_results WHERE status = 'failed') as failed,
1788
- (SELECT AVG(duration) FROM test_results) as avgDuration
1789
- `);
1790
- const passRate = summary.totalTests ? (summary.passed / summary.totalTests * 100).toFixed(2) : 0;
1791
- return {
1792
- totalRuns: summary.totalRuns,
1793
- totalTests: summary.totalTests,
1794
- passed: summary.passed,
1795
- failed: summary.failed,
1796
- passRate: parseFloat(passRate.toString()),
1797
- avgDuration: Math.round(summary.avgDuration || 0)
1798
- // raw ms avg
1799
- };
1800
- } catch (error) {
1801
- console.error("OrtoniReport: Error getting summary data:", error);
1802
- return {
1803
- totalRuns: 0,
1804
- totalTests: 0,
1805
- passed: 0,
1806
- failed: 0,
1807
- passRate: 0,
1808
- avgDuration: 0
1809
- };
1810
- }
1811
- }
1812
- async getTrends(limit = 100) {
1813
- if (!this.db) {
1814
- console.error("OrtoniReport: Database not initialized");
1815
- return [];
1816
- }
1817
- try {
1818
- const rows = await this.db.all(
1819
- `
1820
- SELECT trun.run_date,
1821
- SUM(CASE WHEN tr.status = 'passed' THEN 1 ELSE 0 END) AS passed,
1822
- SUM(CASE WHEN tr.status = 'failed' THEN 1 ELSE 0 END) AS failed,
1823
- AVG(tr.duration) AS avg_duration
1824
- FROM test_results tr
1825
- JOIN test_runs trun ON tr.run_id = trun.id
1826
- GROUP BY trun.run_date
1827
- ORDER BY trun.run_date DESC
1828
- LIMIT ?
1829
- `,
1830
- [limit]
1831
- );
1832
- return rows.reverse().map((row) => ({
1833
- ...row,
1834
- run_date: formatDateLocal(row.run_date),
1835
- avg_duration: Math.round(row.avg_duration || 0)
1836
- // raw ms avg
1837
- }));
1838
- } catch (error) {
1839
- console.error("OrtoniReport: Error getting trends data:", error);
1840
- return [];
1841
- }
1842
- }
1843
- async getFlakyTests(limit = 10) {
1844
- if (!this.db) {
1845
- console.error("OrtoniReport: Database not initialized");
1846
- return [];
1847
- }
1848
- try {
1849
- return await this.db.all(
1850
- `
1851
- SELECT
1852
- test_id,
1853
- COUNT(*) AS total,
1854
- SUM(CASE WHEN status = 'flaky' THEN 1 ELSE 0 END) AS flaky,
1855
- AVG(duration) AS avg_duration
1856
- FROM test_results
1857
- GROUP BY test_id
1858
- HAVING flaky > 0
1859
- ORDER BY flaky DESC
1860
- LIMIT ?
1861
- `,
1862
- [limit]
1863
- );
1864
- } catch (error) {
1865
- console.error("OrtoniReport: Error getting flaky tests:", error);
1866
- return [];
1867
- }
1868
- }
1869
- async getSlowTests(limit = 10) {
1870
- if (!this.db) {
1871
- console.error("OrtoniReport: Database not initialized");
1872
- return [];
1873
- }
1874
- try {
1875
- const rows = await this.db.all(
1876
- `
1877
- SELECT
1878
- test_id,
1879
- AVG(duration) AS avg_duration
1880
- FROM test_results
1881
- GROUP BY test_id
1882
- ORDER BY avg_duration DESC
1883
- LIMIT ?
1884
- `,
1885
- [limit]
1886
- );
1887
- return rows.map((row) => ({
1888
- test_id: row.test_id,
1889
- avg_duration: Math.round(row.avg_duration || 0)
1890
- // raw ms avg
1891
- }));
1892
- } catch (error) {
1893
- console.error("OrtoniReport: Error getting slow tests:", error);
1894
- return [];
1895
- }
1896
- }
1897
- };
1898
-
1899
1360
  // src/ortoni-report.ts
1900
- import path5 from "path";
1361
+ import path3 from "path";
1901
1362
  var OrtoniReport = class {
1902
1363
  constructor(ortoniConfig = {}) {
1903
1364
  this.ortoniConfig = ortoniConfig;
@@ -1928,8 +1389,9 @@ var OrtoniReport = class {
1928
1389
  this.testResultProcessor = new TestResultProcessor(config.rootDir);
1929
1390
  this.fileManager.ensureReportDirectory();
1930
1391
  await this.dbManager.initialize(
1931
- path5.join(this.folderPath, "ortoni-data-history.sqlite")
1392
+ path3.join(this.folderPath, "ortoni-data-history.sqlite")
1932
1393
  );
1394
+ this.config = config?.shard;
1933
1395
  }
1934
1396
  onStdOut(chunk, _test, _result) {
1935
1397
  if (this.reportsCount == 1 && this.showConsoleLogs) {
@@ -1965,6 +1427,20 @@ var OrtoniReport = class {
1965
1427
  (r) => r.status !== "skipped"
1966
1428
  );
1967
1429
  const totalDuration = result.duration;
1430
+ if (this.config && this.config.total > 1) {
1431
+ const shard = this.config;
1432
+ const shardFile = `ortoni-shard-${shard.current}-of-${shard.total}.json`;
1433
+ const shardData = {
1434
+ status: result.status,
1435
+ duration: totalDuration,
1436
+ results: this.results,
1437
+ projectSet: Array.from(this.projectSet)
1438
+ };
1439
+ this.fileManager.writeRawFile(shardFile, shardData);
1440
+ console.log(`\u{1F4E6} OrtoniReport wrote shard file: ${shardFile}`);
1441
+ this.shouldGenerateReport = false;
1442
+ return;
1443
+ }
1968
1444
  const runId = await this.dbManager.saveTestRun();
1969
1445
  if (runId !== null) {
1970
1446
  await this.dbManager.saveTestResults(runId, this.results);