@arghajit/dummy 0.1.0-beta-4 → 0.1.0-beta-6

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.
@@ -87,13 +87,14 @@ class PlaywrightPulseReporter {
87
87
  : configDir;
88
88
  this.outputDir = path.resolve(configFileDir, (_a = this.options.outputDir) !== null && _a !== void 0 ? _a : "pulse-report");
89
89
  this.attachmentsDir = path.resolve(this.outputDir, ATTACHMENTS_SUBDIR);
90
- this.options.outputDir = this.outputDir;
90
+ this.options.outputDir = this.outputDir; // Ensure options has the resolved path
91
91
  const totalShards = this.config.shard ? this.config.shard.total : 1;
92
92
  this.isSharded = totalShards > 1;
93
93
  this.shardIndex = this.config.shard
94
94
  ? this.config.shard.current - 1
95
95
  : undefined;
96
96
  this._ensureDirExists(this.outputDir)
97
+ .then(() => this._ensureDirExists(this.attachmentsDir)) // Also ensure attachmentsDir exists
97
98
  .then(() => {
98
99
  if (this.shardIndex === undefined || this.shardIndex === 0) {
99
100
  console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
@@ -106,10 +107,11 @@ class PlaywrightPulseReporter {
106
107
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
107
108
  }
108
109
  onTestBegin(test) {
109
- // console.log(`Starting test: ${test.title}`);
110
+ // Optional: console.log(`Starting test: ${test.titlePath().join(' > ')} for project ${test.parent?.project()?.name}`);
110
111
  }
111
- async processStep(step, testId, browserName, testCase) {
112
- var _a, _b, _c, _d;
112
+ async processStep(step, testId, browserName, // This will be the detailed browser info string
113
+ testCase) {
114
+ var _a, _b, _c, _d, _e;
113
115
  let stepStatus = "passed";
114
116
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
115
117
  if ((_c = (_b = step.error) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.startsWith("Test is skipped:")) {
@@ -122,7 +124,8 @@ class PlaywrightPulseReporter {
122
124
  const startTime = new Date(step.startTime);
123
125
  const endTime = new Date(startTime.getTime() + Math.max(0, duration));
124
126
  let codeLocation = "";
125
- if (step.location) {
127
+ if ((_d = step.location) === null || _d === void 0 ? void 0 : _d.file) {
128
+ // Check if file path exists
126
129
  codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
127
130
  }
128
131
  let stepTitle = step.title;
@@ -133,9 +136,9 @@ class PlaywrightPulseReporter {
133
136
  duration: duration,
134
137
  startTime: startTime,
135
138
  endTime: endTime,
136
- browser: browserName,
139
+ browser: browserName, // Store the detailed browser string for the step
137
140
  errorMessage: errorMessage,
138
- stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
141
+ stackTrace: ((_e = step.error) === null || _e === void 0 ? void 0 : _e.stack) || undefined,
139
142
  codeLocation: codeLocation || undefined,
140
143
  isHook: step.category === "hook",
141
144
  hookType: step.category === "hook"
@@ -143,120 +146,103 @@ class PlaywrightPulseReporter {
143
146
  ? "before"
144
147
  : "after"
145
148
  : undefined,
146
- steps: [],
149
+ steps: [], // Will be populated by recursive calls in onTestEnd
147
150
  };
148
151
  }
149
152
  getBrowserInfo(test) {
150
- var _a, _b, _c, _d, _e, _f, _g, _h;
151
- // Changed return type to string
153
+ var _a, _b, _c, _d;
152
154
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
153
- const configuredBrowserType = (_c = (_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) === null || _c === void 0 ? void 0 : _c.toLowerCase(); // e.g., "chromium", "firefox", "webkit"
155
+ const configuredBrowserType = (_c = (_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) === null || _c === void 0 ? void 0 : _c.toLowerCase();
154
156
  const userAgentString = (_d = project === null || project === void 0 ? void 0 : project.use) === null || _d === void 0 ? void 0 : _d.userAgent;
155
- let finalBrowserName = configuredBrowserType || "unknown"; // Start with the configured type or "unknown"
156
- let version = "";
157
- let osName = "";
158
- let osVersion = "";
159
- let deviceType = "";
157
+ // --- DEBUG LOGS (IMPORTANT! Check these in your console output) ---
158
+ console.log(`[PulseReporter DEBUG] Project: ${(project === null || project === void 0 ? void 0 : project.name) || "N/A"}`);
159
+ console.log(`[PulseReporter DEBUG] Configured Browser Type: "${configuredBrowserType}"`);
160
+ console.log(`[PulseReporter DEBUG] User Agent String for UAParser: "${userAgentString}"`);
161
+ // --- END DEBUG LOGS ---
162
+ let parsedBrowserName;
163
+ let parsedVersion;
164
+ let parsedOsName;
165
+ let parsedOsVersion;
166
+ let deviceModel;
167
+ let deviceType;
160
168
  if (userAgentString) {
161
169
  try {
162
170
  const parser = new ua_parser_js_1.UAParser(userAgentString);
163
171
  const uaResult = parser.getResult();
164
- deviceType = uaResult.device.type || ""; // e.g., "mobile", "tablet", "console", "smarttv"
165
- // 1. Try UAParser's browser name
166
- if (uaResult.browser.name) {
167
- finalBrowserName = uaResult.browser.name;
168
- if (uaResult.browser.version) {
169
- // Get major version, or full version if no dot
170
- version = ` v${uaResult.browser.version.split(".")[0]}`;
171
- }
172
- }
173
- // If UAParser didn't find a browser name, but we have an engine,
174
- // it might be more informative than just the configuredBrowserType.
175
- else if (uaResult.engine.name &&
176
- configuredBrowserType !== uaResult.engine.name.toLowerCase()) {
177
- // Prefer engine name if it's more specific and different from default (e.g. WebKit for a generic device)
178
- finalBrowserName = uaResult.engine.name;
179
- }
180
- // 2. Specific Overrides / Refinements
181
- // Handling for mobile devices, especially if UAParser provides generic browser names
172
+ // --- DEBUG LOGS (IMPORTANT! Check these in your console output) ---
173
+ console.log("[PulseReporter DEBUG] UAParser Result:", JSON.stringify(uaResult, null, 2));
174
+ // --- END DEBUG LOGS ---
175
+ parsedBrowserName = uaResult.browser.name;
176
+ parsedVersion = uaResult.browser.version;
177
+ parsedOsName = uaResult.os.name;
178
+ parsedOsVersion = uaResult.os.version;
179
+ deviceModel = uaResult.device.model;
180
+ deviceType = uaResult.device.type;
182
181
  if (deviceType === "mobile" || deviceType === "tablet") {
183
- if (((_e = uaResult.os.name) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes("ios")) ||
184
- uaResult.browser.name === "Mobile Safari") {
185
- finalBrowserName = "Mobile Safari";
186
- }
187
- else if ((_f = uaResult.os.name) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes("android")) {
188
- if ((_g = uaResult.browser.name) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes("chrome")) {
189
- finalBrowserName = "Chrome Mobile";
182
+ if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("android")) {
183
+ if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("chrome")) {
184
+ parsedBrowserName = "Chrome Mobile";
190
185
  }
191
- else if ((_h = uaResult.browser.name) === null || _h === void 0 ? void 0 : _h.toLowerCase().includes("firefox")) {
192
- finalBrowserName = "Firefox Mobile";
186
+ else if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("firefox")) {
187
+ parsedBrowserName = "Firefox Mobile";
193
188
  }
194
- else if (uaResult.engine.name === "Blink" &&
195
- !uaResult.browser.name) {
196
- // Generic Android Webview
197
- finalBrowserName = "Android WebView";
189
+ else if (uaResult.engine.name === "Blink" && !parsedBrowserName) {
190
+ parsedBrowserName = "Android WebView";
198
191
  }
199
- else if (uaResult.browser.name) {
200
- finalBrowserName = `${uaResult.browser.name} Mobile`; // Generic, e.g., "Opera Mobile"
192
+ else if (parsedBrowserName) {
193
+ // Parsed name is likely okay
201
194
  }
202
195
  else {
203
- finalBrowserName = "Android Browser"; // Fallback for Android
196
+ parsedBrowserName = "Android Browser";
204
197
  }
205
198
  }
206
- }
207
- else if (uaResult.browser.name === "Electron") {
208
- finalBrowserName = "Electron App"; // More descriptive for Electron
209
- // For Electron, version might be app's version, not Chromium's.
210
- // You might need custom logic if you want the underlying Chromium version.
211
- }
212
- // 3. OS Information
213
- if (uaResult.os.name) {
214
- osName = ` on ${uaResult.os.name}`;
215
- if (uaResult.os.version) {
216
- osVersion = ` ${uaResult.os.version.split(".")[0]}`; // Major OS version
199
+ else if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("ios")) {
200
+ parsedBrowserName = "Mobile Safari";
217
201
  }
218
202
  }
203
+ else if (parsedBrowserName === "Electron") {
204
+ parsedBrowserName = "Electron App";
205
+ }
219
206
  }
220
207
  catch (error) {
221
208
  console.warn(`Pulse Reporter: Error parsing User-Agent string "${userAgentString}":`, error);
222
- // Fallback to configuredBrowserType already set in finalBrowserName
223
209
  }
224
210
  }
225
- // If after UA parsing, we still have a generic engine name like "Blink" or "WebKit"
226
- // and a more specific configuredBrowserType exists (like "chromium"), prefer the configured one.
227
- if ((finalBrowserName.toLowerCase() === "blink" ||
228
- finalBrowserName.toLowerCase() === "webkit" ||
229
- finalBrowserName.toLowerCase() === "gecko") &&
230
- configuredBrowserType &&
231
- configuredBrowserType !== "unknown") {
232
- finalBrowserName =
211
+ let finalDisplayName;
212
+ if (parsedBrowserName) {
213
+ finalDisplayName = parsedBrowserName;
214
+ if (parsedVersion) {
215
+ finalDisplayName += ` v${parsedVersion.split(".")[0]}`;
216
+ }
217
+ }
218
+ else if (configuredBrowserType && configuredBrowserType !== "unknown") {
219
+ finalDisplayName =
233
220
  configuredBrowserType.charAt(0).toUpperCase() +
234
- configuredBrowserType.slice(1); // Capitalize
235
- }
236
- // Construct the display string
237
- // Prioritize showing device type for mobile/tablet if it adds clarity
238
- let displayString = finalBrowserName;
239
- if (version)
240
- displayString += version;
241
- // Add device type if it's mobile/tablet and not already obvious from browser name
242
- if ((deviceType === "mobile" || deviceType === "tablet") &&
243
- !finalBrowserName.toLowerCase().includes(deviceType)) {
244
- // displayString += ` (${deviceType.charAt(0).toUpperCase() + deviceType.slice(1)})`;
245
- }
246
- if (osName)
247
- displayString += osName;
248
- if (osVersion && osName)
249
- displayString += osVersion; // Only add osVersion if osName is present
250
- return displayString.trim();
221
+ configuredBrowserType.slice(1);
222
+ }
223
+ else {
224
+ finalDisplayName = "Unknown Browser";
225
+ }
226
+ if (parsedOsName) {
227
+ finalDisplayName += ` on ${parsedOsName}`;
228
+ if (parsedOsVersion) {
229
+ finalDisplayName += ` ${parsedOsVersion.split(".")[0]}`;
230
+ }
231
+ }
232
+ // Example: Append device model if it's a mobile/tablet and model exists
233
+ // if ((deviceType === "mobile" || deviceType === "tablet") && deviceModel && !finalDisplayName.includes(deviceModel)) {
234
+ // finalDisplayName += ` (${deviceModel})`;
235
+ // }
236
+ return finalDisplayName.trim();
251
237
  }
252
238
  async onTestEnd(test, result) {
253
- var _a, _b, _c, _d, _e, _f, _g, _h;
239
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
254
240
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
255
- const browserName = this.getBrowserInfo(test);
241
+ const browserDisplayInfo = this.getBrowserInfo(test);
256
242
  const testStatus = convertStatus(result.status, test);
257
243
  const startTime = new Date(result.startTime);
258
244
  const endTime = new Date(startTime.getTime() + result.duration);
259
- const testIdForFiles = test.id ||
245
+ const testIdForFiles = test.id || // Playwright's internal unique ID for the test case
260
246
  `${test
261
247
  .titlePath()
262
248
  .join("_")
@@ -264,60 +250,54 @@ class PlaywrightPulseReporter {
264
250
  const processAllSteps = async (steps) => {
265
251
  let processed = [];
266
252
  for (const step of steps) {
267
- const processedStep = await this.processStep(step, testIdForFiles, browserName, test);
253
+ const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo, // Pass the detailed browser info string
254
+ test);
268
255
  processed.push(processedStep);
269
256
  if (step.steps && step.steps.length > 0) {
270
- processedStep.steps = await processAllSteps(step.steps);
257
+ processedStep.steps = await processAllSteps(step.steps); // Recursive call
271
258
  }
272
259
  }
273
260
  return processed;
274
261
  };
275
262
  let codeSnippet = undefined;
276
263
  try {
277
- 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)) {
264
+ if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
265
+ ((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) !== undefined &&
266
+ ((_d = test.location) === null || _d === void 0 ? void 0 : _d.column) !== undefined) {
278
267
  const relativePath = path.relative(this.config.rootDir, test.location.file);
279
268
  codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
280
269
  }
281
270
  }
282
271
  catch (e) {
283
- console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
272
+ // console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
284
273
  }
285
- const stdoutMessages = [];
286
- if (result.stdout && result.stdout.length > 0) {
287
- result.stdout.forEach((item) => {
288
- stdoutMessages.push(typeof item === "string" ? item : item.toString());
289
- });
290
- }
291
- const stderrMessages = [];
292
- if (result.stderr && result.stderr.length > 0) {
293
- result.stderr.forEach((item) => {
294
- stderrMessages.push(typeof item === "string" ? item : item.toString());
295
- });
296
- }
297
- const uniqueTestId = test.id;
274
+ const stdoutMessages = ((_e = result.stdout) === null || _e === void 0 ? void 0 : _e.map((item) => typeof item === "string" ? item : item.toString())) || [];
275
+ const stderrMessages = ((_f = result.stderr) === null || _f === void 0 ? void 0 : _f.map((item) => typeof item === "string" ? item : item.toString())) || [];
276
+ const uniqueTestId = test.id; // test.id is Playwright's unique ID for a test case instance
298
277
  const pulseResult = {
299
278
  id: uniqueTestId,
300
- runId: "TBD",
279
+ runId: "TBD", // Will be set during final report generation
301
280
  name: test.titlePath().join(" > "),
302
- 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",
281
+ suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_g = this.config.projects[0]) === null || _g === void 0 ? void 0 : _g.name) || "Default Suite",
303
282
  status: testStatus,
304
283
  duration: result.duration,
305
284
  startTime: startTime,
306
285
  endTime: endTime,
307
- browser: browserName,
286
+ browser: browserDisplayInfo, // Use the detailed browser string
308
287
  retries: result.retry,
309
- steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
310
- errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
311
- stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
288
+ steps: ((_h = result.steps) === null || _h === void 0 ? void 0 : _h.length) ? await processAllSteps(result.steps) : [],
289
+ errorMessage: (_j = result.error) === null || _j === void 0 ? void 0 : _j.message,
290
+ stackTrace: (_k = result.error) === null || _k === void 0 ? void 0 : _k.stack,
312
291
  codeSnippet: codeSnippet,
313
292
  tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
314
- screenshots: [],
315
- videoPath: undefined,
316
- tracePath: undefined,
293
+ screenshots: [], // To be populated by attachFiles
294
+ videoPath: undefined, // To be populated by attachFiles
295
+ tracePath: undefined, // To be populated by attachFiles
317
296
  stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
318
297
  stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
319
298
  };
320
299
  try {
300
+ // IMPORTANT: attachFiles logic
321
301
  (0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
322
302
  }
323
303
  catch (attachError) {
@@ -352,27 +332,28 @@ class PlaywrightPulseReporter {
352
332
  console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
353
333
  }
354
334
  }
355
- async _mergeShardResults(finalRunData) {
335
+ async _mergeShardResults(finalRunData // Pass the TestRun object to populate
336
+ ) {
337
+ var _a, _b;
356
338
  let allShardProcessedResults = [];
357
- const totalShards = this.config.shard ? this.config.shard.total : 1;
339
+ const totalShards = (_b = (_a = this.config.shard) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 1;
358
340
  for (let i = 0; i < totalShards; i++) {
359
341
  const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
360
342
  try {
361
343
  const content = await fs.readFile(tempFilePath, "utf-8");
362
- const shardResults = JSON.parse(content);
363
- allShardProcessedResults =
364
- allShardProcessedResults.concat(shardResults);
344
+ const shardResults = JSON.parse(content); // Dates are already ISO strings
345
+ allShardProcessedResults.push(...shardResults);
365
346
  }
366
347
  catch (error) {
367
348
  if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
368
- console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}. This might be normal if a shard had no tests or failed early.`);
349
+ // console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}.`);
369
350
  }
370
351
  else {
371
352
  console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
372
353
  }
373
354
  }
374
355
  }
375
- let finalUniqueResultsMap = new Map();
356
+ const finalUniqueResultsMap = new Map();
376
357
  for (const result of allShardProcessedResults) {
377
358
  const existing = finalUniqueResultsMap.get(result.id);
378
359
  if (!existing || result.retries >= existing.retries) {
@@ -380,23 +361,15 @@ class PlaywrightPulseReporter {
380
361
  }
381
362
  }
382
363
  const finalResultsList = Array.from(finalUniqueResultsMap.values());
383
- finalResultsList.forEach((r) => (r.runId = finalRunData.id));
364
+ finalResultsList.forEach((r) => (r.runId = finalRunData.id)); // Assign runId to each test result
365
+ // Update the passed finalRunData object with aggregated stats
384
366
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
385
367
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
386
368
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
387
369
  finalRunData.totalTests = finalResultsList.length;
388
- const reviveDates = (key, value) => {
389
- const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
390
- if (typeof value === "string" && isoDateRegex.test(value)) {
391
- const date = new Date(value);
392
- return !isNaN(date.getTime()) ? date : value;
393
- }
394
- return value;
395
- };
396
- const properlyTypedResults = JSON.parse(JSON.stringify(finalResultsList), reviveDates);
397
370
  return {
398
- run: finalRunData,
399
- results: properlyTypedResults,
371
+ run: finalRunData, // Contains Date object for timestamp
372
+ results: finalResultsList, // Contains ISO strings for dates from shards
400
373
  metadata: { generatedAt: new Date().toISOString() },
401
374
  };
402
375
  }
@@ -410,7 +383,7 @@ class PlaywrightPulseReporter {
410
383
  }
411
384
  catch (error) {
412
385
  if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
413
- console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
386
+ // console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
414
387
  }
415
388
  }
416
389
  }
@@ -426,7 +399,7 @@ class PlaywrightPulseReporter {
426
399
  }
427
400
  }
428
401
  async onEnd(result) {
429
- var _a, _b, _c;
402
+ var _a, _b;
430
403
  if (this.shardIndex !== undefined) {
431
404
  await this._writeShardResults();
432
405
  return;
@@ -435,16 +408,18 @@ class PlaywrightPulseReporter {
435
408
  const duration = runEndTime - this.runStartTime;
436
409
  const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
437
410
  const runData = {
411
+ // This is the single source of truth for current run's data
438
412
  id: runId,
439
- timestamp: new Date(this.runStartTime),
413
+ timestamp: new Date(this.runStartTime), // Stored as Date object
440
414
  totalTests: 0,
441
415
  passed: 0,
442
416
  failed: 0,
443
417
  skipped: 0,
444
418
  duration,
445
419
  };
446
- let finalReport = undefined; // Initialize as undefined
420
+ let finalReport;
447
421
  if (this.isSharded) {
422
+ // _mergeShardResults will populate the runData object passed to it
448
423
  finalReport = await this._mergeShardResults(runData);
449
424
  }
450
425
  else {
@@ -453,37 +428,18 @@ class PlaywrightPulseReporter {
453
428
  runData.failed = this.results.filter((r) => r.status === "failed").length;
454
429
  runData.skipped = this.results.filter((r) => r.status === "skipped").length;
455
430
  runData.totalTests = this.results.length;
456
- const reviveDates = (key, value) => {
457
- const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
458
- if (typeof value === "string" && isoDateRegex.test(value)) {
459
- const date = new Date(value);
460
- return !isNaN(date.getTime()) ? date : value;
461
- }
462
- return value;
463
- };
464
- const properlyTypedResults = JSON.parse(JSON.stringify(this.results), reviveDates);
465
431
  finalReport = {
466
- run: runData,
467
- results: properlyTypedResults,
432
+ run: runData, // runData contains a Date object for timestamp
433
+ results: this.results, // results contain Date objects for startTime, endTime
468
434
  metadata: { generatedAt: new Date().toISOString() },
469
435
  };
470
436
  }
471
- if (!finalReport) {
472
- console.error("PlaywrightPulseReporter: CRITICAL - finalReport object was not generated. Cannot create summary.");
473
- const errorSummary = `
474
- PlaywrightPulseReporter: Run Finished
475
- -----------------------------------------
476
- Overall Status: ERROR (Report data missing)
477
- Total Tests: N/A
478
- Passed: N/A
479
- Failed: N/A
480
- Skipped: N/A
481
- Duration: N/A
482
- -----------------------------------------`;
483
- if (this.printsToStdio()) {
484
- console.log(errorSummary);
485
- }
486
- const errorReport = {
437
+ // This check should be robust now
438
+ if (!finalReport ||
439
+ !finalReport.run ||
440
+ typeof finalReport.run.totalTests !== "number") {
441
+ console.error("PlaywrightPulseReporter: CRITICAL - finalReport object or its run data was malformed. Cannot create summary.");
442
+ const errorReportMinimal = {
487
443
  run: {
488
444
  id: runId,
489
445
  timestamp: new Date(this.runStartTime),
@@ -491,28 +447,30 @@ PlaywrightPulseReporter: Run Finished
491
447
  passed: 0,
492
448
  failed: 0,
493
449
  skipped: 0,
494
- duration: duration,
450
+ duration,
495
451
  },
496
452
  results: [],
497
453
  metadata: {
498
454
  generatedAt: new Date().toISOString(),
499
455
  },
500
456
  };
501
- const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
502
457
  try {
458
+ const errorPath = path.join(this.outputDir, this.baseOutputFile);
503
459
  await this._ensureDirExists(this.outputDir);
504
- await fs.writeFile(finalOutputPathOnError, JSON.stringify(errorReport, null, 2));
505
- console.warn(`PlaywrightPulseReporter: Wrote an error report to ${finalOutputPathOnError} as finalReport was missing.`);
460
+ // Stringify with Date conversion for the minimal error report
461
+ await fs.writeFile(errorPath, JSON.stringify(errorReportMinimal, (key, value) => value instanceof Date ? value.toISOString() : value, 2));
462
+ console.warn(`PlaywrightPulseReporter: Wrote a minimal error report to ${errorPath}.`);
506
463
  }
507
- catch (writeError) {
508
- console.error(`PlaywrightPulseReporter: Failed to write error report: ${writeError.message}`);
464
+ catch (e) {
465
+ console.error("PlaywrightPulseReporter: Failed to write minimal error report.", e);
509
466
  }
510
467
  return;
511
468
  }
469
+ // At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
512
470
  const reportRunData = finalReport.run;
513
- const finalRunStatus = ((_a = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
471
+ const finalRunStatus = ((_a = reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
514
472
  ? "failed"
515
- : ((_b = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
473
+ : ((_b = reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
516
474
  ? result.status === "interrupted"
517
475
  ? "interrupted"
518
476
  : "no tests or error"
@@ -521,11 +479,11 @@ PlaywrightPulseReporter: Run Finished
521
479
  PlaywrightPulseReporter: Run Finished
522
480
  -----------------------------------------
523
481
  Overall Status: ${finalRunStatus.toUpperCase()}
524
- Total Tests: ${(reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) || 0}
525
- Passed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.passed}
526
- Failed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed}
527
- Skipped: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.skipped}
528
- Duration: ${(((_c = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.duration) !== null && _c !== void 0 ? _c : 0) / 1000).toFixed(2)}s
482
+ Total Tests: ${reportRunData.totalTests}
483
+ Passed: ${reportRunData.passed}
484
+ Failed: ${reportRunData.failed}
485
+ Skipped: ${reportRunData.skipped}
486
+ Duration: ${(reportRunData.duration / 1000).toFixed(2)}s
529
487
  -----------------------------------------`;
530
488
  if (this.printsToStdio()) {
531
489
  console.log(summary);
@@ -533,11 +491,11 @@ PlaywrightPulseReporter: Run Finished
533
491
  const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
534
492
  try {
535
493
  await this._ensureDirExists(this.outputDir);
494
+ // Custom replacer for JSON.stringify to handle Date objects correctly
536
495
  await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
537
- if (value instanceof Date)
496
+ if (value instanceof Date) {
538
497
  return value.toISOString();
539
- if (typeof value === "bigint")
540
- return value.toString();
498
+ }
541
499
  return value;
542
500
  }, 2));
543
501
  if (this.printsToStdio()) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
3
  "author": "Arghajit Singha",
4
- "version": "0.1.0-beta-4",
4
+ "version": "0.1.0-beta-6",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "keywords": [
7
7
  "playwright",
@@ -62,6 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/node": "^20",
65
+ "@types/ua-parser-js": "^0.7.39",
65
66
  "eslint": "9.25.1",
66
67
  "typescript": "^5"
67
68
  },