@arghajit/dummy 0.1.2-beta-1 → 0.1.2-beta-3

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.
@@ -21,8 +21,13 @@ export declare class PlaywrightPulseReporter implements Reporter {
21
21
  private processStep;
22
22
  onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
23
23
  private _getBaseTestId;
24
- private _getFinalizedResults;
25
24
  private _getStatusOrder;
25
+ /**
26
+ * Refactored to group all run attempts for a single logical test case.
27
+ * @param allAttempts An array of all individual test run attempts.
28
+ * @returns An array of ConsolidatedTestResult objects, where each object represents one logical test and contains an array of all its runs.
29
+ */
30
+ private _getFinalizedResults;
26
31
  onError(error: any): void;
27
32
  private _getEnvDetails;
28
33
  private _writeShardResults;
@@ -40,14 +40,10 @@ const crypto_1 = require("crypto");
40
40
  const ua_parser_js_1 = require("ua-parser-js");
41
41
  const os = __importStar(require("os"));
42
42
  const convertStatus = (status, testCase, retryCount = 0) => {
43
- // If a test passes on a retry, it's considered flaky regardless of expected status.
44
- // This is the most critical check for flaky tests.
45
43
  if (status === "passed" && retryCount > 0) {
46
44
  return "flaky";
47
45
  }
48
- // Handle expected statuses for the final result.
49
46
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
50
- // If expected to fail but passed, it's flaky.
51
47
  if (status === "passed")
52
48
  return "flaky";
53
49
  return "failed";
@@ -55,7 +51,6 @@ const convertStatus = (status, testCase, retryCount = 0) => {
55
51
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "skipped") {
56
52
  return "skipped";
57
53
  }
58
- // Default Playwright status mapping
59
54
  switch (status) {
60
55
  case "passed":
61
56
  return "passed";
@@ -74,12 +69,12 @@ const INDIVIDUAL_REPORTS_SUBDIR = "pulse-results";
74
69
  class PlaywrightPulseReporter {
75
70
  constructor(options = {}) {
76
71
  var _a, _b, _c;
77
- // This will now store all individual run attempts for all tests.
72
+ // This will now store all individual run attempts for all tests using our new local type.
78
73
  this.results = [];
79
74
  this.baseOutputFile = "playwright-pulse-report.json";
80
75
  this.isSharded = false;
81
76
  this.shardIndex = undefined;
82
- this.currentRunId = ""; // Added to store the overall run ID
77
+ this.currentRunId = "";
83
78
  this.options = options;
84
79
  this.baseOutputFile = (_a = options.outputFile) !== null && _a !== void 0 ? _a : this.baseOutputFile;
85
80
  this.outputDir = (_b = options.outputDir) !== null && _b !== void 0 ? _b : "pulse-report";
@@ -94,7 +89,6 @@ class PlaywrightPulseReporter {
94
89
  this.config = config;
95
90
  this.suite = suite;
96
91
  this.runStartTime = Date.now();
97
- // Generate the overall runId once at the beginning
98
92
  this.currentRunId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
99
93
  const configDir = this.config.rootDir;
100
94
  const configFileDir = this.config.configFile
@@ -120,9 +114,7 @@ class PlaywrightPulseReporter {
120
114
  })
121
115
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
122
116
  }
123
- onTestBegin(test) {
124
- // console.log(`Starting test: ${test.title}`); // Removed for brevity in final output
125
- }
117
+ onTestBegin(test) { }
126
118
  getBrowserDetails(test) {
127
119
  var _a, _b, _c, _d;
128
120
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
@@ -173,8 +165,7 @@ class PlaywrightPulseReporter {
173
165
  }
174
166
  return finalString.trim();
175
167
  }
176
- async processStep(step, testId, browserDetails, testCase, retryCount = 0 // Pass retryCount to convertStatus for steps
177
- ) {
168
+ async processStep(step, testId, browserDetails, testCase, retryCount = 0) {
178
169
  var _a, _b, _c, _d;
179
170
  let stepStatus = "passed";
180
171
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
@@ -182,7 +173,6 @@ class PlaywrightPulseReporter {
182
173
  stepStatus = "skipped";
183
174
  }
184
175
  else {
185
- // Use the extended convertStatus
186
176
  stepStatus = convertStatus(step.error ? "failed" : "passed", testCase, retryCount);
187
177
  }
188
178
  const duration = step.duration;
@@ -216,15 +206,13 @@ class PlaywrightPulseReporter {
216
206
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
217
207
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
218
208
  const browserDetails = this.getBrowserDetails(test);
219
- // Use the extended convertStatus, passing result.retry
220
209
  const testStatus = convertStatus(result.status, test, result.retry);
221
210
  const startTime = new Date(result.startTime);
222
211
  const endTime = new Date(startTime.getTime() + result.duration);
223
212
  const processAllSteps = async (steps) => {
224
213
  let processed = [];
225
214
  for (const step of steps) {
226
- const processedStep = await this.processStep(step, test.id, browserDetails, test, result.retry // Pass retryCount to processStep
227
- );
215
+ const processedStep = await this.processStep(step, test.id, browserDetails, test, result.retry);
228
216
  processed.push(processedStep);
229
217
  if (step.steps && step.steps.length > 0) {
230
218
  processedStep.steps = await processAllSteps(step.steps);
@@ -256,11 +244,11 @@ class PlaywrightPulseReporter {
256
244
  ? JSON.stringify(this.config.metadata)
257
245
  : undefined,
258
246
  };
259
- // Correctly handle the ID for each run. A unique ID per attempt is crucial.
247
+ // Correctly handle the ID for each run attempt.
260
248
  const testIdWithRunCounter = `${test.id}-run-${result.retry}`;
261
249
  const pulseResult = {
262
- id: testIdWithRunCounter, // Use the modified ID
263
- runId: this.currentRunId, // Assign the overall run ID
250
+ id: testIdWithRunCounter,
251
+ runId: this.currentRunId,
264
252
  name: test.titlePath().join(" > "),
265
253
  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",
266
254
  status: testStatus,
@@ -268,8 +256,8 @@ class PlaywrightPulseReporter {
268
256
  startTime: startTime,
269
257
  endTime: endTime,
270
258
  browser: browserDetails,
271
- retries: result.retry, // This is the Playwright retry count (0 for first run, 1 for first retry, etc.)
272
- runCounter: result.retry, // This is your 'runCounter'
259
+ retries: result.retry,
260
+ runCounter: result.retry,
273
261
  steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
274
262
  errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
275
263
  stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
@@ -288,7 +276,6 @@ class PlaywrightPulseReporter {
288
276
  if (!attachment.path)
289
277
  continue;
290
278
  try {
291
- // Use the new testIdWithRunCounter for the subfolder
292
279
  const testSubfolder = testIdWithRunCounter.replace(/[^a-zA-Z0-9_-]/g, "_");
293
280
  const safeAttachmentName = path
294
281
  .basename(attachment.path)
@@ -326,49 +313,6 @@ class PlaywrightPulseReporter {
326
313
  const parts = testResultId.split("-run-");
327
314
  return parts[0];
328
315
  }
329
- _getFinalizedResults(allResults) {
330
- const finalResultsMap = new Map();
331
- const allRunsMap = new Map();
332
- // First, group all run attempts by their base test ID
333
- for (const result of allResults) {
334
- const baseTestId = this._getBaseTestId(result.id);
335
- if (!allRunsMap.has(baseTestId)) {
336
- allRunsMap.set(baseTestId, []);
337
- }
338
- allRunsMap.get(baseTestId).push(result);
339
- }
340
- // Now, iterate through the grouped runs to determine the final state
341
- for (const [baseTestId, runs] of allRunsMap.entries()) {
342
- let finalResult = undefined;
343
- // Sort runs to process them in chronological order
344
- runs.sort((a, b) => a.runCounter - b.runCounter);
345
- for (const currentRun of runs) {
346
- if (!finalResult) {
347
- finalResult = currentRun;
348
- }
349
- else {
350
- // Compare the current run to the best result found so far
351
- const currentStatusOrder = this._getStatusOrder(currentRun.status);
352
- const finalStatusOrder = this._getStatusOrder(finalResult.status);
353
- if (currentStatusOrder < finalStatusOrder) {
354
- // Current run is "better" (e.g., passed over failed)
355
- finalResult = currentRun;
356
- }
357
- else if (currentStatusOrder === finalStatusOrder &&
358
- currentRun.retries > finalResult.retries) {
359
- // Same status, but prefer the latest attempt
360
- finalResult = currentRun;
361
- }
362
- }
363
- }
364
- if (finalResult) {
365
- // Ensure the ID of the final result is the base test ID for de-duplication
366
- finalResult.id = baseTestId;
367
- finalResultsMap.set(baseTestId, finalResult);
368
- }
369
- }
370
- return Array.from(finalResultsMap.values());
371
- }
372
316
  _getStatusOrder(status) {
373
317
  switch (status) {
374
318
  case "passed":
@@ -380,9 +324,47 @@ class PlaywrightPulseReporter {
380
324
  case "skipped":
381
325
  return 4;
382
326
  default:
383
- return 99; // Unknown status
327
+ return 99;
384
328
  }
385
329
  }
330
+ /**
331
+ * Refactored to group all run attempts for a single logical test case.
332
+ * @param allAttempts An array of all individual test run attempts.
333
+ * @returns An array of ConsolidatedTestResult objects, where each object represents one logical test and contains an array of all its runs.
334
+ */
335
+ _getFinalizedResults(allAttempts) {
336
+ const groupedResults = new Map();
337
+ for (const attempt of allAttempts) {
338
+ const baseTestId = this._getBaseTestId(attempt.id);
339
+ if (!groupedResults.has(baseTestId)) {
340
+ groupedResults.set(baseTestId, []);
341
+ }
342
+ groupedResults.get(baseTestId).push(attempt);
343
+ }
344
+ const finalResults = [];
345
+ for (const [baseId, runs] of groupedResults.entries()) {
346
+ // Sort runs to find the best status
347
+ runs.sort((a, b) => this._getStatusOrder(a.status) - this._getStatusOrder(b.status));
348
+ const bestRun = runs[0];
349
+ // Calculate total duration from the earliest start to the latest end time of all runs
350
+ const startTimes = runs.map((run) => run.startTime.getTime());
351
+ const endTimes = runs.map((run) => run.endTime.getTime());
352
+ const overallDuration = Math.max(...endTimes) - Math.min(...startTimes);
353
+ finalResults.push({
354
+ id: baseId,
355
+ name: bestRun.name,
356
+ suiteName: bestRun.suiteName,
357
+ status: bestRun.status,
358
+ duration: overallDuration,
359
+ startTime: new Date(Math.min(...startTimes)),
360
+ endTime: new Date(Math.max(...endTimes)),
361
+ browser: bestRun.browser,
362
+ tags: bestRun.tags,
363
+ runs: runs.sort((a, b) => a.runCounter - b.runCounter), // Sort runs chronologically for the report
364
+ });
365
+ }
366
+ return finalResults;
367
+ }
386
368
  onError(error) {
387
369
  var _a;
388
370
  console.error(`PlaywrightPulseReporter: Error encountered (Shard: ${(_a = this.shardIndex) !== null && _a !== void 0 ? _a : "Main"}):`, (error === null || error === void 0 ? void 0 : error.message) || error);
@@ -417,7 +399,7 @@ class PlaywrightPulseReporter {
417
399
  }
418
400
  }
419
401
  async _mergeShardResults(finalRunData) {
420
- let allShardRawResults = []; // Store raw results before final de-duplication
402
+ let allShardRawResults = [];
421
403
  const totalShards = this.config.shard ? this.config.shard.total : 1;
422
404
  for (let i = 0; i < totalShards; i++) {
423
405
  const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
@@ -435,13 +417,10 @@ class PlaywrightPulseReporter {
435
417
  }
436
418
  }
437
419
  }
438
- // Apply _getFinalizedResults after all raw shard results are collected
439
420
  const finalResultsList = this._getFinalizedResults(allShardRawResults);
440
- finalResultsList.forEach((r) => (r.runId = finalRunData.id));
441
421
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
442
422
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
443
423
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
444
- // Add flaky count
445
424
  finalRunData.flaky = finalResultsList.filter((r) => r.status === "flaky").length;
446
425
  finalRunData.totalTests = finalResultsList.length;
447
426
  const reviveDates = (key, value) => {
@@ -489,12 +468,9 @@ class PlaywrightPulseReporter {
489
468
  await this._writeShardResults();
490
469
  return;
491
470
  }
492
- // Now, `this.results` contains all individual run attempts.
493
- // _getFinalizedResults will select the "best" run for each logical test.
494
471
  const finalResults = this._getFinalizedResults(this.results);
495
472
  const runEndTime = Date.now();
496
473
  const duration = runEndTime - this.runStartTime;
497
- // Use the stored overall runId
498
474
  const runId = this.currentRunId;
499
475
  const environmentDetails = this._getEnvDetails();
500
476
  const runData = {
@@ -504,21 +480,18 @@ class PlaywrightPulseReporter {
504
480
  passed: finalResults.filter((r) => r.status === "passed").length,
505
481
  failed: finalResults.filter((r) => r.status === "failed").length,
506
482
  skipped: finalResults.filter((r) => r.status === "skipped").length,
507
- flaky: finalResults.filter((r) => r.status === "flaky").length, // Add flaky count
483
+ flaky: finalResults.filter((r) => r.status === "flaky").length,
508
484
  duration,
509
485
  environment: environmentDetails,
510
486
  };
511
- // Ensure all final results have the correct overall runId
512
- finalResults.forEach((r) => (r.runId = runId));
513
487
  let finalReport = undefined;
514
488
  if (this.isSharded) {
515
- // _mergeShardResults will now perform the final de-duplication across shards
516
489
  finalReport = await this._mergeShardResults(runData);
517
490
  }
518
491
  else {
519
492
  finalReport = {
520
493
  run: runData,
521
- results: finalResults, // Use the de-duplicated results for a non-sharded run
494
+ results: finalResults, // Cast to any to bypass the type mismatch
522
495
  metadata: { generatedAt: new Date().toISOString() },
523
496
  };
524
497
  }
@@ -592,8 +565,6 @@ class PlaywrightPulseReporter {
592
565
  const allResultsFromAllFiles = [];
593
566
  let latestTimestamp = new Date(0);
594
567
  let lastRunEnvironment = undefined;
595
- // We can't simply sum durations across merged files, as the tests might overlap.
596
- // The final duration will be derived from the range of start/end times in the final results.
597
568
  let earliestStartTime = Date.now();
598
569
  let latestEndTime = 0;
599
570
  for (const file of reportFiles) {
@@ -601,24 +572,26 @@ class PlaywrightPulseReporter {
601
572
  try {
602
573
  const content = await fs.readFile(filePath, "utf-8");
603
574
  const json = JSON.parse(content);
604
- if (json.run) {
605
- const runTimestamp = new Date(json.run.timestamp);
606
- if (runTimestamp > latestTimestamp) {
607
- latestTimestamp = runTimestamp;
608
- lastRunEnvironment = json.run.environment || undefined;
609
- }
610
- }
575
+ // This is the tricky part. We need to handle both old and new report formats.
576
+ // Assuming the `results` array might contain the old, single-run objects or the new, consolidated ones.
611
577
  if (json.results) {
612
- allResultsFromAllFiles.push(...json.results);
578
+ json.results.forEach((testResult) => {
579
+ // Check if the TestResult has a 'runs' array (new format)
580
+ if ("runs" in testResult && Array.isArray(testResult.runs)) {
581
+ allResultsFromAllFiles.push(...testResult.runs);
582
+ }
583
+ else {
584
+ // This is the old format (single run). We'll treat it as a single attempt.
585
+ allResultsFromAllFiles.push(testResult); // Cast to any to get properties
586
+ }
587
+ });
613
588
  }
614
589
  }
615
590
  catch (err) {
616
591
  console.warn(`Pulse Reporter: Could not parse report file ${filePath}. Skipping. Error: ${err.message}`);
617
592
  }
618
593
  }
619
- // De-duplicate the results from ALL merged files using the helper function
620
594
  const finalMergedResults = this._getFinalizedResults(allResultsFromAllFiles);
621
- // Calculate overall duration from the earliest start and latest end of the final merged results
622
595
  for (const res of finalMergedResults) {
623
596
  if (res.startTime.getTime() < earliestStartTime)
624
597
  earliestStartTime = res.startTime.getTime();
@@ -634,7 +607,7 @@ class PlaywrightPulseReporter {
634
607
  passed: finalMergedResults.filter((r) => r.status === "passed").length,
635
608
  failed: finalMergedResults.filter((r) => r.status === "failed").length,
636
609
  skipped: finalMergedResults.filter((r) => r.status === "skipped").length,
637
- flaky: finalMergedResults.filter((r) => r.status === "flaky").length, // Add flaky count
610
+ flaky: finalMergedResults.filter((r) => r.status === "flaky").length,
638
611
  duration: totalDuration,
639
612
  };
640
613
  const finalReport = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
- "version": "0.1.2-beta-1",
3
+ "version": "0.1.2-beta-3",
4
4
  "description": "A Playwright reporter and dashboard for visualizing test results.",
5
5
  "homepage": "https://playwright-pulse-report.netlify.app/",
6
6
  "keywords": [
@@ -50,7 +50,8 @@
50
50
  "report:merge": "node ./scripts/merge-pulse-report.js",
51
51
  "report:email": "node ./scripts/sendReport.mjs",
52
52
  "report:minify": "node ./scripts/generate-email-report.mjs",
53
- "generate-trend": "node ./scripts/generate-trend.mjs"
53
+ "generate-trend": "node ./scripts/generate-trend.mjs",
54
+ "deploy": "npm publish --access -public"
54
55
  },
55
56
  "dependencies": {
56
57
  "archiver": "^7.0.1",