@arghajit/dummy 0.1.0 → 0.1.1

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
  "use strict";
2
- // input_file_0.ts
3
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
3
  if (k2 === undefined) k2 = k;
5
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -38,7 +37,8 @@ exports.PlaywrightPulseReporter = void 0;
38
37
  const fs = __importStar(require("fs/promises"));
39
38
  const path = __importStar(require("path"));
40
39
  const crypto_1 = require("crypto");
41
- const attachment_utils_1 = require("./attachment-utils"); // Use relative path
40
+ const ua_parser_js_1 = require("ua-parser-js");
41
+ const os = __importStar(require("os"));
42
42
  const convertStatus = (status, testCase) => {
43
43
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
44
44
  return "failed";
@@ -60,9 +60,10 @@ const convertStatus = (status, testCase) => {
60
60
  };
61
61
  const TEMP_SHARD_FILE_PREFIX = ".pulse-shard-results-";
62
62
  const ATTACHMENTS_SUBDIR = "attachments";
63
+ const INDIVIDUAL_REPORTS_SUBDIR = "pulse-results";
63
64
  class PlaywrightPulseReporter {
64
65
  constructor(options = {}) {
65
- var _a, _b;
66
+ var _a, _b, _c;
66
67
  this.results = [];
67
68
  this.baseOutputFile = "playwright-pulse-report.json";
68
69
  this.isSharded = false;
@@ -71,6 +72,7 @@ class PlaywrightPulseReporter {
71
72
  this.baseOutputFile = (_a = options.outputFile) !== null && _a !== void 0 ? _a : this.baseOutputFile;
72
73
  this.outputDir = (_b = options.outputDir) !== null && _b !== void 0 ? _b : "pulse-report";
73
74
  this.attachmentsDir = path.join(this.outputDir, ATTACHMENTS_SUBDIR);
75
+ this.resetOnEachRun = (_c = options.resetOnEachRun) !== null && _c !== void 0 ? _c : true;
74
76
  }
75
77
  printsToStdio() {
76
78
  return this.shardIndex === undefined || this.shardIndex === 0;
@@ -94,7 +96,7 @@ class PlaywrightPulseReporter {
94
96
  : undefined;
95
97
  this._ensureDirExists(this.outputDir)
96
98
  .then(() => {
97
- if (this.shardIndex === undefined || this.shardIndex === 0) {
99
+ if (this.printsToStdio()) {
98
100
  console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
99
101
  if (this.shardIndex === undefined ||
100
102
  (this.isSharded && this.shardIndex === 0)) {
@@ -105,9 +107,59 @@ class PlaywrightPulseReporter {
105
107
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
106
108
  }
107
109
  onTestBegin(test) {
108
- // console.log(`Starting test: ${test.title}`);
110
+ console.log(`Starting test: ${test.title}`);
109
111
  }
110
- async processStep(step, testId, browserName, testCase) {
112
+ getBrowserDetails(test) {
113
+ var _a, _b, _c, _d;
114
+ const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
115
+ const projectConfig = project === null || project === void 0 ? void 0 : project.use;
116
+ const userAgent = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.userAgent;
117
+ const configuredBrowserType = (_b = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.browserName) === null || _b === void 0 ? void 0 : _b.toLowerCase();
118
+ const parser = new ua_parser_js_1.UAParser(userAgent);
119
+ const result = parser.getResult();
120
+ let browserName = result.browser.name;
121
+ const browserVersion = result.browser.version
122
+ ? ` v${result.browser.version.split(".")[0]}`
123
+ : "";
124
+ const osName = result.os.name ? ` on ${result.os.name}` : "";
125
+ const osVersion = result.os.version
126
+ ? ` ${result.os.version.split(".")[0]}`
127
+ : "";
128
+ const deviceType = result.device.type;
129
+ let finalString;
130
+ if (browserName === undefined) {
131
+ browserName = configuredBrowserType;
132
+ finalString = `${browserName}`;
133
+ }
134
+ else {
135
+ if (deviceType === "mobile" || deviceType === "tablet") {
136
+ if ((_c = result.os.name) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes("android")) {
137
+ if (browserName.toLowerCase().includes("chrome"))
138
+ browserName = "Chrome Mobile";
139
+ else if (browserName.toLowerCase().includes("firefox"))
140
+ browserName = "Firefox Mobile";
141
+ else if (result.engine.name === "Blink" && !result.browser.name)
142
+ browserName = "Android WebView";
143
+ else if (browserName &&
144
+ !browserName.toLowerCase().includes("mobile")) {
145
+ // Keep it as is
146
+ }
147
+ else {
148
+ browserName = "Android Browser";
149
+ }
150
+ }
151
+ else if ((_d = result.os.name) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes("ios")) {
152
+ browserName = "Mobile Safari";
153
+ }
154
+ }
155
+ else if (browserName === "Electron") {
156
+ browserName = "Electron App";
157
+ }
158
+ finalString = `${browserName}${browserVersion}${osName}${osVersion}`;
159
+ }
160
+ return finalString.trim();
161
+ }
162
+ async processStep(step, testId, browserDetails, testCase) {
111
163
  var _a, _b, _c, _d;
112
164
  let stepStatus = "passed";
113
165
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
@@ -124,15 +176,14 @@ class PlaywrightPulseReporter {
124
176
  if (step.location) {
125
177
  codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
126
178
  }
127
- let stepTitle = step.title;
128
179
  return {
129
180
  id: `${testId}_step_${startTime.toISOString()}-${duration}-${(0, crypto_1.randomUUID)()}`,
130
- title: stepTitle,
181
+ title: step.title,
131
182
  status: stepStatus,
132
183
  duration: duration,
133
184
  startTime: startTime,
134
185
  endTime: endTime,
135
- browser: browserName,
186
+ browser: browserDetails,
136
187
  errorMessage: errorMessage,
137
188
  stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
138
189
  codeLocation: codeLocation || undefined,
@@ -146,21 +197,16 @@ class PlaywrightPulseReporter {
146
197
  };
147
198
  }
148
199
  async onTestEnd(test, result) {
149
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
200
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
150
201
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
151
- const browserName = ((_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) || (project === null || project === void 0 ? void 0 : project.name) || "unknown";
202
+ const browserDetails = this.getBrowserDetails(test);
152
203
  const testStatus = convertStatus(result.status, test);
153
204
  const startTime = new Date(result.startTime);
154
205
  const endTime = new Date(startTime.getTime() + result.duration);
155
- const testIdForFiles = test.id ||
156
- `${test
157
- .titlePath()
158
- .join("_")
159
- .replace(/[^a-zA-Z0-9]/g, "_")}_${startTime.getTime()}`;
160
206
  const processAllSteps = async (steps) => {
161
207
  let processed = [];
162
208
  for (const step of steps) {
163
- const processedStep = await this.processStep(step, testIdForFiles, browserName, test);
209
+ const processedStep = await this.processStep(step, test.id, browserDetails, test);
164
210
  processed.push(processedStep);
165
211
  if (step.steps && step.steps.length > 0) {
166
212
  processedStep.steps = await processAllSteps(step.steps);
@@ -170,7 +216,7 @@ class PlaywrightPulseReporter {
170
216
  };
171
217
  let codeSnippet = undefined;
172
218
  try {
173
- if (((_c = test.location) === null || _c === void 0 ? void 0 : _c.file) && ((_d = test.location) === null || _d === void 0 ? void 0 : _d.line) && ((_e = test.location) === null || _e === void 0 ? void 0 : _e.column)) {
219
+ if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) && ((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) && ((_d = test.location) === null || _d === void 0 ? void 0 : _d.column)) {
174
220
  const relativePath = path.relative(this.config.rootDir, test.location.file);
175
221
  codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
176
222
  }
@@ -178,56 +224,91 @@ class PlaywrightPulseReporter {
178
224
  catch (e) {
179
225
  console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
180
226
  }
181
- const stdoutMessages = [];
182
- if (result.stdout && result.stdout.length > 0) {
183
- result.stdout.forEach((item) => {
184
- stdoutMessages.push(typeof item === "string" ? item : item.toString());
185
- });
186
- }
187
- const stderrMessages = [];
188
- if (result.stderr && result.stderr.length > 0) {
189
- result.stderr.forEach((item) => {
190
- stderrMessages.push(typeof item === "string" ? item : item.toString());
191
- });
192
- }
193
- const uniqueTestId = test.id;
227
+ const stdoutMessages = result.stdout.map((item) => typeof item === "string" ? item : item.toString());
228
+ const stderrMessages = result.stderr.map((item) => typeof item === "string" ? item : item.toString());
229
+ const maxWorkers = this.config.workers;
230
+ let mappedWorkerId = result.workerIndex === -1
231
+ ? -1
232
+ : (result.workerIndex % (maxWorkers > 0 ? maxWorkers : 1)) + 1;
233
+ const testSpecificData = {
234
+ workerId: mappedWorkerId,
235
+ totalWorkers: maxWorkers,
236
+ configFile: this.config.configFile,
237
+ metadata: this.config.metadata
238
+ ? JSON.stringify(this.config.metadata)
239
+ : undefined,
240
+ };
194
241
  const pulseResult = {
195
- id: uniqueTestId,
242
+ id: test.id,
196
243
  runId: "TBD",
197
244
  name: test.titlePath().join(" > "),
198
- suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_f = this.config.projects[0]) === null || _f === void 0 ? void 0 : _f.name) || "Default Suite",
245
+ suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_e = this.config.projects[0]) === null || _e === void 0 ? void 0 : _e.name) || "Default Suite",
199
246
  status: testStatus,
200
247
  duration: result.duration,
201
248
  startTime: startTime,
202
249
  endTime: endTime,
203
- browser: browserName,
250
+ browser: browserDetails,
204
251
  retries: result.retry,
205
- steps: ((_g = result.steps) === null || _g === void 0 ? void 0 : _g.length) ? await processAllSteps(result.steps) : [],
206
- errorMessage: (_h = result.error) === null || _h === void 0 ? void 0 : _h.message,
207
- stackTrace: (_j = result.error) === null || _j === void 0 ? void 0 : _j.stack,
252
+ steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
253
+ errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
254
+ stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
255
+ snippet: (_j = result.error) === null || _j === void 0 ? void 0 : _j.snippet,
208
256
  codeSnippet: codeSnippet,
209
257
  tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
210
258
  screenshots: [],
211
- videoPath: undefined,
259
+ videoPath: [],
212
260
  tracePath: undefined,
261
+ attachments: [],
213
262
  stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
214
263
  stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
264
+ ...testSpecificData,
215
265
  };
216
- try {
217
- (0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
218
- }
219
- catch (attachError) {
220
- console.error(`Pulse Reporter: Error processing attachments for test ${pulseResult.name} (ID: ${testIdForFiles}): ${attachError.message}`);
221
- }
222
- const existingTestIndex = this.results.findIndex((r) => r.id === uniqueTestId);
223
- if (existingTestIndex !== -1) {
224
- if (pulseResult.retries >= this.results[existingTestIndex].retries) {
225
- this.results[existingTestIndex] = pulseResult;
266
+ for (const [index, attachment] of result.attachments.entries()) {
267
+ if (!attachment.path)
268
+ continue;
269
+ try {
270
+ const testSubfolder = test.id.replace(/[^a-zA-Z0-9_-]/g, "_");
271
+ const safeAttachmentName = path
272
+ .basename(attachment.path)
273
+ .replace(/[^a-zA-Z0-9_.-]/g, "_");
274
+ const uniqueFileName = `${index}-${Date.now()}-${safeAttachmentName}`;
275
+ const relativeDestPath = path.join(ATTACHMENTS_SUBDIR, testSubfolder, uniqueFileName);
276
+ const absoluteDestPath = path.join(this.outputDir, relativeDestPath);
277
+ await this._ensureDirExists(path.dirname(absoluteDestPath));
278
+ await fs.copyFile(attachment.path, absoluteDestPath);
279
+ if (attachment.contentType.startsWith("image/")) {
280
+ (_k = pulseResult.screenshots) === null || _k === void 0 ? void 0 : _k.push(relativeDestPath);
281
+ }
282
+ else if (attachment.contentType.startsWith("video/")) {
283
+ (_l = pulseResult.videoPath) === null || _l === void 0 ? void 0 : _l.push(relativeDestPath);
284
+ }
285
+ else if (attachment.name === "trace") {
286
+ pulseResult.tracePath = relativeDestPath;
287
+ }
288
+ else {
289
+ (_m = pulseResult.attachments) === null || _m === void 0 ? void 0 : _m.push({
290
+ name: attachment.name,
291
+ path: relativeDestPath,
292
+ contentType: attachment.contentType,
293
+ });
294
+ }
295
+ }
296
+ catch (err) {
297
+ console.error(`Pulse Reporter: Failed to process attachment "${attachment.name}" for test ${pulseResult.name}. Error: ${err.message}`);
226
298
  }
227
299
  }
228
- else {
229
- this.results.push(pulseResult);
300
+ this.results.push(pulseResult);
301
+ }
302
+ _getFinalizedResults(allResults) {
303
+ const finalResultsMap = new Map();
304
+ for (const result of allResults) {
305
+ const existing = finalResultsMap.get(result.id);
306
+ // Keep the result with the highest retry attempt for each test ID
307
+ if (!existing || result.retries >= existing.retries) {
308
+ finalResultsMap.set(result.id, result);
309
+ }
230
310
  }
311
+ return Array.from(finalResultsMap.values());
231
312
  }
232
313
  onError(error) {
233
314
  var _a;
@@ -236,6 +317,20 @@ class PlaywrightPulseReporter {
236
317
  console.error(error.stack);
237
318
  }
238
319
  }
320
+ _getEnvDetails() {
321
+ return {
322
+ host: os.hostname(),
323
+ os: `${os.platform()} ${os.release()}`,
324
+ cpu: {
325
+ model: os.cpus()[0] ? os.cpus()[0].model : "N/A",
326
+ cores: os.cpus().length,
327
+ },
328
+ memory: `${(os.totalmem() / 1024 ** 3).toFixed(2)}GB`,
329
+ node: process.version,
330
+ v8: process.versions.v8,
331
+ cwd: process.cwd(),
332
+ };
333
+ }
239
334
  async _writeShardResults() {
240
335
  if (this.shardIndex === undefined) {
241
336
  return;
@@ -268,14 +363,7 @@ class PlaywrightPulseReporter {
268
363
  }
269
364
  }
270
365
  }
271
- let finalUniqueResultsMap = new Map();
272
- for (const result of allShardProcessedResults) {
273
- const existing = finalUniqueResultsMap.get(result.id);
274
- if (!existing || result.retries >= existing.retries) {
275
- finalUniqueResultsMap.set(result.id, result);
276
- }
277
- }
278
- const finalResultsList = Array.from(finalUniqueResultsMap.values());
366
+ const finalResultsList = this._getFinalizedResults(allShardProcessedResults);
279
367
  finalResultsList.forEach((r) => (r.runId = finalRunData.id));
280
368
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
281
369
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
@@ -322,131 +410,167 @@ class PlaywrightPulseReporter {
322
410
  }
323
411
  }
324
412
  async onEnd(result) {
325
- var _a, _b, _c;
326
413
  if (this.shardIndex !== undefined) {
327
414
  await this._writeShardResults();
328
415
  return;
329
416
  }
417
+ // De-duplicate and handle retries here, in a safe, single-threaded context.
418
+ const finalResults = this._getFinalizedResults(this.results);
330
419
  const runEndTime = Date.now();
331
420
  const duration = runEndTime - this.runStartTime;
332
- const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
421
+ const runId = `run-${this.runStartTime}-581d5ad8-ce75-4ca5-94a6-ed29c466c815`;
422
+ const environmentDetails = this._getEnvDetails();
333
423
  const runData = {
334
424
  id: runId,
335
425
  timestamp: new Date(this.runStartTime),
336
- totalTests: 0,
337
- passed: 0,
338
- failed: 0,
339
- skipped: 0,
426
+ // Use the length of the de-duplicated array for all counts
427
+ totalTests: finalResults.length,
428
+ passed: finalResults.filter((r) => r.status === "passed").length,
429
+ failed: finalResults.filter((r) => r.status === "failed").length,
430
+ skipped: finalResults.filter((r) => r.status === "skipped").length,
340
431
  duration,
432
+ environment: environmentDetails,
341
433
  };
342
- let finalReport = undefined; // Initialize as undefined
434
+ finalResults.forEach((r) => (r.runId = runId));
435
+ let finalReport = undefined;
343
436
  if (this.isSharded) {
437
+ // The _mergeShardResults method will handle its own de-duplication
344
438
  finalReport = await this._mergeShardResults(runData);
345
439
  }
346
440
  else {
347
- this.results.forEach((r) => (r.runId = runId));
348
- runData.passed = this.results.filter((r) => r.status === "passed").length;
349
- runData.failed = this.results.filter((r) => r.status === "failed").length;
350
- runData.skipped = this.results.filter((r) => r.status === "skipped").length;
351
- runData.totalTests = this.results.length;
352
- const reviveDates = (key, value) => {
353
- const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
354
- if (typeof value === "string" && isoDateRegex.test(value)) {
355
- const date = new Date(value);
356
- return !isNaN(date.getTime()) ? date : value;
357
- }
358
- return value;
359
- };
360
- const properlyTypedResults = JSON.parse(JSON.stringify(this.results), reviveDates);
361
441
  finalReport = {
362
442
  run: runData,
363
- results: properlyTypedResults,
443
+ // Use the de-duplicated results
444
+ results: finalResults,
364
445
  metadata: { generatedAt: new Date().toISOString() },
365
446
  };
366
447
  }
367
448
  if (!finalReport) {
368
449
  console.error("PlaywrightPulseReporter: CRITICAL - finalReport object was not generated. Cannot create summary.");
369
- const errorSummary = `
370
- PlaywrightPulseReporter: Run Finished
371
- -----------------------------------------
372
- Overall Status: ERROR (Report data missing)
373
- Total Tests: N/A
374
- Passed: N/A
375
- Failed: N/A
376
- Skipped: N/A
377
- Duration: N/A
378
- -----------------------------------------`;
379
- if (this.printsToStdio()) {
380
- console.log(errorSummary);
381
- }
382
- const errorReport = {
383
- run: {
384
- id: runId,
385
- timestamp: new Date(this.runStartTime),
386
- totalTests: 0,
387
- passed: 0,
388
- failed: 0,
389
- skipped: 0,
390
- duration: duration,
391
- },
392
- results: [],
393
- metadata: {
394
- generatedAt: new Date().toISOString(),
395
- },
396
- };
397
- const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
450
+ return;
451
+ }
452
+ const jsonReplacer = (key, value) => {
453
+ if (value instanceof Date)
454
+ return value.toISOString();
455
+ if (typeof value === "bigint")
456
+ return value.toString();
457
+ return value;
458
+ };
459
+ if (this.resetOnEachRun) {
460
+ const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
398
461
  try {
399
462
  await this._ensureDirExists(this.outputDir);
400
- await fs.writeFile(finalOutputPathOnError, JSON.stringify(errorReport, null, 2));
401
- console.warn(`PlaywrightPulseReporter: Wrote an error report to ${finalOutputPathOnError} as finalReport was missing.`);
463
+ await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, jsonReplacer, 2));
464
+ if (this.printsToStdio()) {
465
+ console.log(`PlaywrightPulseReporter: JSON report written to ${finalOutputPath}`);
466
+ }
402
467
  }
403
- catch (writeError) {
404
- console.error(`PlaywrightPulseReporter: Failed to write error report: ${writeError.message}`);
468
+ catch (error) {
469
+ console.error(`Pulse Reporter: Failed to write final JSON report to ${finalOutputPath}. Error: ${error.message}`);
470
+ }
471
+ }
472
+ else {
473
+ // Logic for appending/merging reports
474
+ const pulseResultsDir = path.join(this.outputDir, INDIVIDUAL_REPORTS_SUBDIR);
475
+ const individualReportPath = path.join(pulseResultsDir, `playwright-pulse-report-${Date.now()}.json`);
476
+ try {
477
+ await this._ensureDirExists(pulseResultsDir);
478
+ await fs.writeFile(individualReportPath, JSON.stringify(finalReport, jsonReplacer, 2));
479
+ if (this.printsToStdio()) {
480
+ console.log(`PlaywrightPulseReporter: Individual run report for merging written to ${individualReportPath}`);
481
+ }
482
+ await this._mergeAllRunReports();
483
+ }
484
+ catch (error) {
485
+ console.error(`Pulse Reporter: Failed to write or merge report. Error: ${error.message}`);
405
486
  }
406
- return;
407
487
  }
408
- const reportRunData = finalReport.run;
409
- const finalRunStatus = ((_a = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
410
- ? "failed"
411
- : ((_b = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
412
- ? result.status === "interrupted"
413
- ? "interrupted"
414
- : "no tests or error"
415
- : "passed";
416
- const summary = `
417
- PlaywrightPulseReporter: Run Finished
418
- -----------------------------------------
419
- Overall Status: ${finalRunStatus.toUpperCase()}
420
- Total Tests: ${(reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) || 0}
421
- Passed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.passed}
422
- Failed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed}
423
- Skipped: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.skipped}
424
- Duration: ${(((_c = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.duration) !== null && _c !== void 0 ? _c : 0) / 1000).toFixed(2)}s
425
- -----------------------------------------`;
426
- if (this.printsToStdio()) {
427
- console.log(summary);
488
+ if (this.isSharded) {
489
+ await this._cleanupTemporaryFiles();
428
490
  }
491
+ }
492
+ async _mergeAllRunReports() {
493
+ const pulseResultsDir = path.join(this.outputDir, INDIVIDUAL_REPORTS_SUBDIR);
429
494
  const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
495
+ let reportFiles;
496
+ try {
497
+ const allFiles = await fs.readdir(pulseResultsDir);
498
+ reportFiles = allFiles.filter((file) => file.startsWith("playwright-pulse-report-") && file.endsWith(".json"));
499
+ }
500
+ catch (error) {
501
+ if (error.code === "ENOENT") {
502
+ if (this.printsToStdio()) {
503
+ console.log(`Pulse Reporter: No individual reports directory found at ${pulseResultsDir}. Skipping merge.`);
504
+ }
505
+ return;
506
+ }
507
+ console.error(`Pulse Reporter: Error reading report directory ${pulseResultsDir}:`, error);
508
+ return;
509
+ }
510
+ if (reportFiles.length === 0) {
511
+ if (this.printsToStdio()) {
512
+ console.log("Pulse Reporter: No matching JSON report files found to merge.");
513
+ }
514
+ return;
515
+ }
516
+ const allResultsFromAllFiles = [];
517
+ let latestTimestamp = new Date(0);
518
+ let lastRunEnvironment = undefined;
519
+ let totalDuration = 0;
520
+ for (const file of reportFiles) {
521
+ const filePath = path.join(pulseResultsDir, file);
522
+ try {
523
+ const content = await fs.readFile(filePath, "utf-8");
524
+ const json = JSON.parse(content);
525
+ if (json.run) {
526
+ const runTimestamp = new Date(json.run.timestamp);
527
+ if (runTimestamp > latestTimestamp) {
528
+ latestTimestamp = runTimestamp;
529
+ lastRunEnvironment = json.run.environment || undefined;
530
+ }
531
+ }
532
+ if (json.results) {
533
+ allResultsFromAllFiles.push(...json.results);
534
+ }
535
+ }
536
+ catch (err) {
537
+ console.warn(`Pulse Reporter: Could not parse report file ${filePath}. Skipping. Error: ${err.message}`);
538
+ }
539
+ }
540
+ // De-duplicate the results from ALL merged files using the helper function
541
+ const finalMergedResults = this._getFinalizedResults(allResultsFromAllFiles);
542
+ // Sum the duration from the final, de-duplicated list of tests
543
+ totalDuration = finalMergedResults.reduce((acc, r) => acc + (r.duration || 0), 0);
544
+ const combinedRun = {
545
+ id: `merged-${Date.now()}`,
546
+ timestamp: latestTimestamp,
547
+ environment: lastRunEnvironment,
548
+ // Recalculate counts based on the truly final, de-duplicated list
549
+ totalTests: finalMergedResults.length,
550
+ passed: finalMergedResults.filter((r) => r.status === "passed").length,
551
+ failed: finalMergedResults.filter((r) => r.status === "failed").length,
552
+ skipped: finalMergedResults.filter((r) => r.status === "skipped").length,
553
+ duration: totalDuration,
554
+ };
555
+ const finalReport = {
556
+ run: combinedRun,
557
+ results: finalMergedResults, // Use the de-duplicated list
558
+ metadata: {
559
+ generatedAt: new Date().toISOString(),
560
+ },
561
+ };
430
562
  try {
431
- await this._ensureDirExists(this.outputDir);
432
563
  await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
433
564
  if (value instanceof Date)
434
565
  return value.toISOString();
435
- if (typeof value === "bigint")
436
- return value.toString();
437
566
  return value;
438
567
  }, 2));
439
568
  if (this.printsToStdio()) {
440
- console.log(`PlaywrightPulseReporter: JSON report written to ${finalOutputPath}`);
569
+ console.log(`PlaywrightPulseReporter: Merged report with ${finalMergedResults.length} total results saved to ${finalOutputPath}`);
441
570
  }
442
571
  }
443
- catch (error) {
444
- console.error(`Pulse Reporter: Failed to write final JSON report to ${finalOutputPath}. Error: ${error.message}`);
445
- }
446
- finally {
447
- if (this.isSharded) {
448
- await this._cleanupTemporaryFiles();
449
- }
572
+ catch (err) {
573
+ console.error(`Pulse Reporter: Failed to write final merged report to ${finalOutputPath}. Error: ${err.message}`);
450
574
  }
451
575
  }
452
576
  }