@arghajit/dummy 0.1.2-beta-4 → 0.1.2

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.
@@ -20,14 +20,8 @@ export declare class PlaywrightPulseReporter implements Reporter {
20
20
  private getBrowserDetails;
21
21
  private processStep;
22
22
  onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
23
- private _getBaseTestId;
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
23
  private _getFinalizedResults;
24
+ private _getStatusOrder;
31
25
  onError(error: any): void;
32
26
  private _getEnvDetails;
33
27
  private _writeShardResults;
@@ -40,10 +40,8 @@ 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 (status === "passed" && retryCount > 0) {
44
- return "flaky";
45
- }
46
43
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
44
+ // If expected to fail but passed, it's flaky
47
45
  if (status === "passed")
48
46
  return "flaky";
49
47
  return "failed";
@@ -51,6 +49,10 @@ const convertStatus = (status, testCase, retryCount = 0) => {
51
49
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "skipped") {
52
50
  return "skipped";
53
51
  }
52
+ // If a test passes on a retry, it's considered flaky
53
+ if (status === "passed" && retryCount > 0) {
54
+ return "flaky";
55
+ }
54
56
  switch (status) {
55
57
  case "passed":
56
58
  return "passed";
@@ -69,12 +71,11 @@ const INDIVIDUAL_REPORTS_SUBDIR = "pulse-results";
69
71
  class PlaywrightPulseReporter {
70
72
  constructor(options = {}) {
71
73
  var _a, _b, _c;
72
- // This will now store all individual run attempts for all tests using our new local type.
73
74
  this.results = [];
74
75
  this.baseOutputFile = "playwright-pulse-report.json";
75
76
  this.isSharded = false;
76
77
  this.shardIndex = undefined;
77
- this.currentRunId = "";
78
+ this.currentRunId = ""; // Added to store the overall run ID
78
79
  this.options = options;
79
80
  this.baseOutputFile = (_a = options.outputFile) !== null && _a !== void 0 ? _a : this.baseOutputFile;
80
81
  this.outputDir = (_b = options.outputDir) !== null && _b !== void 0 ? _b : "pulse-report";
@@ -89,6 +90,7 @@ class PlaywrightPulseReporter {
89
90
  this.config = config;
90
91
  this.suite = suite;
91
92
  this.runStartTime = Date.now();
93
+ // Generate the overall runId once at the beginning
92
94
  this.currentRunId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
93
95
  const configDir = this.config.rootDir;
94
96
  const configFileDir = this.config.configFile
@@ -114,7 +116,9 @@ class PlaywrightPulseReporter {
114
116
  })
115
117
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
116
118
  }
117
- onTestBegin(test) { }
119
+ onTestBegin(test) {
120
+ // console.log(`Starting test: ${test.title}`); // Removed for brevity in final output
121
+ }
118
122
  getBrowserDetails(test) {
119
123
  var _a, _b, _c, _d;
120
124
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
@@ -165,7 +169,8 @@ class PlaywrightPulseReporter {
165
169
  }
166
170
  return finalString.trim();
167
171
  }
168
- async processStep(step, testId, browserDetails, testCase, retryCount = 0) {
172
+ async processStep(step, testId, browserDetails, testCase, retryCount = 0 // Pass retryCount to convertStatus for steps
173
+ ) {
169
174
  var _a, _b, _c, _d;
170
175
  let stepStatus = "passed";
171
176
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
@@ -173,6 +178,7 @@ class PlaywrightPulseReporter {
173
178
  stepStatus = "skipped";
174
179
  }
175
180
  else {
181
+ // Use the extended convertStatus
176
182
  stepStatus = convertStatus(step.error ? "failed" : "passed", testCase, retryCount);
177
183
  }
178
184
  const duration = step.duration;
@@ -206,13 +212,15 @@ class PlaywrightPulseReporter {
206
212
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
207
213
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
208
214
  const browserDetails = this.getBrowserDetails(test);
215
+ // Use the extended convertStatus, passing result.retry
209
216
  const testStatus = convertStatus(result.status, test, result.retry);
210
217
  const startTime = new Date(result.startTime);
211
218
  const endTime = new Date(startTime.getTime() + result.duration);
212
219
  const processAllSteps = async (steps) => {
213
220
  let processed = [];
214
221
  for (const step of steps) {
215
- const processedStep = await this.processStep(step, test.id, browserDetails, test, result.retry);
222
+ const processedStep = await this.processStep(step, test.id, browserDetails, test, result.retry // Pass retryCount to processStep
223
+ );
216
224
  processed.push(processedStep);
217
225
  if (step.steps && step.steps.length > 0) {
218
226
  processedStep.steps = await processAllSteps(step.steps);
@@ -244,11 +252,11 @@ class PlaywrightPulseReporter {
244
252
  ? JSON.stringify(this.config.metadata)
245
253
  : undefined,
246
254
  };
247
- // Correctly handle the ID for each run attempt.
248
- const testIdWithRunCounter = `${test.id}-run-${result.retry}`;
255
+ // Modify test.id for retries
256
+ const testIdWithRunCounter = result.retry > 0 ? `${test.id}-${result.retry}` : test.id;
249
257
  const pulseResult = {
250
- id: testIdWithRunCounter,
251
- runId: this.currentRunId,
258
+ id: testIdWithRunCounter, // Use the modified ID
259
+ runId: this.currentRunId, // Assign the overall run ID
252
260
  name: test.titlePath().join(" > "),
253
261
  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",
254
262
  status: testStatus,
@@ -256,8 +264,8 @@ class PlaywrightPulseReporter {
256
264
  startTime: startTime,
257
265
  endTime: endTime,
258
266
  browser: browserDetails,
259
- retries: result.retry,
260
- runCounter: result.retry,
267
+ retries: result.retry, // This remains the Playwright retry count (0 for first run, 1 for first retry, etc.)
268
+ runCounter: result.retry, // This is your 'runCounter'
261
269
  steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
262
270
  errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
263
271
  stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
@@ -276,6 +284,7 @@ class PlaywrightPulseReporter {
276
284
  if (!attachment.path)
277
285
  continue;
278
286
  try {
287
+ // Use the new testIdWithRunCounter for the subfolder
279
288
  const testSubfolder = testIdWithRunCounter.replace(/[^a-zA-Z0-9_-]/g, "_");
280
289
  const safeAttachmentName = path
281
290
  .basename(attachment.path)
@@ -308,10 +317,34 @@ class PlaywrightPulseReporter {
308
317
  }
309
318
  this.results.push(pulseResult);
310
319
  }
311
- // New method to extract the base test ID, ignoring the run-counter suffix
312
- _getBaseTestId(testResultId) {
313
- const parts = testResultId.split("-run-");
314
- return parts[0];
320
+ _getFinalizedResults(allResults) {
321
+ const finalResultsMap = new Map();
322
+ for (const result of allResults) {
323
+ // The key for de-duplication should now be the base test ID (without the run counter suffix)
324
+ // This ensures that all runs of a single logical test are considered together.
325
+ const baseTestId = result.id.split("-").slice(0, -1).join("-"); // Remove '-${runCounter}'
326
+ const existing = finalResultsMap.get(baseTestId);
327
+ // We want to keep the "most successful" run for the final report.
328
+ // Priority: passed > flaky > failed > skipped.
329
+ // If statuses are equal, prefer the one with higher retry count (latest attempt).
330
+ if (!existing) {
331
+ finalResultsMap.set(baseTestId, result);
332
+ }
333
+ else {
334
+ const currentStatusOrder = this._getStatusOrder(result.status);
335
+ const existingStatusOrder = this._getStatusOrder(existing.status);
336
+ if (currentStatusOrder < existingStatusOrder) {
337
+ // Current result is "better" (e.g., passed over failed)
338
+ finalResultsMap.set(baseTestId, result);
339
+ }
340
+ else if (currentStatusOrder === existingStatusOrder &&
341
+ result.retries > existing.retries) {
342
+ // Same status, but current is a later retry, so prefer it
343
+ finalResultsMap.set(baseTestId, result);
344
+ }
345
+ }
346
+ }
347
+ return Array.from(finalResultsMap.values());
315
348
  }
316
349
  _getStatusOrder(status) {
317
350
  switch (status) {
@@ -324,47 +357,9 @@ class PlaywrightPulseReporter {
324
357
  case "skipped":
325
358
  return 4;
326
359
  default:
327
- return 99;
360
+ return 99; // Unknown status
328
361
  }
329
362
  }
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
- }
368
363
  onError(error) {
369
364
  var _a;
370
365
  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);
@@ -399,7 +394,7 @@ class PlaywrightPulseReporter {
399
394
  }
400
395
  }
401
396
  async _mergeShardResults(finalRunData) {
402
- let allShardRawResults = [];
397
+ let allShardRawResults = []; // Store raw results before final de-duplication
403
398
  const totalShards = this.config.shard ? this.config.shard.total : 1;
404
399
  for (let i = 0; i < totalShards; i++) {
405
400
  const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
@@ -417,10 +412,13 @@ class PlaywrightPulseReporter {
417
412
  }
418
413
  }
419
414
  }
415
+ // Apply _getFinalizedResults after all raw shard results are collected
420
416
  const finalResultsList = this._getFinalizedResults(allShardRawResults);
417
+ finalResultsList.forEach((r) => (r.runId = finalRunData.id));
421
418
  finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
422
419
  finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
423
420
  finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
421
+ // Add flaky count
424
422
  finalRunData.flaky = finalResultsList.filter((r) => r.status === "flaky").length;
425
423
  finalRunData.totalTests = finalResultsList.length;
426
424
  const reviveDates = (key, value) => {
@@ -468,39 +466,36 @@ class PlaywrightPulseReporter {
468
466
  await this._writeShardResults();
469
467
  return;
470
468
  }
471
- let finalReport;
472
- // `this.results` contains all individual run attempts. We will keep them all.
473
- const allAttempts = this.results;
474
- // Use your existing logic ONLY to get the final statuses for the summary counts.
475
- const summaryResults = this._getFinalizedResults(this.results);
469
+ // `this.results` now contains all individual run attempts.
470
+ // _getFinalizedResults will select the "best" run for each logical test.
471
+ const finalResults = this._getFinalizedResults(this.results);
476
472
  const runEndTime = Date.now();
477
473
  const duration = runEndTime - this.runStartTime;
474
+ // Use the stored overall runId
478
475
  const runId = this.currentRunId;
479
476
  const environmentDetails = this._getEnvDetails();
480
477
  const runData = {
481
478
  id: runId,
482
479
  timestamp: new Date(this.runStartTime),
483
- totalTests: summaryResults.length,
484
- passed: summaryResults.filter((r) => r.status === "passed").length,
485
- failed: summaryResults.filter((r) => r.status === "failed").length,
486
- skipped: summaryResults.filter((r) => r.status === "skipped").length,
487
- flaky: summaryResults.filter((r) => r.status === "flaky").length,
480
+ totalTests: finalResults.length,
481
+ passed: finalResults.filter((r) => r.status === "passed").length,
482
+ failed: finalResults.filter((r) => r.status === "failed").length,
483
+ skipped: finalResults.filter((r) => r.status === "skipped").length,
484
+ flaky: finalResults.filter((r) => r.status === "flaky").length, // Add flaky count
488
485
  duration,
489
486
  environment: environmentDetails,
490
487
  };
491
- // In the final report object, use the complete list of attempts.
492
- finalReport = {
493
- run: runData,
494
- results: allAttempts, // <<< USE THE FULL LIST HERE
495
- metadata: { generatedAt: new Date().toISOString() },
496
- };
488
+ // Ensure all final results have the correct overall runId
489
+ finalResults.forEach((r) => (r.runId = runId));
490
+ let finalReport = undefined;
497
491
  if (this.isSharded) {
492
+ // _mergeShardResults will now perform the final de-duplication across shards
498
493
  finalReport = await this._mergeShardResults(runData);
499
494
  }
500
495
  else {
501
496
  finalReport = {
502
497
  run: runData,
503
- results: summaryResults, // Cast to any to bypass the type mismatch
498
+ results: finalResults, // Use the de-duplicated results for a non-sharded run
504
499
  metadata: { generatedAt: new Date().toISOString() },
505
500
  };
506
501
  }
@@ -574,6 +569,8 @@ class PlaywrightPulseReporter {
574
569
  const allResultsFromAllFiles = [];
575
570
  let latestTimestamp = new Date(0);
576
571
  let lastRunEnvironment = undefined;
572
+ // We can't simply sum durations across merged files, as the tests might overlap.
573
+ // The final duration will be derived from the range of start/end times in the final results.
577
574
  let earliestStartTime = Date.now();
578
575
  let latestEndTime = 0;
579
576
  for (const file of reportFiles) {
@@ -581,26 +578,24 @@ class PlaywrightPulseReporter {
581
578
  try {
582
579
  const content = await fs.readFile(filePath, "utf-8");
583
580
  const json = JSON.parse(content);
584
- // This is the tricky part. We need to handle both old and new report formats.
585
- // Assuming the `results` array might contain the old, single-run objects or the new, consolidated ones.
581
+ if (json.run) {
582
+ const runTimestamp = new Date(json.run.timestamp);
583
+ if (runTimestamp > latestTimestamp) {
584
+ latestTimestamp = runTimestamp;
585
+ lastRunEnvironment = json.run.environment || undefined;
586
+ }
587
+ }
586
588
  if (json.results) {
587
- json.results.forEach((testResult) => {
588
- // Check if the TestResult has a 'runs' array (new format)
589
- if ("runs" in testResult && Array.isArray(testResult.runs)) {
590
- allResultsFromAllFiles.push(...testResult.runs);
591
- }
592
- else {
593
- // This is the old format (single run). We'll treat it as a single attempt.
594
- allResultsFromAllFiles.push(testResult); // Cast to any to get properties
595
- }
596
- });
589
+ allResultsFromAllFiles.push(...json.results);
597
590
  }
598
591
  }
599
592
  catch (err) {
600
593
  console.warn(`Pulse Reporter: Could not parse report file ${filePath}. Skipping. Error: ${err.message}`);
601
594
  }
602
595
  }
596
+ // De-duplicate the results from ALL merged files using the helper function
603
597
  const finalMergedResults = this._getFinalizedResults(allResultsFromAllFiles);
598
+ // Calculate overall duration from the earliest start and latest end of the final merged results
604
599
  for (const res of finalMergedResults) {
605
600
  if (res.startTime.getTime() < earliestStartTime)
606
601
  earliestStartTime = res.startTime.getTime();
@@ -616,7 +611,7 @@ class PlaywrightPulseReporter {
616
611
  passed: finalMergedResults.filter((r) => r.status === "passed").length,
617
612
  failed: finalMergedResults.filter((r) => r.status === "failed").length,
618
613
  skipped: finalMergedResults.filter((r) => r.status === "skipped").length,
619
- flaky: finalMergedResults.filter((r) => r.status === "flaky").length,
614
+ flaky: finalMergedResults.filter((r) => r.status === "flaky").length, // Add flaky count
620
615
  duration: totalDuration,
621
616
  };
622
617
  const finalReport = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
- "version": "0.1.2-beta-4",
3
+ "version": "0.1.2",
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,8 +50,7 @@
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",
54
- "deploy": "npm publish --access -public"
53
+ "generate-trend": "node ./scripts/generate-trend.mjs"
55
54
  },
56
55
  "dependencies": {
57
56
  "archiver": "^7.0.1",