@arghajit/dummy 0.1.0-beta-6 → 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,103 +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, _d;
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 = (_d = project === null || project === void 0 ? void 0 : project.use) === null || _d === void 0 ? void 0 : _d.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);
231
+ const browserDetails = this.getBrowserDetails(test);
242
232
  const testStatus = convertStatus(result.status, test);
243
233
  const startTime = new Date(result.startTime);
244
234
  const endTime = new Date(startTime.getTime() + result.duration);
245
- const testIdForFiles = test.id || // Playwright's internal unique ID for the test case
235
+ const testIdForFiles = test.id ||
246
236
  `${test
247
237
  .titlePath()
248
238
  .join("_")
@@ -250,54 +240,60 @@ class PlaywrightPulseReporter {
250
240
  const processAllSteps = async (steps) => {
251
241
  let processed = [];
252
242
  for (const step of steps) {
253
- const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo, // Pass the detailed browser info string
254
- test);
243
+ const processedStep = await this.processStep(step, testIdForFiles, browserDetails, test);
255
244
  processed.push(processedStep);
256
245
  if (step.steps && step.steps.length > 0) {
257
- processedStep.steps = await processAllSteps(step.steps); // Recursive call
246
+ processedStep.steps = await processAllSteps(step.steps);
258
247
  }
259
248
  }
260
249
  return processed;
261
250
  };
262
251
  let codeSnippet = undefined;
263
252
  try {
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) {
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)) {
267
254
  const relativePath = path.relative(this.config.rootDir, test.location.file);
268
255
  codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
269
256
  }
270
257
  }
271
258
  catch (e) {
272
- // 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);
273
260
  }
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
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
+ });
266
+ }
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;
277
274
  const pulseResult = {
278
275
  id: uniqueTestId,
279
- runId: "TBD", // Will be set during final report generation
276
+ runId: "TBD",
280
277
  name: test.titlePath().join(" > "),
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",
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",
282
279
  status: testStatus,
283
280
  duration: result.duration,
284
281
  startTime: startTime,
285
282
  endTime: endTime,
286
- browser: browserDisplayInfo, // Use the detailed browser string
283
+ browser: browserDetails,
287
284
  retries: result.retry,
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,
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,
291
288
  codeSnippet: codeSnippet,
292
289
  tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
293
- screenshots: [], // To be populated by attachFiles
294
- videoPath: undefined, // To be populated by attachFiles
295
- tracePath: undefined, // To be populated by attachFiles
290
+ screenshots: [],
291
+ videoPath: undefined,
292
+ tracePath: undefined,
296
293
  stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
297
294
  stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
298
295
  };
299
296
  try {
300
- // IMPORTANT: attachFiles logic
301
297
  (0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
302
298
  }
303
299
  catch (attachError) {
@@ -332,28 +328,27 @@ class PlaywrightPulseReporter {
332
328
  console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
333
329
  }
334
330
  }
335
- async _mergeShardResults(finalRunData // Pass the TestRun object to populate
336
- ) {
337
- var _a, _b;
331
+ async _mergeShardResults(finalRunData) {
338
332
  let allShardProcessedResults = [];
339
- 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;
340
334
  for (let i = 0; i < totalShards; i++) {
341
335
  const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
342
336
  try {
343
337
  const content = await fs.readFile(tempFilePath, "utf-8");
344
- const shardResults = JSON.parse(content); // Dates are already ISO strings
345
- allShardProcessedResults.push(...shardResults);
338
+ const shardResults = JSON.parse(content);
339
+ allShardProcessedResults =
340
+ allShardProcessedResults.concat(shardResults);
346
341
  }
347
342
  catch (error) {
348
343
  if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
349
- // 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.`);
350
345
  }
351
346
  else {
352
347
  console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
353
348
  }
354
349
  }
355
350
  }
356
- const finalUniqueResultsMap = new Map();
351
+ let finalUniqueResultsMap = new Map();
357
352
  for (const result of allShardProcessedResults) {
358
353
  const existing = finalUniqueResultsMap.get(result.id);
359
354
  if (!existing || result.retries >= existing.retries) {
@@ -361,15 +356,23 @@ class PlaywrightPulseReporter {
361
356
  }
362
357
  }
363
358
  const finalResultsList = Array.from(finalUniqueResultsMap.values());
364
- finalResultsList.forEach((r) => (r.runId = finalRunData.id)); // Assign runId to each test result
365
- // Update the passed finalRunData object with aggregated stats
359
+ finalResultsList.forEach((r) => (r.runId = finalRunData.id));
366
360
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
367
361
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
368
362
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
369
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);
370
373
  return {
371
- run: finalRunData, // Contains Date object for timestamp
372
- results: finalResultsList, // Contains ISO strings for dates from shards
374
+ run: finalRunData,
375
+ results: properlyTypedResults,
373
376
  metadata: { generatedAt: new Date().toISOString() },
374
377
  };
375
378
  }
@@ -383,7 +386,7 @@ class PlaywrightPulseReporter {
383
386
  }
384
387
  catch (error) {
385
388
  if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
386
- // 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);
387
390
  }
388
391
  }
389
392
  }
@@ -399,7 +402,7 @@ class PlaywrightPulseReporter {
399
402
  }
400
403
  }
401
404
  async onEnd(result) {
402
- var _a, _b;
405
+ var _a, _b, _c;
403
406
  if (this.shardIndex !== undefined) {
404
407
  await this._writeShardResults();
405
408
  return;
@@ -408,18 +411,16 @@ class PlaywrightPulseReporter {
408
411
  const duration = runEndTime - this.runStartTime;
409
412
  const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
410
413
  const runData = {
411
- // This is the single source of truth for current run's data
412
414
  id: runId,
413
- timestamp: new Date(this.runStartTime), // Stored as Date object
415
+ timestamp: new Date(this.runStartTime),
414
416
  totalTests: 0,
415
417
  passed: 0,
416
418
  failed: 0,
417
419
  skipped: 0,
418
420
  duration,
419
421
  };
420
- let finalReport;
422
+ let finalReport = undefined; // Initialize as undefined
421
423
  if (this.isSharded) {
422
- // _mergeShardResults will populate the runData object passed to it
423
424
  finalReport = await this._mergeShardResults(runData);
424
425
  }
425
426
  else {
@@ -428,18 +429,37 @@ class PlaywrightPulseReporter {
428
429
  runData.failed = this.results.filter((r) => r.status === "failed").length;
429
430
  runData.skipped = this.results.filter((r) => r.status === "skipped").length;
430
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);
431
441
  finalReport = {
432
- run: runData, // runData contains a Date object for timestamp
433
- results: this.results, // results contain Date objects for startTime, endTime
442
+ run: runData,
443
+ results: properlyTypedResults,
434
444
  metadata: { generatedAt: new Date().toISOString() },
435
445
  };
436
446
  }
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 = {
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 = {
443
463
  run: {
444
464
  id: runId,
445
465
  timestamp: new Date(this.runStartTime),
@@ -447,30 +467,28 @@ class PlaywrightPulseReporter {
447
467
  passed: 0,
448
468
  failed: 0,
449
469
  skipped: 0,
450
- duration,
470
+ duration: duration,
451
471
  },
452
472
  results: [],
453
473
  metadata: {
454
474
  generatedAt: new Date().toISOString(),
455
475
  },
456
476
  };
477
+ const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
457
478
  try {
458
- const errorPath = path.join(this.outputDir, this.baseOutputFile);
459
479
  await this._ensureDirExists(this.outputDir);
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}.`);
480
+ await fs.writeFile(finalOutputPathOnError, JSON.stringify(errorReport, null, 2));
481
+ console.warn(`PlaywrightPulseReporter: Wrote an error report to ${finalOutputPathOnError} as finalReport was missing.`);
463
482
  }
464
- catch (e) {
465
- console.error("PlaywrightPulseReporter: Failed to write minimal error report.", e);
483
+ catch (writeError) {
484
+ console.error(`PlaywrightPulseReporter: Failed to write error report: ${writeError.message}`);
466
485
  }
467
486
  return;
468
487
  }
469
- // At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
470
488
  const reportRunData = finalReport.run;
471
- 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
472
490
  ? "failed"
473
- : ((_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"
474
492
  ? result.status === "interrupted"
475
493
  ? "interrupted"
476
494
  : "no tests or error"
@@ -479,11 +497,11 @@ class PlaywrightPulseReporter {
479
497
  PlaywrightPulseReporter: Run Finished
480
498
  -----------------------------------------
481
499
  Overall Status: ${finalRunStatus.toUpperCase()}
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
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
487
505
  -----------------------------------------`;
488
506
  if (this.printsToStdio()) {
489
507
  console.log(summary);
@@ -491,11 +509,11 @@ PlaywrightPulseReporter: Run Finished
491
509
  const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
492
510
  try {
493
511
  await this._ensureDirExists(this.outputDir);
494
- // Custom replacer for JSON.stringify to handle Date objects correctly
495
512
  await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
496
- if (value instanceof Date) {
513
+ if (value instanceof Date)
497
514
  return value.toISOString();
498
- }
515
+ if (typeof value === "bigint")
516
+ return value.toString();
499
517
  return value;
500
518
  }, 2));
501
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-6",
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": {