@arghajit/dummy 0.1.0-beta-7 → 0.1.0-beta-8

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.
@@ -15,8 +15,8 @@ export declare class PlaywrightPulseReporter implements Reporter {
15
15
  printsToStdio(): boolean;
16
16
  onBegin(config: FullConfig, suite: Suite): void;
17
17
  onTestBegin(test: TestCase): void;
18
+ private getBrowserDetails;
18
19
  private processStep;
19
- getBrowserInfo(test: TestCase): string;
20
20
  onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
21
21
  onError(error: any): void;
22
22
  private _writeShardResults;
@@ -39,7 +39,7 @@ const fs = __importStar(require("fs/promises"));
39
39
  const path = __importStar(require("path"));
40
40
  const crypto_1 = require("crypto");
41
41
  const attachment_utils_1 = require("./attachment-utils"); // Use relative path
42
- const ua_parser_js_1 = require("ua-parser-js");
42
+ const ua_parser_js_1 = require("ua-parser-js"); // Added UAParser import
43
43
  const convertStatus = (status, testCase) => {
44
44
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
45
45
  return "failed";
@@ -87,14 +87,13 @@ 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; // Ensure options has the resolved path
90
+ this.options.outputDir = this.outputDir;
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
98
97
  .then(() => {
99
98
  if (this.shardIndex === undefined || this.shardIndex === 0) {
100
99
  console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
@@ -107,11 +106,89 @@ class PlaywrightPulseReporter {
107
106
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
108
107
  }
109
108
  onTestBegin(test) {
110
- // Optional: console.log(`Starting test: ${test.titlePath().join(' > ')} for project ${test.parent?.project()?.name}`);
109
+ console.log(`Starting test: ${test.title}`);
111
110
  }
112
- async processStep(step, testId, browserName, // This will be the detailed browser info string
113
- testCase) {
114
- var _a, _b, _c, _d, _e;
111
+ getBrowserDetails(test) {
112
+ var _a, _b, _c, _d;
113
+ const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project(); // project() can return undefined if not in a project context
114
+ const projectConfig = project === null || project === void 0 ? void 0 : project.use; // This is where options like userAgent, defaultBrowserType are
115
+ const userAgent = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.userAgent;
116
+ const configuredBrowserType = (_b = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.defaultBrowserType) === null || _b === void 0 ? void 0 : _b.toLowerCase();
117
+ // --- DEBUG LOGS (Uncomment if needed for diagnosing) ---
118
+ // console.log(`[PulseReporter DEBUG] Project: ${test.info().project.name}`);
119
+ // console.log(`[PulseReporter DEBUG] Configured Browser Type: "${configuredBrowserType}"`);
120
+ // console.log(`[PulseReporter DEBUG] User Agent for UAParser: "${userAgent}"`);
121
+ // --- END DEBUG LOGS ---
122
+ // if (!userAgent) {
123
+ // // Fallback if no user agent is available
124
+ // return configuredBrowserType
125
+ // ? configuredBrowserType.charAt(0).toUpperCase() +
126
+ // configuredBrowserType.slice(1)
127
+ // : "Unknown Browser";
128
+ // }
129
+ // try {
130
+ const parser = new ua_parser_js_1.UAParser(userAgent);
131
+ const result = parser.getResult();
132
+ // --- DEBUG LOGS (Uncomment if needed for diagnosing) ---
133
+ // console.log("[PulseReporter DEBUG] UAParser Result:", JSON.stringify(result, null, 2));
134
+ // --- END DEBUG LOGS ---
135
+ let browserName = result.browser.name;
136
+ const browserVersion = result.browser.version
137
+ ? ` v${result.browser.version.split(".")[0]}`
138
+ : ""; // Major version
139
+ const osName = result.os.name ? ` on ${result.os.name}` : "";
140
+ const osVersion = result.os.version
141
+ ? ` ${result.os.version.split(".")[0]}`
142
+ : ""; // Major version
143
+ const deviceType = result.device.type; // "mobile", "tablet", etc.
144
+ // If UAParser couldn't determine browser name, fallback to configured type
145
+ if (!browserName) {
146
+ browserName = configuredBrowserType
147
+ ? configuredBrowserType.charAt(0).toUpperCase() +
148
+ configuredBrowserType.slice(1)
149
+ : "Unknown";
150
+ }
151
+ else {
152
+ // Specific refinements for mobile based on parsed OS and device type
153
+ if (deviceType === "mobile" || deviceType === "tablet") {
154
+ if ((_c = result.os.name) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes("android")) {
155
+ if (browserName.toLowerCase().includes("chrome"))
156
+ browserName = "Chrome Mobile";
157
+ else if (browserName.toLowerCase().includes("firefox"))
158
+ browserName = "Firefox Mobile";
159
+ else if (result.engine.name === "Blink" && !result.browser.name)
160
+ browserName = "Android WebView";
161
+ else if (browserName &&
162
+ !browserName.toLowerCase().includes("mobile")) {
163
+ // Keep it as is, e.g. "Samsung Browser" is specific enough
164
+ }
165
+ else {
166
+ browserName = "Android Browser"; // default for android if not specific
167
+ }
168
+ }
169
+ else if ((_d = result.os.name) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes("ios")) {
170
+ browserName = "Mobile Safari";
171
+ }
172
+ }
173
+ else if (browserName === "Electron") {
174
+ browserName = "Electron App";
175
+ }
176
+ }
177
+ let finalString = `${browserName}${browserVersion}${osName}${osVersion}`;
178
+ return finalString.trim() || "Unknown Browser";
179
+ // } catch (error) {
180
+ // console.warn(
181
+ // `Pulse Reporter: Error parsing User-Agent string "${userAgent}":`,
182
+ // error
183
+ // );
184
+ // return configuredBrowserType
185
+ // ? configuredBrowserType.charAt(0).toUpperCase() +
186
+ // configuredBrowserType.slice(1)
187
+ // : "Unknown Browser";
188
+ // }
189
+ }
190
+ async processStep(step, testId, browserDetails, testCase) {
191
+ var _a, _b, _c, _d;
115
192
  let stepStatus = "passed";
116
193
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
117
194
  if ((_c = (_b = step.error) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.startsWith("Test is skipped:")) {
@@ -124,8 +201,7 @@ class PlaywrightPulseReporter {
124
201
  const startTime = new Date(step.startTime);
125
202
  const endTime = new Date(startTime.getTime() + Math.max(0, duration));
126
203
  let codeLocation = "";
127
- if ((_d = step.location) === null || _d === void 0 ? void 0 : _d.file) {
128
- // Check if file path exists
204
+ if (step.location) {
129
205
  codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
130
206
  }
131
207
  let stepTitle = step.title;
@@ -136,9 +212,9 @@ class PlaywrightPulseReporter {
136
212
  duration: duration,
137
213
  startTime: startTime,
138
214
  endTime: endTime,
139
- browser: browserName, // Store the detailed browser string for the step
215
+ browser: browserDetails,
140
216
  errorMessage: errorMessage,
141
- stackTrace: ((_e = step.error) === null || _e === void 0 ? void 0 : _e.stack) || undefined,
217
+ stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
142
218
  codeLocation: codeLocation || undefined,
143
219
  isHook: step.category === "hook",
144
220
  hookType: step.category === "hook"
@@ -146,107 +222,17 @@ class PlaywrightPulseReporter {
146
222
  ? "before"
147
223
  : "after"
148
224
  : undefined,
149
- steps: [], // Will be populated by recursive calls in onTestEnd
225
+ steps: [],
150
226
  };
151
227
  }
152
- getBrowserInfo(test) {
153
- var _a, _b, _c;
154
- const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
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();
156
- const userAgentString = test.info().project.use.userAgent;
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;
168
- if (userAgentString) {
169
- try {
170
- const parser = new ua_parser_js_1.UAParser(userAgentString);
171
- const uaResult = parser.getResult();
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;
181
- if (deviceType === "mobile" || deviceType === "tablet") {
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";
185
- }
186
- else if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("firefox")) {
187
- parsedBrowserName = "Firefox Mobile";
188
- }
189
- else if (uaResult.engine.name === "Blink" && !parsedBrowserName) {
190
- parsedBrowserName = "Android WebView";
191
- }
192
- else if (parsedBrowserName) {
193
- // Parsed name is likely okay
194
- }
195
- else {
196
- parsedBrowserName = "Android Browser";
197
- }
198
- }
199
- else if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("ios")) {
200
- parsedBrowserName = "Mobile Safari";
201
- }
202
- }
203
- else if (parsedBrowserName === "Electron") {
204
- parsedBrowserName = "Electron App";
205
- }
206
- }
207
- catch (error) {
208
- console.warn(`Pulse Reporter: Error parsing User-Agent string "${userAgentString}":`, error);
209
- }
210
- }
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 =
220
- configuredBrowserType.charAt(0).toUpperCase() +
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();
237
- }
238
228
  async onTestEnd(test, result) {
239
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
229
+ var _a, _b, _c, _d, _e, _f, _g, _h;
240
230
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
241
- // const browserDisplayInfo = this.getBrowserInfo(test);
242
- const ua = test.info().project.use.userAgent;
243
- const parser = new ua_parser_js_1.UAParser(ua);
244
- const res = parser.getResult();
245
- const browserDisplayInfo = res.browser.name || "";
231
+ const browserDetails = this.getBrowserDetails(test);
246
232
  const testStatus = convertStatus(result.status, test);
247
233
  const startTime = new Date(result.startTime);
248
234
  const endTime = new Date(startTime.getTime() + result.duration);
249
- const testIdForFiles = test.id || // Playwright's internal unique ID for the test case
235
+ const testIdForFiles = test.id ||
250
236
  `${test
251
237
  .titlePath()
252
238
  .join("_")
@@ -254,54 +240,60 @@ class PlaywrightPulseReporter {
254
240
  const processAllSteps = async (steps) => {
255
241
  let processed = [];
256
242
  for (const step of steps) {
257
- const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo, // Pass the detailed browser info string
258
- test);
243
+ const processedStep = await this.processStep(step, testIdForFiles, browserDetails, test);
259
244
  processed.push(processedStep);
260
245
  if (step.steps && step.steps.length > 0) {
261
- processedStep.steps = await processAllSteps(step.steps); // Recursive call
246
+ processedStep.steps = await processAllSteps(step.steps);
262
247
  }
263
248
  }
264
249
  return processed;
265
250
  };
266
251
  let codeSnippet = undefined;
267
252
  try {
268
- if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
269
- ((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) !== undefined &&
270
- ((_d = test.location) === null || _d === void 0 ? void 0 : _d.column) !== undefined) {
253
+ 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)) {
271
254
  const relativePath = path.relative(this.config.rootDir, test.location.file);
272
255
  codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
273
256
  }
274
257
  }
275
258
  catch (e) {
276
- // console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
259
+ console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
260
+ }
261
+ const stdoutMessages = [];
262
+ if (result.stdout && result.stdout.length > 0) {
263
+ result.stdout.forEach((item) => {
264
+ stdoutMessages.push(typeof item === "string" ? item : item.toString());
265
+ });
277
266
  }
278
- const stdoutMessages = ((_e = result.stdout) === null || _e === void 0 ? void 0 : _e.map((item) => typeof item === "string" ? item : item.toString())) || [];
279
- const stderrMessages = ((_f = result.stderr) === null || _f === void 0 ? void 0 : _f.map((item) => typeof item === "string" ? item : item.toString())) || [];
280
- const uniqueTestId = test.id; // test.id is Playwright's unique ID for a test case instance
267
+ const stderrMessages = [];
268
+ if (result.stderr && result.stderr.length > 0) {
269
+ result.stderr.forEach((item) => {
270
+ stderrMessages.push(typeof item === "string" ? item : item.toString());
271
+ });
272
+ }
273
+ const uniqueTestId = test.id;
281
274
  const pulseResult = {
282
275
  id: uniqueTestId,
283
- runId: "TBD", // Will be set during final report generation
276
+ runId: "TBD",
284
277
  name: test.titlePath().join(" > "),
285
- 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",
278
+ 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",
286
279
  status: testStatus,
287
280
  duration: result.duration,
288
281
  startTime: startTime,
289
282
  endTime: endTime,
290
- browser: browserDisplayInfo, // Use the detailed browser string
283
+ browser: browserDetails,
291
284
  retries: result.retry,
292
- steps: ((_h = result.steps) === null || _h === void 0 ? void 0 : _h.length) ? await processAllSteps(result.steps) : [],
293
- errorMessage: (_j = result.error) === null || _j === void 0 ? void 0 : _j.message,
294
- stackTrace: (_k = result.error) === null || _k === void 0 ? void 0 : _k.stack,
285
+ steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
286
+ errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
287
+ stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
295
288
  codeSnippet: codeSnippet,
296
289
  tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
297
- screenshots: [], // To be populated by attachFiles
298
- videoPath: undefined, // To be populated by attachFiles
299
- tracePath: undefined, // To be populated by attachFiles
290
+ screenshots: [],
291
+ videoPath: undefined,
292
+ tracePath: undefined,
300
293
  stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
301
294
  stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
302
295
  };
303
296
  try {
304
- // IMPORTANT: attachFiles logic
305
297
  (0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
306
298
  }
307
299
  catch (attachError) {
@@ -336,28 +328,27 @@ class PlaywrightPulseReporter {
336
328
  console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
337
329
  }
338
330
  }
339
- async _mergeShardResults(finalRunData // Pass the TestRun object to populate
340
- ) {
341
- var _a, _b;
331
+ async _mergeShardResults(finalRunData) {
342
332
  let allShardProcessedResults = [];
343
- const totalShards = (_b = (_a = this.config.shard) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 1;
333
+ const totalShards = this.config.shard ? this.config.shard.total : 1;
344
334
  for (let i = 0; i < totalShards; i++) {
345
335
  const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
346
336
  try {
347
337
  const content = await fs.readFile(tempFilePath, "utf-8");
348
- const shardResults = JSON.parse(content); // Dates are already ISO strings
349
- allShardProcessedResults.push(...shardResults);
338
+ const shardResults = JSON.parse(content);
339
+ allShardProcessedResults =
340
+ allShardProcessedResults.concat(shardResults);
350
341
  }
351
342
  catch (error) {
352
343
  if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
353
- // console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}.`);
344
+ console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}. This might be normal if a shard had no tests or failed early.`);
354
345
  }
355
346
  else {
356
347
  console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
357
348
  }
358
349
  }
359
350
  }
360
- const finalUniqueResultsMap = new Map();
351
+ let finalUniqueResultsMap = new Map();
361
352
  for (const result of allShardProcessedResults) {
362
353
  const existing = finalUniqueResultsMap.get(result.id);
363
354
  if (!existing || result.retries >= existing.retries) {
@@ -365,15 +356,23 @@ class PlaywrightPulseReporter {
365
356
  }
366
357
  }
367
358
  const finalResultsList = Array.from(finalUniqueResultsMap.values());
368
- finalResultsList.forEach((r) => (r.runId = finalRunData.id)); // Assign runId to each test result
369
- // Update the passed finalRunData object with aggregated stats
359
+ finalResultsList.forEach((r) => (r.runId = finalRunData.id));
370
360
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
371
361
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
372
362
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
373
363
  finalRunData.totalTests = finalResultsList.length;
364
+ const reviveDates = (key, value) => {
365
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
366
+ if (typeof value === "string" && isoDateRegex.test(value)) {
367
+ const date = new Date(value);
368
+ return !isNaN(date.getTime()) ? date : value;
369
+ }
370
+ return value;
371
+ };
372
+ const properlyTypedResults = JSON.parse(JSON.stringify(finalResultsList), reviveDates);
374
373
  return {
375
- run: finalRunData, // Contains Date object for timestamp
376
- results: finalResultsList, // Contains ISO strings for dates from shards
374
+ run: finalRunData,
375
+ results: properlyTypedResults,
377
376
  metadata: { generatedAt: new Date().toISOString() },
378
377
  };
379
378
  }
@@ -387,7 +386,7 @@ class PlaywrightPulseReporter {
387
386
  }
388
387
  catch (error) {
389
388
  if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
390
- // console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
389
+ console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
391
390
  }
392
391
  }
393
392
  }
@@ -403,7 +402,7 @@ class PlaywrightPulseReporter {
403
402
  }
404
403
  }
405
404
  async onEnd(result) {
406
- var _a, _b;
405
+ var _a, _b, _c;
407
406
  if (this.shardIndex !== undefined) {
408
407
  await this._writeShardResults();
409
408
  return;
@@ -412,18 +411,16 @@ class PlaywrightPulseReporter {
412
411
  const duration = runEndTime - this.runStartTime;
413
412
  const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
414
413
  const runData = {
415
- // This is the single source of truth for current run's data
416
414
  id: runId,
417
- timestamp: new Date(this.runStartTime), // Stored as Date object
415
+ timestamp: new Date(this.runStartTime),
418
416
  totalTests: 0,
419
417
  passed: 0,
420
418
  failed: 0,
421
419
  skipped: 0,
422
420
  duration,
423
421
  };
424
- let finalReport;
422
+ let finalReport = undefined; // Initialize as undefined
425
423
  if (this.isSharded) {
426
- // _mergeShardResults will populate the runData object passed to it
427
424
  finalReport = await this._mergeShardResults(runData);
428
425
  }
429
426
  else {
@@ -432,18 +429,37 @@ class PlaywrightPulseReporter {
432
429
  runData.failed = this.results.filter((r) => r.status === "failed").length;
433
430
  runData.skipped = this.results.filter((r) => r.status === "skipped").length;
434
431
  runData.totalTests = this.results.length;
432
+ const reviveDates = (key, value) => {
433
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
434
+ if (typeof value === "string" && isoDateRegex.test(value)) {
435
+ const date = new Date(value);
436
+ return !isNaN(date.getTime()) ? date : value;
437
+ }
438
+ return value;
439
+ };
440
+ const properlyTypedResults = JSON.parse(JSON.stringify(this.results), reviveDates);
435
441
  finalReport = {
436
- run: runData, // runData contains a Date object for timestamp
437
- results: this.results, // results contain Date objects for startTime, endTime
442
+ run: runData,
443
+ results: properlyTypedResults,
438
444
  metadata: { generatedAt: new Date().toISOString() },
439
445
  };
440
446
  }
441
- // This check should be robust now
442
- if (!finalReport ||
443
- !finalReport.run ||
444
- typeof finalReport.run.totalTests !== "number") {
445
- console.error("PlaywrightPulseReporter: CRITICAL - finalReport object or its run data was malformed. Cannot create summary.");
446
- const errorReportMinimal = {
447
+ if (!finalReport) {
448
+ console.error("PlaywrightPulseReporter: CRITICAL - finalReport object was not generated. Cannot create summary.");
449
+ const errorSummary = `
450
+ PlaywrightPulseReporter: Run Finished
451
+ -----------------------------------------
452
+ Overall Status: ERROR (Report data missing)
453
+ Total Tests: N/A
454
+ Passed: N/A
455
+ Failed: N/A
456
+ Skipped: N/A
457
+ Duration: N/A
458
+ -----------------------------------------`;
459
+ if (this.printsToStdio()) {
460
+ console.log(errorSummary);
461
+ }
462
+ const errorReport = {
447
463
  run: {
448
464
  id: runId,
449
465
  timestamp: new Date(this.runStartTime),
@@ -451,30 +467,28 @@ class PlaywrightPulseReporter {
451
467
  passed: 0,
452
468
  failed: 0,
453
469
  skipped: 0,
454
- duration,
470
+ duration: duration,
455
471
  },
456
472
  results: [],
457
473
  metadata: {
458
474
  generatedAt: new Date().toISOString(),
459
475
  },
460
476
  };
477
+ const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
461
478
  try {
462
- const errorPath = path.join(this.outputDir, this.baseOutputFile);
463
479
  await this._ensureDirExists(this.outputDir);
464
- // Stringify with Date conversion for the minimal error report
465
- await fs.writeFile(errorPath, JSON.stringify(errorReportMinimal, (key, value) => value instanceof Date ? value.toISOString() : value, 2));
466
- console.warn(`PlaywrightPulseReporter: Wrote a minimal error report to ${errorPath}.`);
480
+ await fs.writeFile(finalOutputPathOnError, JSON.stringify(errorReport, null, 2));
481
+ console.warn(`PlaywrightPulseReporter: Wrote an error report to ${finalOutputPathOnError} as finalReport was missing.`);
467
482
  }
468
- catch (e) {
469
- console.error("PlaywrightPulseReporter: Failed to write minimal error report.", e);
483
+ catch (writeError) {
484
+ console.error(`PlaywrightPulseReporter: Failed to write error report: ${writeError.message}`);
470
485
  }
471
486
  return;
472
487
  }
473
- // At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
474
488
  const reportRunData = finalReport.run;
475
- const finalRunStatus = ((_a = reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
489
+ const finalRunStatus = ((_a = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
476
490
  ? "failed"
477
- : ((_b = reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
491
+ : ((_b = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
478
492
  ? result.status === "interrupted"
479
493
  ? "interrupted"
480
494
  : "no tests or error"
@@ -483,11 +497,11 @@ class PlaywrightPulseReporter {
483
497
  PlaywrightPulseReporter: Run Finished
484
498
  -----------------------------------------
485
499
  Overall Status: ${finalRunStatus.toUpperCase()}
486
- Total Tests: ${reportRunData.totalTests}
487
- Passed: ${reportRunData.passed}
488
- Failed: ${reportRunData.failed}
489
- Skipped: ${reportRunData.skipped}
490
- Duration: ${(reportRunData.duration / 1000).toFixed(2)}s
500
+ Total Tests: ${(reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) || 0}
501
+ Passed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.passed}
502
+ Failed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed}
503
+ Skipped: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.skipped}
504
+ Duration: ${(((_c = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.duration) !== null && _c !== void 0 ? _c : 0) / 1000).toFixed(2)}s
491
505
  -----------------------------------------`;
492
506
  if (this.printsToStdio()) {
493
507
  console.log(summary);
@@ -495,11 +509,11 @@ PlaywrightPulseReporter: Run Finished
495
509
  const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
496
510
  try {
497
511
  await this._ensureDirExists(this.outputDir);
498
- // Custom replacer for JSON.stringify to handle Date objects correctly
499
512
  await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
500
- if (value instanceof Date) {
513
+ if (value instanceof Date)
501
514
  return value.toISOString();
502
- }
515
+ if (typeof value === "bigint")
516
+ return value.toString();
503
517
  return value;
504
518
  }, 2));
505
519
  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-7",
4
+ "version": "0.1.0-beta-8",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "keywords": [
7
7
  "playwright",
@@ -45,25 +45,65 @@
45
45
  "report:email": "node ./scripts/sendReport.js"
46
46
  },
47
47
  "dependencies": {
48
+ "@genkit-ai/googleai": "^1.6.2",
49
+ "@genkit-ai/next": "^1.6.2",
50
+ "@hookform/resolvers": "^4.1.3",
51
+ "@radix-ui/react-accordion": "^1.2.3",
52
+ "@radix-ui/react-alert-dialog": "^1.1.6",
53
+ "@radix-ui/react-avatar": "^1.1.3",
54
+ "@radix-ui/react-checkbox": "^1.1.4",
55
+ "@radix-ui/react-dialog": "^1.1.6",
56
+ "@radix-ui/react-dropdown-menu": "^2.1.6",
57
+ "@radix-ui/react-label": "^2.1.2",
58
+ "@radix-ui/react-menubar": "^1.1.6",
59
+ "@radix-ui/react-popover": "^1.1.6",
60
+ "@radix-ui/react-progress": "^1.1.2",
61
+ "@radix-ui/react-radio-group": "^1.2.3",
62
+ "@radix-ui/react-scroll-area": "^1.2.3",
63
+ "@radix-ui/react-select": "^2.1.6",
64
+ "@radix-ui/react-separator": "^1.1.2",
65
+ "@radix-ui/react-slider": "^1.2.3",
66
+ "@radix-ui/react-slot": "^1.1.2",
67
+ "@radix-ui/react-switch": "^1.1.3",
68
+ "@radix-ui/react-tabs": "^1.1.3",
69
+ "@radix-ui/react-toast": "^1.2.6",
70
+ "@radix-ui/react-tooltip": "^1.1.8",
71
+ "@tanstack-query-firebase/react": "^1.0.5",
72
+ "@tanstack/react-query": "^5.66.0",
48
73
  "archiver": "^7.0.1",
49
74
  "class-variance-authority": "^0.7.1",
50
75
  "clsx": "^2.1.1",
51
76
  "d3": "^7.9.0",
52
77
  "date-fns": "^3.6.0",
53
78
  "dotenv": "^16.5.0",
79
+ "firebase": "^11.3.0",
80
+ "genkit": "^1.6.2",
54
81
  "highcharts": "^12.2.0",
55
82
  "jsdom": "^26.1.0",
56
83
  "lucide-react": "^0.475.0",
84
+ "next": "15.2.3",
57
85
  "nodemailer": "^7.0.3",
58
86
  "patch-package": "^8.0.0",
87
+ "react": "^18.3.1",
88
+ "react-day-picker": "^8.10.1",
89
+ "react-dom": "^18.3.1",
90
+ "react-hook-form": "^7.54.2",
59
91
  "recharts": "^2.15.1",
92
+ "tailwind-merge": "^3.0.1",
93
+ "tailwindcss-animate": "^1.0.7",
60
94
  "ua-parser-js": "^2.0.3",
61
95
  "zod": "^3.24.2"
62
96
  },
63
97
  "devDependencies": {
64
98
  "@types/node": "^20",
99
+ "@types/react": "^18",
100
+ "@types/react-dom": "^18",
65
101
  "@types/ua-parser-js": "^0.7.39",
66
102
  "eslint": "9.25.1",
103
+ "eslint-config-next": "15.3.1",
104
+ "genkit-cli": "^1.6.1",
105
+ "postcss": "^8",
106
+ "tailwindcss": "^3.4.1",
67
107
  "typescript": "^5"
68
108
  },
69
109
  "engines": {