@testdino/playwright 1.0.8 → 1.0.10

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/index.js CHANGED
@@ -1,35 +1,48 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var crypto = require('crypto');
6
- var fs = require('fs');
7
- var WebSocket = require('ws');
8
- var axios = require('axios');
9
- var path = require('path');
10
- var promises = require('fs/promises');
11
- var execa = require('execa');
12
- var os = require('os');
13
- var process$1 = require('process');
14
- var istanbulCoverage = require('istanbul-lib-coverage');
15
- var istanbulLibReport = require('istanbul-lib-report');
16
- var istanbulReports = require('istanbul-reports');
17
- var test$1 = require('@playwright/test');
18
- var chalk = require('chalk');
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ coverageFixtures: () => coverageFixtures,
34
+ default: () => TestdinoReporter,
35
+ expect: () => import_test.expect,
36
+ test: () => test
37
+ });
38
+ module.exports = __toCommonJS(src_exports);
21
39
 
22
- var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
23
- var axios__default = /*#__PURE__*/_interopDefault(axios);
24
- var istanbulCoverage__default = /*#__PURE__*/_interopDefault(istanbulCoverage);
25
- var chalk__default = /*#__PURE__*/_interopDefault(chalk);
40
+ // src/reporter/index.ts
41
+ var import_crypto2 = require("crypto");
42
+ var import_fs2 = require("fs");
26
43
 
27
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
28
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
29
- }) : x)(function(x) {
30
- if (typeof require !== "undefined") return require.apply(this, arguments);
31
- throw Error('Dynamic require of "' + x + '" is not supported');
32
- });
44
+ // src/streaming/websocket.ts
45
+ var import_ws = __toESM(require("ws"));
33
46
 
34
47
  // src/reporter/errors.ts
35
48
  var TestDinoServerError = class extends Error {
@@ -101,7 +114,7 @@ var WebSocketClient = class {
101
114
  * Passes sessionId from HTTP auth so the server reuses the existing session.
102
115
  */
103
116
  async connect() {
104
- if (this.isConnecting || this.ws?.readyState === WebSocket__default.default.OPEN) {
117
+ if (this.isConnecting || this.ws?.readyState === import_ws.default.OPEN) {
105
118
  return;
106
119
  }
107
120
  this.isConnecting = true;
@@ -111,7 +124,7 @@ var WebSocketClient = class {
111
124
  if (this.options.sessionId) {
112
125
  wsUrl += `&sessionId=${this.options.sessionId}`;
113
126
  }
114
- this.ws = new WebSocket__default.default(wsUrl);
127
+ this.ws = new import_ws.default(wsUrl);
115
128
  let serverReady = false;
116
129
  const handshakeTimeout = setTimeout(() => {
117
130
  if (!serverReady) {
@@ -175,7 +188,7 @@ var WebSocketClient = class {
175
188
  * Send event through WebSocket
176
189
  */
177
190
  async send(event) {
178
- if (!this.ws || this.ws.readyState !== WebSocket__default.default.OPEN) {
191
+ if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
179
192
  throw new Error("WebSocket is not connected");
180
193
  }
181
194
  return new Promise((resolve, reject) => {
@@ -193,7 +206,7 @@ var WebSocketClient = class {
193
206
  * Note: Uses Promise.all intentionally - if one send fails, connection is broken
194
207
  */
195
208
  async sendBatch(events) {
196
- if (!this.ws || this.ws.readyState !== WebSocket__default.default.OPEN) {
209
+ if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
197
210
  throw new Error("WebSocket is not connected");
198
211
  }
199
212
  await Promise.all(events.map((event) => this.send(event)));
@@ -203,7 +216,7 @@ var WebSocketClient = class {
203
216
  * Use this for critical events like run:end where delivery must be confirmed
204
217
  */
205
218
  async sendAndWaitForAck(event, timeout = this.ackTimeout) {
206
- if (!this.ws || this.ws.readyState !== WebSocket__default.default.OPEN) {
219
+ if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
207
220
  throw new Error("WebSocket is not connected");
208
221
  }
209
222
  const sequence = event.sequence;
@@ -232,7 +245,7 @@ var WebSocketClient = class {
232
245
  * Check if WebSocket is connected
233
246
  */
234
247
  isConnected() {
235
- return this.ws?.readyState === WebSocket__default.default.OPEN;
248
+ return this.ws?.readyState === import_ws.default.OPEN;
236
249
  }
237
250
  /**
238
251
  * Close WebSocket connection
@@ -367,7 +380,7 @@ var WebSocketClient = class {
367
380
  startPing() {
368
381
  this.stopPing();
369
382
  this.pingInterval = setInterval(() => {
370
- if (this.ws?.readyState === WebSocket__default.default.OPEN) {
383
+ if (this.ws?.readyState === import_ws.default.OPEN) {
371
384
  this.ws.ping();
372
385
  }
373
386
  }, 3e4);
@@ -382,9 +395,15 @@ var WebSocketClient = class {
382
395
  }
383
396
  }
384
397
  };
398
+
399
+ // src/streaming/http.ts
400
+ var import_axios = __toESM(require("axios"));
401
+
402
+ // src/utils/index.ts
403
+ var import_path = require("path");
385
404
  function normalizePath(filePath, rootDir) {
386
405
  if (rootDir && filePath.startsWith(rootDir)) {
387
- return path.relative(rootDir, filePath);
406
+ return (0, import_path.relative)(rootDir, filePath);
388
407
  }
389
408
  return filePath;
390
409
  }
@@ -405,7 +424,7 @@ var HttpClient = class {
405
424
  retryDelay: 1e3,
406
425
  ...options
407
426
  };
408
- this.client = axios__default.default.create({
427
+ this.client = import_axios.default.create({
409
428
  baseURL: this.options.serverUrl,
410
429
  headers: {
411
430
  "Content-Type": "application/json",
@@ -422,7 +441,7 @@ var HttpClient = class {
422
441
  const response = await this.client.post("/auth");
423
442
  return response.data;
424
443
  } catch (error) {
425
- if (axios__default.default.isAxiosError(error) && error.response?.status === 402) {
444
+ if (import_axios.default.isAxiosError(error) && error.response?.status === 402) {
426
445
  const quotaError = error.response.data;
427
446
  throw new QuotaExhaustedError(quotaError.message, quotaError.details);
428
447
  }
@@ -458,7 +477,7 @@ var HttpClient = class {
458
477
  * Extract error message from various error types
459
478
  */
460
479
  getErrorMessage(error) {
461
- if (axios__default.default.isAxiosError(error)) {
480
+ if (import_axios.default.isAxiosError(error)) {
462
481
  return error.response?.data?.message || error.message;
463
482
  }
464
483
  if (error instanceof Error) {
@@ -550,6 +569,9 @@ var EventBuffer = class {
550
569
  }
551
570
  };
552
571
 
572
+ // src/metadata/git.ts
573
+ var import_promises = require("fs/promises");
574
+
553
575
  // src/metadata/base.ts
554
576
  var BaseMetadataCollector = class {
555
577
  name;
@@ -635,6 +657,16 @@ var BaseMetadataCollector = class {
635
657
  };
636
658
 
637
659
  // src/metadata/git.ts
660
+ var execaPromise = null;
661
+ async function getExeca() {
662
+ if (!execaPromise) {
663
+ execaPromise = import("execa").then((m) => m.execa).catch((error) => {
664
+ console.error("Failed to import execa:", error.message);
665
+ throw new Error("Failed to load execa. Ensure execa is installed: npm install execa");
666
+ });
667
+ }
668
+ return execaPromise;
669
+ }
638
670
  var GitMetadataCollector = class extends BaseMetadataCollector {
639
671
  options;
640
672
  constructor(options = {}) {
@@ -727,7 +759,8 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
727
759
  const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
728
760
  if (!isCI) return;
729
761
  try {
730
- await execa.execa("git", ["config", "--global", "--add", "safe.directory", this.options.cwd], {
762
+ const execa = await getExeca();
763
+ await execa("git", ["config", "--global", "--add", "safe.directory", this.options.cwd], {
731
764
  timeout: this.options.timeout,
732
765
  reject: true
733
766
  });
@@ -742,7 +775,7 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
742
775
  if (!eventPath) return void 0;
743
776
  try {
744
777
  const content = await this.withTimeout(
745
- promises.readFile(eventPath, "utf-8"),
778
+ (0, import_promises.readFile)(eventPath, "utf-8"),
746
779
  this.options.timeout,
747
780
  "GitHub event file read"
748
781
  );
@@ -1026,8 +1059,9 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
1026
1059
  * Execute git command with timeout
1027
1060
  */
1028
1061
  async execGit(args) {
1062
+ const execa = await getExeca();
1029
1063
  const { stdout } = await this.withTimeout(
1030
- execa.execa("git", args, {
1064
+ execa("git", args, {
1031
1065
  cwd: this.options.cwd,
1032
1066
  timeout: this.options.timeout,
1033
1067
  reject: true
@@ -1038,6 +1072,9 @@ var GitMetadataCollector = class extends BaseMetadataCollector {
1038
1072
  return stdout.trim();
1039
1073
  }
1040
1074
  };
1075
+
1076
+ // src/metadata/ci.ts
1077
+ var import_os = require("os");
1041
1078
  var CIMetadataCollector = class extends BaseMetadataCollector {
1042
1079
  constructor(_options = {}) {
1043
1080
  super("ci");
@@ -1115,9 +1152,9 @@ var CIMetadataCollector = class extends BaseMetadataCollector {
1115
1152
  collectEnvironment() {
1116
1153
  try {
1117
1154
  return {
1118
- name: os.type(),
1155
+ name: (0, import_os.type)(),
1119
1156
  type: process.platform,
1120
- os: `${os.type()} ${os.release()}`,
1157
+ os: `${(0, import_os.type)()} ${(0, import_os.release)()}`,
1121
1158
  node: process.version
1122
1159
  };
1123
1160
  } catch {
@@ -1125,6 +1162,10 @@ var CIMetadataCollector = class extends BaseMetadataCollector {
1125
1162
  }
1126
1163
  }
1127
1164
  };
1165
+
1166
+ // src/metadata/system.ts
1167
+ var import_os2 = require("os");
1168
+ var import_process = require("process");
1128
1169
  var SystemMetadataCollector = class extends BaseMetadataCollector {
1129
1170
  constructor(_options = {}) {
1130
1171
  super("system");
@@ -1159,11 +1200,11 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1159
1200
  let platformName = "unknown";
1160
1201
  let releaseVersion = "unknown";
1161
1202
  try {
1162
- platformName = os.platform();
1203
+ platformName = (0, import_os2.platform)();
1163
1204
  } catch {
1164
1205
  }
1165
1206
  try {
1166
- releaseVersion = os.release();
1207
+ releaseVersion = (0, import_os2.release)();
1167
1208
  } catch {
1168
1209
  }
1169
1210
  return `${platformName} ${releaseVersion}`;
@@ -1174,7 +1215,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1174
1215
  */
1175
1216
  getCpuInfo() {
1176
1217
  try {
1177
- const cpuList = os.cpus();
1218
+ const cpuList = (0, import_os2.cpus)();
1178
1219
  if (cpuList.length === 0) {
1179
1220
  return "unknown";
1180
1221
  }
@@ -1191,7 +1232,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1191
1232
  */
1192
1233
  getMemoryInfo() {
1193
1234
  try {
1194
- const totalBytes = os.totalmem();
1235
+ const totalBytes = (0, import_os2.totalmem)();
1195
1236
  const totalGB = totalBytes / (1024 * 1024 * 1024);
1196
1237
  return `${totalGB.toFixed(1)} GB`;
1197
1238
  } catch {
@@ -1204,7 +1245,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1204
1245
  */
1205
1246
  getNodeVersion() {
1206
1247
  try {
1207
- return process$1.version;
1248
+ return import_process.version;
1208
1249
  } catch {
1209
1250
  return "unknown";
1210
1251
  }
@@ -1215,7 +1256,7 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1215
1256
  */
1216
1257
  getPlatform() {
1217
1258
  try {
1218
- return os.platform();
1259
+ return (0, import_os2.platform)();
1219
1260
  } catch {
1220
1261
  return "unknown";
1221
1262
  }
@@ -1226,12 +1267,15 @@ var SystemMetadataCollector = class extends BaseMetadataCollector {
1226
1267
  */
1227
1268
  getHostname() {
1228
1269
  try {
1229
- return os.hostname();
1270
+ return (0, import_os2.hostname)();
1230
1271
  } catch {
1231
1272
  return "unknown";
1232
1273
  }
1233
1274
  }
1234
1275
  };
1276
+
1277
+ // src/metadata/playwright.ts
1278
+ var import_promises2 = require("fs/promises");
1235
1279
  var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
1236
1280
  options;
1237
1281
  constructor(options = {}) {
@@ -1281,7 +1325,7 @@ var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
1281
1325
  return void 0;
1282
1326
  }
1283
1327
  const packageJsonContent = await this.withTimeout(
1284
- promises.readFile(packageJsonPath, "utf-8"),
1328
+ (0, import_promises2.readFile)(packageJsonPath, "utf-8"),
1285
1329
  this.options.timeout,
1286
1330
  "Playwright package.json read"
1287
1331
  );
@@ -1300,7 +1344,7 @@ var PlaywrightMetadataCollector = class extends BaseMetadataCollector {
1300
1344
  */
1301
1345
  resolvePlaywrightPackageJson() {
1302
1346
  try {
1303
- return __require.resolve("@playwright/test/package.json");
1347
+ return require.resolve("@playwright/test/package.json");
1304
1348
  } catch {
1305
1349
  return void 0;
1306
1350
  }
@@ -1704,6 +1748,9 @@ function createMetadataCollector(playwrightConfig, playwrightSuite) {
1704
1748
  );
1705
1749
  return aggregator;
1706
1750
  }
1751
+
1752
+ // src/uploads/sas-token-client.ts
1753
+ var import_axios2 = __toESM(require("axios"));
1707
1754
  var SASTokenClient = class {
1708
1755
  client;
1709
1756
  options;
@@ -1713,7 +1760,7 @@ var SASTokenClient = class {
1713
1760
  retryDelay: 1e3,
1714
1761
  ...options
1715
1762
  };
1716
- this.client = axios__default.default.create({
1763
+ this.client = import_axios2.default.create({
1717
1764
  baseURL: this.options.serverUrl,
1718
1765
  headers: {
1719
1766
  "Content-Type": "application/json",
@@ -1755,7 +1802,7 @@ var SASTokenClient = class {
1755
1802
  * Extract error message from various error types
1756
1803
  */
1757
1804
  getErrorMessage(error) {
1758
- if (axios__default.default.isAxiosError(error)) {
1805
+ if (import_axios2.default.isAxiosError(error)) {
1759
1806
  if (error.response?.status === 401) {
1760
1807
  return "Invalid API key for artifact uploads";
1761
1808
  }
@@ -1773,6 +1820,11 @@ var SASTokenClient = class {
1773
1820
  return String(error);
1774
1821
  }
1775
1822
  };
1823
+
1824
+ // src/uploads/artifact-uploader.ts
1825
+ var import_fs = require("fs");
1826
+ var import_path2 = require("path");
1827
+ var import_axios3 = __toESM(require("axios"));
1776
1828
  var ArtifactUploader = class {
1777
1829
  sasToken;
1778
1830
  options;
@@ -1793,9 +1845,9 @@ var ArtifactUploader = class {
1793
1845
  */
1794
1846
  async uploadFile(attachment, testId) {
1795
1847
  const startTime = Date.now();
1796
- const fileName = path.basename(attachment.path);
1848
+ const fileName = (0, import_path2.basename)(attachment.path);
1797
1849
  try {
1798
- const stats = fs.statSync(attachment.path);
1850
+ const stats = (0, import_fs.statSync)(attachment.path);
1799
1851
  const fileSize = stats.size;
1800
1852
  if (fileSize > this.sasToken.maxSize) {
1801
1853
  return {
@@ -1806,7 +1858,7 @@ var ArtifactUploader = class {
1806
1858
  duration: Date.now() - startTime
1807
1859
  };
1808
1860
  }
1809
- const extension = path.extname(fileName).slice(1).toLowerCase();
1861
+ const extension = (0, import_path2.extname)(fileName).slice(1).toLowerCase();
1810
1862
  const allowedTypes = this.sasToken.allowedFileTypes;
1811
1863
  if (allowedTypes.length > 0 && !allowedTypes.includes(extension)) {
1812
1864
  return {
@@ -1916,9 +1968,9 @@ var ArtifactUploader = class {
1916
1968
  * Perform actual file upload to Azure
1917
1969
  */
1918
1970
  async doUpload(filePath, uploadUrl, contentType) {
1919
- const fileStream = fs.createReadStream(filePath);
1920
- const stats = fs.statSync(filePath);
1921
- await axios__default.default.put(uploadUrl, fileStream, {
1971
+ const fileStream = (0, import_fs.createReadStream)(filePath);
1972
+ const stats = (0, import_fs.statSync)(filePath);
1973
+ await import_axios3.default.put(uploadUrl, fileStream, {
1922
1974
  headers: {
1923
1975
  "Content-Type": contentType,
1924
1976
  "Content-Length": stats.size,
@@ -1944,6 +1996,10 @@ var ArtifactUploader = class {
1944
1996
  return this.sasToken.uniqueId;
1945
1997
  }
1946
1998
  };
1999
+
2000
+ // src/code-coverage/merger.ts
2001
+ var import_istanbul_lib_coverage = __toESM(require("istanbul-lib-coverage"));
2002
+ var import_picomatch = __toESM(require("picomatch"));
1947
2003
  function toIstanbulMapData(map) {
1948
2004
  return map;
1949
2005
  }
@@ -1951,7 +2007,7 @@ function fromIstanbulMapData(data) {
1951
2007
  return data;
1952
2008
  }
1953
2009
  var CoverageMerger = class {
1954
- coverageMap = istanbulCoverage__default.default.createCoverageMap({});
2010
+ coverageMap = import_istanbul_lib_coverage.default.createCoverageMap({});
1955
2011
  hasData = false;
1956
2012
  includePatterns;
1957
2013
  excludePatterns;
@@ -1983,18 +2039,20 @@ var CoverageMerger = class {
1983
2039
  }
1984
2040
  }
1985
2041
  /**
1986
- * Filter files from a coverage map based on include/exclude patterns.
2042
+ * Filter files from a coverage map based on include/exclude glob patterns.
1987
2043
  */
1988
2044
  filterCoverageMap(coverageMap) {
1989
2045
  const hasInclude = this.includePatterns && this.includePatterns.length > 0;
1990
2046
  const hasExclude = this.excludePatterns && this.excludePatterns.length > 0;
1991
2047
  if (!hasInclude && !hasExclude) return coverageMap;
2048
+ const isExcluded = hasExclude ? (0, import_picomatch.default)(this.excludePatterns) : void 0;
2049
+ const isIncluded = hasInclude ? (0, import_picomatch.default)(this.includePatterns) : void 0;
1992
2050
  const filtered = {};
1993
2051
  for (const [filePath, fileCoverage] of Object.entries(coverageMap)) {
1994
- if (hasExclude && this.excludePatterns.some((pattern) => filePath.includes(pattern))) {
2052
+ if (isExcluded && isExcluded(filePath)) {
1995
2053
  continue;
1996
2054
  }
1997
- if (hasInclude && !this.includePatterns.some((pattern) => filePath.includes(pattern))) {
2055
+ if (isIncluded && !isIncluded(filePath)) {
1998
2056
  continue;
1999
2057
  }
2000
2058
  filtered[filePath] = fileCoverage;
@@ -2058,6 +2116,9 @@ function extractMetric(metric) {
2058
2116
  pct: metric.pct
2059
2117
  };
2060
2118
  }
2119
+
2120
+ // src/code-coverage/compact.ts
2121
+ var import_crypto = require("crypto");
2061
2122
  function extractCompactCounts(coverageMapJSON, gitRoot) {
2062
2123
  const files = {};
2063
2124
  let fileCount = 0;
@@ -2093,14 +2154,19 @@ function computeShapeHash(fileCoverage) {
2093
2154
  b: countBranchPaths(branchMap),
2094
2155
  bp: Object.values(branchMap).map((b) => (b.locations || []).length)
2095
2156
  };
2096
- return crypto.createHash("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 12);
2157
+ return (0, import_crypto.createHash)("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 12);
2097
2158
  }
2159
+
2160
+ // src/code-coverage/html-report.ts
2161
+ var import_istanbul_lib_report = require("istanbul-lib-report");
2162
+ var import_istanbul_reports = require("istanbul-reports");
2163
+ var import_promises3 = require("fs/promises");
2098
2164
  async function generateIstanbulHtmlReport(coverageMerger, options) {
2099
- await promises.rm(options.outputDir, { recursive: true, force: true }).catch(() => {
2165
+ await (0, import_promises3.rm)(options.outputDir, { recursive: true, force: true }).catch(() => {
2100
2166
  });
2101
- await promises.mkdir(options.outputDir, { recursive: true });
2167
+ await (0, import_promises3.mkdir)(options.outputDir, { recursive: true });
2102
2168
  const coverageMap = coverageMerger.getRawCoverageMap();
2103
- const context = istanbulLibReport.createContext({
2169
+ const context = (0, import_istanbul_lib_report.createContext)({
2104
2170
  dir: options.outputDir,
2105
2171
  watermarks: {
2106
2172
  statements: [50, 80],
@@ -2110,13 +2176,16 @@ async function generateIstanbulHtmlReport(coverageMerger, options) {
2110
2176
  },
2111
2177
  coverageMap
2112
2178
  });
2113
- const reporter = istanbulReports.create("html", {
2179
+ const reporter = (0, import_istanbul_reports.create)("html", {
2114
2180
  skipEmpty: false,
2115
2181
  subdir: ""
2116
2182
  });
2117
2183
  reporter.execute(context);
2118
2184
  return `${options.outputDir}/index.html`;
2119
2185
  }
2186
+
2187
+ // src/code-coverage/fixtures.ts
2188
+ var import_test = require("@playwright/test");
2120
2189
  var COVERAGE_EXTRACT_TIMEOUT_MS = 3e4;
2121
2190
  async function extractCoverageFromPage(page, timeoutMs = COVERAGE_EXTRACT_TIMEOUT_MS) {
2122
2191
  return Promise.race([
@@ -2145,9 +2214,30 @@ var coverageFixtures = {
2145
2214
  { auto: true }
2146
2215
  ]
2147
2216
  };
2148
- var test = test$1.test.extend(
2217
+ var test = import_test.test.extend(
2149
2218
  coverageFixtures
2150
2219
  );
2220
+
2221
+ // src/reporter/log.ts
2222
+ var chalkPromise = null;
2223
+ async function getChalk() {
2224
+ if (!chalkPromise) {
2225
+ chalkPromise = import("chalk").then((m) => m.default).catch((error) => {
2226
+ console.warn("\u26A0\uFE0F TestDino: Failed to load chalk, using plain text output");
2227
+ console.debug("Chalk import error:", error.message);
2228
+ return {
2229
+ green: (s) => s,
2230
+ yellow: (s) => s,
2231
+ red: (s) => s,
2232
+ blue: (s) => s,
2233
+ cyan: (s) => s,
2234
+ dim: (s) => s,
2235
+ bold: (s) => s
2236
+ };
2237
+ });
2238
+ }
2239
+ return chalkPromise;
2240
+ }
2151
2241
  function stripAnsi(str) {
2152
2242
  return str.replace(/\u001b\[[0-9;]*m/g, "");
2153
2243
  }
@@ -2176,20 +2266,20 @@ function shortenPath(filePath) {
2176
2266
  const parts = filePath.split("/");
2177
2267
  return parts.length > 2 ? parts.slice(-2).join("/") : filePath;
2178
2268
  }
2179
- function colorPct(pct) {
2180
- const color = pct >= 80 ? chalk__default.default.green : pct >= 50 ? chalk__default.default.yellow : chalk__default.default.red;
2269
+ function colorPct(pct, chalk) {
2270
+ const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;
2181
2271
  return color(pct === 100 ? "100%" : `${pct.toFixed(1)}%`);
2182
2272
  }
2183
- function printCoverageTable(event) {
2273
+ function printCoverageTableWithChalk(event, chalk) {
2184
2274
  const { summary, files } = event;
2185
- const row = (content) => ` ${chalk__default.default.dim("\u2502")} ${content}`;
2275
+ const row = (content) => ` ${chalk.dim("\u2502")} ${content}`;
2186
2276
  const nameW = 30;
2187
2277
  const colW = 10;
2188
- console.log(row(`${chalk__default.default.bold("Coverage")} ${chalk__default.default.dim(`${files.length} files`)}`));
2278
+ console.log(row(`${chalk.bold("Coverage")} ${chalk.dim(`${files.length} files`)}`));
2189
2279
  console.log(row(""));
2190
2280
  console.log(
2191
2281
  row(
2192
- ` ${chalk__default.default.dim(`${pad("File", nameW)}${padStart("Stmts", colW)}${padStart("Branch", colW)}${padStart("Funcs", colW)}${padStart("Lines", colW)}`)}`
2282
+ ` ${chalk.dim(`${pad("File", nameW)}${padStart("Stmts", colW)}${padStart("Branch", colW)}${padStart("Funcs", colW)}${padStart("Lines", colW)}`)}`
2193
2283
  )
2194
2284
  );
2195
2285
  for (const file of files) {
@@ -2197,67 +2287,67 @@ function printCoverageTable(event) {
2197
2287
  const short = name.length > nameW ? name.slice(0, nameW - 1) + "~" : name;
2198
2288
  console.log(
2199
2289
  row(
2200
- ` ${pad(short, nameW)}` + padStart(colorPct(file.statements.pct), colW) + padStart(colorPct(file.branches.pct), colW) + padStart(colorPct(file.functions.pct), colW) + padStart(colorPct(file.lines.pct), colW)
2290
+ ` ${pad(short, nameW)}` + padStart(colorPct(file.statements.pct, chalk), colW) + padStart(colorPct(file.branches.pct, chalk), colW) + padStart(colorPct(file.functions.pct, chalk), colW) + padStart(colorPct(file.lines.pct, chalk), colW)
2201
2291
  )
2202
2292
  );
2203
2293
  }
2204
- console.log(row(` ${chalk__default.default.dim("\u2500".repeat(nameW + colW * 4))}`));
2294
+ console.log(row(` ${chalk.dim("\u2500".repeat(nameW + colW * 4))}`));
2205
2295
  console.log(
2206
2296
  row(
2207
- ` ${chalk__default.default.bold(pad("All files", nameW))}` + padStart(colorPct(summary.statements.pct), colW) + padStart(colorPct(summary.branches.pct), colW) + padStart(colorPct(summary.functions.pct), colW) + padStart(colorPct(summary.lines.pct), colW)
2297
+ ` ${chalk.bold(pad("All files", nameW))}` + padStart(colorPct(summary.statements.pct, chalk), colW) + padStart(colorPct(summary.branches.pct, chalk), colW) + padStart(colorPct(summary.functions.pct, chalk), colW) + padStart(colorPct(summary.lines.pct, chalk), colW)
2208
2298
  )
2209
2299
  );
2210
2300
  }
2211
- function printRunSummary(result, streamingSuccess, data) {
2301
+ function printRunSummaryWithChalk(result, streamingSuccess, data, chalk) {
2212
2302
  const W = 72;
2213
- const topBorder = ` ${chalk__default.default.dim(`\u250C${"\u2500".repeat(W)}\u2510`)}`;
2214
- const bottomBorder = ` ${chalk__default.default.dim(`\u2514${"\u2500".repeat(W)}\u2518`)}`;
2215
- const divider = ` ${chalk__default.default.dim(`\u251C${"\u2500".repeat(W)}\u2524`)}`;
2216
- const row = (content) => ` ${chalk__default.default.dim("\u2502")} ${content}`;
2303
+ const topBorder = ` ${chalk.dim(`\u250C${"\u2500".repeat(W)}\u2510`)}`;
2304
+ const bottomBorder = ` ${chalk.dim(`\u2514${"\u2500".repeat(W)}\u2518`)}`;
2305
+ const divider = ` ${chalk.dim(`\u251C${"\u2500".repeat(W)}\u2524`)}`;
2306
+ const row = (content) => ` ${chalk.dim("\u2502")} ${content}`;
2217
2307
  console.log("");
2218
2308
  console.log(topBorder);
2219
- console.log(row(chalk__default.default.bold("TestDino Run Summary")));
2309
+ console.log(row(chalk.bold("TestDino Run Summary")));
2220
2310
  console.log(divider);
2221
- console.log(row(`${chalk__default.default.dim("Run")} ${data.runId}`));
2311
+ console.log(row(`${chalk.dim("Run")} ${data.runId}`));
2222
2312
  const git = data.runMetadata?.git;
2223
2313
  if (git?.branch || git?.commit?.hash) {
2224
- const branch = git.branch ? chalk__default.default.cyan(git.branch) : "";
2225
- const sha = git.commit?.hash ? chalk__default.default.dim(git.commit.hash.slice(0, 7)) : "";
2226
- const sep = branch && sha ? ` ${chalk__default.default.dim("@")} ` : "";
2227
- const msg = git.commit?.message ? ` ${chalk__default.default.dim(git.commit.message.split("\n")[0].slice(0, 50))}` : "";
2228
- console.log(row(`${chalk__default.default.dim("Git")} ${branch}${sep}${sha}${msg}`));
2314
+ const branch = git.branch ? chalk.cyan(git.branch) : "";
2315
+ const sha = git.commit?.hash ? chalk.dim(git.commit.hash.slice(0, 7)) : "";
2316
+ const sep = branch && sha ? ` ${chalk.dim("@")} ` : "";
2317
+ const msg = git.commit?.message ? ` ${chalk.dim(git.commit.message.split("\n")[0].slice(0, 50))}` : "";
2318
+ console.log(row(`${chalk.dim("Git")} ${branch}${sep}${sha}${msg}`));
2229
2319
  }
2230
2320
  console.log(divider);
2231
- const statusColor = result.status === "passed" ? chalk__default.default.green : result.status === "failed" ? chalk__default.default.red : chalk__default.default.yellow;
2321
+ const statusColor = result.status === "passed" ? chalk.green : result.status === "failed" ? chalk.red : chalk.yellow;
2232
2322
  const statusLabel = result.status === "timedout" ? "timed out" : result.status;
2233
2323
  console.log(
2234
2324
  row(
2235
- `${chalk__default.default.bold("Tests")} ${statusColor(statusLabel.toUpperCase())} ${chalk__default.default.dim(formatDuration(result.duration))}`
2325
+ `${chalk.bold("Tests")} ${statusColor(statusLabel.toUpperCase())} ${chalk.dim(formatDuration(result.duration))}`
2236
2326
  )
2237
2327
  );
2238
2328
  const counts = [];
2239
- if (data.testCounts.passed > 0) counts.push(chalk__default.default.green(`${data.testCounts.passed} passed`));
2240
- if (data.testCounts.failed > 0) counts.push(chalk__default.default.red(`${data.testCounts.failed} failed`));
2241
- if (data.testCounts.flaky > 0) counts.push(chalk__default.default.yellow(`${data.testCounts.flaky} flaky`));
2242
- if (data.testCounts.skipped > 0) counts.push(chalk__default.default.dim(`${data.testCounts.skipped} skipped`));
2243
- if (data.testCounts.timedOut > 0) counts.push(chalk__default.default.red(`${data.testCounts.timedOut} timed out`));
2244
- if (data.testCounts.interrupted > 0) counts.push(chalk__default.default.yellow(`${data.testCounts.interrupted} interrupted`));
2245
- const retriedStr = data.testCounts.retried > 0 ? ` ${chalk__default.default.dim(`(${data.testCounts.retried} retries)`)}` : "";
2246
- console.log(row(` ${counts.join(chalk__default.default.dim(" \xB7 "))} ${chalk__default.default.dim(`of ${data.totalTests}`)}${retriedStr}`));
2329
+ if (data.testCounts.passed > 0) counts.push(chalk.green(`${data.testCounts.passed} passed`));
2330
+ if (data.testCounts.failed > 0) counts.push(chalk.red(`${data.testCounts.failed} failed`));
2331
+ if (data.testCounts.flaky > 0) counts.push(chalk.yellow(`${data.testCounts.flaky} flaky`));
2332
+ if (data.testCounts.skipped > 0) counts.push(chalk.dim(`${data.testCounts.skipped} skipped`));
2333
+ if (data.testCounts.timedOut > 0) counts.push(chalk.red(`${data.testCounts.timedOut} timed out`));
2334
+ if (data.testCounts.interrupted > 0) counts.push(chalk.yellow(`${data.testCounts.interrupted} interrupted`));
2335
+ const retriedStr = data.testCounts.retried > 0 ? ` ${chalk.dim(`(${data.testCounts.retried} retries)`)}` : "";
2336
+ console.log(row(` ${counts.join(chalk.dim(" \xB7 "))} ${chalk.dim(`of ${data.totalTests}`)}${retriedStr}`));
2247
2337
  console.log(divider);
2248
2338
  const shardStr = data.shardInfo ? `${data.shardInfo.current}/${data.shardInfo.total}` : "\u2014";
2249
2339
  console.log(
2250
2340
  row(
2251
- `${pad(`${chalk__default.default.dim("Workers")} ${data.workerCount > 0 ? data.workerCount : "\u2014"}`, 28)}${pad(`${chalk__default.default.dim("Shard")} ${shardStr}`, 28)}${chalk__default.default.dim("Projects")} ${data.projectNames.size > 0 ? Array.from(data.projectNames).join(", ") : "\u2014"}`
2341
+ `${pad(`${chalk.dim("Workers")} ${data.workerCount > 0 ? data.workerCount : "\u2014"}`, 28)}${pad(`${chalk.dim("Shard")} ${shardStr}`, 28)}${chalk.dim("Projects")} ${data.projectNames.size > 0 ? Array.from(data.projectNames).join(", ") : "\u2014"}`
2252
2342
  )
2253
2343
  );
2254
2344
  console.log(divider);
2255
2345
  const transport = data.useHttpFallback ? "HTTP" : "WebSocket";
2256
- const streamIcon = streamingSuccess ? chalk__default.default.green("sent") : chalk__default.default.red("failed");
2257
- console.log(row(`${chalk__default.default.bold("Stream")} ${streamIcon} ${chalk__default.default.dim(`via ${transport}`)}`));
2346
+ const streamIcon = streamingSuccess ? chalk.green("sent") : chalk.red("failed");
2347
+ console.log(row(`${chalk.bold("Stream")} ${streamIcon} ${chalk.dim(`via ${transport}`)}`));
2258
2348
  if (data.lastCoverageEvent) {
2259
2349
  console.log(divider);
2260
- printCoverageTable(data.lastCoverageEvent);
2350
+ printCoverageTableWithChalk(data.lastCoverageEvent, chalk);
2261
2351
  }
2262
2352
  console.log(bottomBorder);
2263
2353
  console.log("");
@@ -2272,8 +2362,14 @@ var createReporterLog = (options) => ({
2272
2362
  console.log(`\u{1F50D} TestDino: ${msg}`);
2273
2363
  }
2274
2364
  },
2275
- printRunSummary,
2276
- printCoverageTable
2365
+ printRunSummary: async (result, streamingSuccess, data) => {
2366
+ const chalk = await getChalk();
2367
+ printRunSummaryWithChalk(result, streamingSuccess, data, chalk);
2368
+ },
2369
+ printCoverageTable: async (event) => {
2370
+ const chalk = await getChalk();
2371
+ printCoverageTableWithChalk(event, chalk);
2372
+ }
2277
2373
  });
2278
2374
 
2279
2375
  // src/reporter/index.ts
@@ -2326,7 +2422,7 @@ var TestdinoReporter = class {
2326
2422
  constructor(config = {}) {
2327
2423
  const cliConfig = this.loadCliConfig();
2328
2424
  this.config = { ...config, ...cliConfig };
2329
- this.runId = crypto.randomUUID();
2425
+ this.runId = (0, import_crypto2.randomUUID)();
2330
2426
  this.log = createReporterLog({ debug: this.config.debug ?? false });
2331
2427
  this.coverageEnabled = this.config.coverage?.enabled ?? false;
2332
2428
  if (this.coverageEnabled) {
@@ -2356,10 +2452,10 @@ var TestdinoReporter = class {
2356
2452
  return {};
2357
2453
  }
2358
2454
  try {
2359
- if (!fs.existsSync(cliConfigPath)) {
2455
+ if (!(0, import_fs2.existsSync)(cliConfigPath)) {
2360
2456
  return {};
2361
2457
  }
2362
- const configContent = fs.readFileSync(cliConfigPath, "utf-8");
2458
+ const configContent = (0, import_fs2.readFileSync)(cliConfigPath, "utf-8");
2363
2459
  const cliConfig = JSON.parse(configContent);
2364
2460
  const mappedConfig = {};
2365
2461
  if (cliConfig.token !== void 0 && typeof cliConfig.token === "string") {
@@ -2642,7 +2738,7 @@ var TestdinoReporter = class {
2642
2738
  }
2643
2739
  }
2644
2740
  if (this.coverageEnabled && this.coverageMerger) {
2645
- this.extractCoverageFromResult(test2, result, this.coverageMerger);
2741
+ this.extractCoverageFromResult(result, this.coverageMerger);
2646
2742
  }
2647
2743
  const projectName = this.getProjectName(test2);
2648
2744
  if (projectName) {
@@ -2743,19 +2839,6 @@ var TestdinoReporter = class {
2743
2839
  }
2744
2840
  if (this.coverageEnabled && this.coverageMerger?.hasCoverage) {
2745
2841
  try {
2746
- if (this.projectNames.size > 1 && !this.config.coverage?.projects?.length) {
2747
- this.log.warn(
2748
- `Coverage collected from ${this.projectNames.size} projects (${Array.from(this.projectNames).join(", ")}). Set coverage.projects to a single project to avoid duplicate counts`
2749
- );
2750
- }
2751
- if (this.config.coverage?.projects?.length) {
2752
- const unmatched = this.config.coverage.projects.filter((p) => !this.projectNames.has(p));
2753
- if (unmatched.length > 0) {
2754
- this.log.warn(
2755
- `coverage.projects contains unmatched values: ${unmatched.join(", ")}. Available projects: ${Array.from(this.projectNames).join(", ")}`
2756
- );
2757
- }
2758
- }
2759
2842
  const coverageEvent = this.buildCoverageEvent(this.coverageMerger);
2760
2843
  await this.buffer.add(coverageEvent);
2761
2844
  this.lastCoverageEvent = coverageEvent;
@@ -2763,15 +2846,21 @@ var TestdinoReporter = class {
2763
2846
  if (thresholdFailures.length > 0) {
2764
2847
  this.coverageThresholdFailed = true;
2765
2848
  }
2766
- if (this.config.coverage?.localReport) {
2767
- const outputDir = this.config.coverage.localReportDir || "./testdino-coverage";
2849
+ } catch (error) {
2850
+ this.log.warn(`Failed to build coverage event: ${error instanceof Error ? error.message : String(error)}`);
2851
+ }
2852
+ if (this.coverageMerger?.hasCoverage) {
2853
+ try {
2854
+ const outputDir = "./coverage";
2768
2855
  const reportPath = await generateIstanbulHtmlReport(this.coverageMerger, {
2769
2856
  outputDir
2770
2857
  });
2771
2858
  this.log.info(`Coverage Report: ${reportPath}`);
2859
+ } catch (error) {
2860
+ this.log.warn(
2861
+ `Failed to generate local HTML report: ${error instanceof Error ? error.message : String(error)}`
2862
+ );
2772
2863
  }
2773
- } catch (error) {
2774
- this.log.warn(`Failed to build coverage event: ${error instanceof Error ? error.message : String(error)}`);
2775
2864
  }
2776
2865
  } else if (this.coverageEnabled && !this.coverageMerger?.hasCoverage) {
2777
2866
  this.log.warn(
@@ -2839,7 +2928,7 @@ var TestdinoReporter = class {
2839
2928
  useHttpFallback: this.useHttpFallback,
2840
2929
  lastCoverageEvent: this.lastCoverageEvent
2841
2930
  };
2842
- this.log.printRunSummary(result, delivered, summaryData);
2931
+ await this.log.printRunSummary(result, delivered, summaryData);
2843
2932
  if (this.coverageThresholdFailed && this.lastCoverageEvent) {
2844
2933
  const failures = this.checkCoverageThresholds(this.lastCoverageEvent.summary);
2845
2934
  this.log.error("Coverage thresholds not met:");
@@ -3476,16 +3565,8 @@ var TestdinoReporter = class {
3476
3565
  /**
3477
3566
  * Extract coverage fragment from test result attachments and merge incrementally.
3478
3567
  * The fixture attaches coverage as an in-memory JSON attachment named 'testdino-coverage'.
3479
- *
3480
- * Respects coverage.projects filter to avoid duplicate coverage from multiple browser projects.
3481
3568
  */
3482
- extractCoverageFromResult(test2, result, merger) {
3483
- if (this.config.coverage?.projects) {
3484
- const projectName = this.getProjectName(test2);
3485
- if (projectName && !this.config.coverage.projects.includes(projectName)) {
3486
- return;
3487
- }
3488
- }
3569
+ extractCoverageFromResult(result, merger) {
3489
3570
  const coverageAttachment = result.attachments.find((a) => a.name === "testdino-coverage");
3490
3571
  if (!coverageAttachment?.body) return;
3491
3572
  try {
@@ -3509,7 +3590,9 @@ var TestdinoReporter = class {
3509
3590
  const files = merger.computeFileCoverage(rootDir);
3510
3591
  const isSharded = !!this.shardInfo;
3511
3592
  if (files.length > COVERAGE_FILE_COUNT_WARNING) {
3512
- this.log.warn(`Coverage includes ${files.length} files \u2014 consider using coverage.projects to reduce scope`);
3593
+ this.log.warn(
3594
+ `Coverage includes ${files.length} files \u2014 consider using coverage.include/exclude to reduce scope`
3595
+ );
3513
3596
  }
3514
3597
  const event = {
3515
3598
  type: "coverage:data",
@@ -3550,13 +3633,10 @@ var TestdinoReporter = class {
3550
3633
  return failures;
3551
3634
  }
3552
3635
  };
3553
-
3554
- Object.defineProperty(exports, "expect", {
3555
- enumerable: true,
3556
- get: function () { return test$1.expect; }
3636
+ // Annotate the CommonJS export names for ESM import in node:
3637
+ 0 && (module.exports = {
3638
+ coverageFixtures,
3639
+ expect,
3640
+ test
3557
3641
  });
3558
- exports.coverageFixtures = coverageFixtures;
3559
- exports.default = TestdinoReporter;
3560
- exports.test = test;
3561
- //# sourceMappingURL=index.js.map
3562
3642
  //# sourceMappingURL=index.js.map