@arghajit/dummy 0.3.28 → 0.3.31

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.
package/dist/pulse.js CHANGED
@@ -1,5 +1,8 @@
1
- import { test } from "@playwright/test";
2
- export const pulse = {
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pulse = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ exports.pulse = {
3
6
  /**
4
7
  * Sets the severity level for the current test.
5
8
  * * @param level - The severity level ('Minor' | 'Low' | 'Medium' | 'High' | 'Critical')
@@ -13,7 +16,7 @@ export const pulse = {
13
16
  // Default to "Medium" if an invalid string is passed
14
17
  const selectedLevel = validLevels.includes(level) ? level : "Medium";
15
18
  // Add the annotation to Playwright's test info
16
- test.info().annotations.push({
19
+ test_1.test.info().annotations.push({
17
20
  type: "pulse_severity",
18
21
  description: selectedLevel,
19
22
  });
@@ -362,7 +362,10 @@ class PlaywrightPulseReporter {
362
362
  attempts.sort((a, b) => a.retries - b.retries);
363
363
  const firstAttempt = attempts[0];
364
364
  const retryAttempts = attempts.slice(1);
365
- if (retryAttempts.length > 0) {
365
+ // Only populate retryHistory if there were actual failures that triggered retries
366
+ // If all attempts passed, we don't need to show retry history
367
+ const hasActualRetries = retryAttempts.length > 0 && retryAttempts.some(attempt => attempt.status === 'failed' || attempt.status === 'flaky' || firstAttempt.status === 'failed' || firstAttempt.status === 'flaky');
368
+ if (hasActualRetries) {
366
369
  firstAttempt.retryHistory = retryAttempts;
367
370
  // Calculate final status and outcome from the last attempt if retries exist
368
371
  const lastAttempt = attempts[attempts.length - 1];
@@ -374,8 +377,9 @@ class PlaywrightPulseReporter {
374
377
  }
375
378
  }
376
379
  else {
377
- // If no retries, ensure final_status is undefined (as requested)
380
+ // If no actual retries (all attempts passed), ensure final_status and retryHistory are removed
378
381
  delete firstAttempt.final_status;
382
+ delete firstAttempt.retryHistory;
379
383
  }
380
384
  finalResults.push(firstAttempt);
381
385
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
3
  "author": "Arghajit Singha",
4
- "version": "0.3.28",
4
+ "version": "0.3.31",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "homepage": "https://arghajit47.github.io/playwright-pulse/",
7
7
  "repository": {
@@ -160,6 +160,8 @@ function getStatusClass(status) {
160
160
  return "status-failed";
161
161
  case "skipped":
162
162
  return "status-skipped";
163
+ case "flaky":
164
+ return "status-flaky";
163
165
  default:
164
166
  return "status-unknown";
165
167
  }
@@ -172,6 +174,8 @@ function getStatusIcon(status) {
172
174
  return "❌";
173
175
  case "skipped":
174
176
  return "⏭️";
177
+ case "flaky":
178
+ return "⚠";
175
179
  default:
176
180
  return "❓";
177
181
  }
@@ -243,13 +247,16 @@ function generateMinifiedHTML(reportData) {
243
247
  )}; font-size: 0.8em; font-weight: 600; padding: 3px 8px; border-radius: 4px; color: #fff; margin-left: 10px; white-space: nowrap;">${severity}</span>`;
244
248
 
245
249
  // --- NEW: Retry Count Badge ---
246
- const retryCountBadge = (test.retryHistory && test.retryHistory.length > 0)
250
+ const unsuccessfulRetries = (test.retryHistory || []).filter(attempt =>
251
+ attempt.status === 'failed' || attempt.status === 'timedout' || attempt.status === 'flaky'
252
+ );
253
+ const retryCountBadge = (unsuccessfulRetries.length > 0)
247
254
  ? `<span style="background-color: #f59e0b; border: 1px solid #d97706; font-size: 0.8em; font-weight: 700; padding: 4px 10px; border-radius: 50px; color: #fff; margin-left: 10px; white-space: nowrap; display: inline-flex; align-items: center; gap: 4px;">
248
255
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle;">
249
256
  <path d="M1 4v6h6"/>
250
257
  <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
251
258
  </svg>
252
- Retry Count: ${test.retryHistory.length}
259
+ Retry Count: ${unsuccessfulRetries.length}
253
260
  </span>`
254
261
  : '';
255
262
 
@@ -310,6 +317,7 @@ function generateMinifiedHTML(reportData) {
310
317
  --light-gray-color: #ecf0f1; /* Light Grey */
311
318
  --medium-gray-color: #bdc3c7; /* Medium Grey */
312
319
  --dark-gray-color: #7f8c8d; /* Dark Grey */
320
+ --flaky-color: #00ccd3; /* Cyan/Teal for Flaky */
313
321
  --text-color: #34495e; /* Dark Grey/Blue for text */
314
322
  --background-color: #f8f9fa;
315
323
  --card-background-color: #ffffff;
@@ -400,6 +408,8 @@ function generateMinifiedHTML(reportData) {
400
408
  .stat-card.failed .value { color: var(--danger-color); }
401
409
  .stat-card.skipped { border-left-color: var(--warning-color); }
402
410
  .stat-card.skipped .value { color: var(--warning-color); }
411
+ .stat-card.flaky { border-left-color: var(--flaky-color); }
412
+ .stat-card.flaky .value { color: var(--flaky-color); }
403
413
 
404
414
  .section-title {
405
415
  font-size: 1.5em;
@@ -494,6 +504,7 @@ function generateMinifiedHTML(reportData) {
494
504
  .test-item.status-passed .test-status-label { background-color: var(--success-color); }
495
505
  .test-item.status-failed .test-status-label { background-color: var(--danger-color); }
496
506
  .test-item.status-skipped .test-status-label { background-color: var(--warning-color); }
507
+ .test-item.status-flaky .test-status-label { background-color: var(--flaky-color); }
497
508
  .test-item.status-unknown .test-status-label { background-color: var(--dark-gray-color); }
498
509
 
499
510
  .no-tests {
@@ -570,6 +581,10 @@ function generateMinifiedHTML(reportData) {
570
581
  <h3>Skipped</h3>
571
582
  <div class="value">${runSummary.skipped || 0}</div>
572
583
  </div>
584
+ <div class="stat-card flaky">
585
+ <h3>Flaky</h3>
586
+ <div class="value">${runSummary.flaky || 0}</div>
587
+ </div>
573
588
  </div>
574
589
  </section>
575
590
 
@@ -599,7 +614,7 @@ function generateMinifiedHTML(reportData) {
599
614
  <option value="passed">✅ Passed</option>
600
615
  <option value="failed">❌ Failed</option>
601
616
  <option value="skipped">⏭️ Skipped</option>
602
- <option value="unknown">❓ Unknown</option>
617
+ <option value="flaky">⚠ Flaky</option>
603
618
  </select>
604
619
  <select id="filter-min-browser">
605
620
  <option value="">All Browsers</option>
@@ -298,7 +298,7 @@ function generateTestTrendsChart(trendData) {
298
298
  {
299
299
  name: "Flaky",
300
300
  data: runs.map((r) => r.flaky || 0),
301
- color: "var(--neutral-500)",
301
+ color: "#00ccd3",
302
302
  marker: { symbol: "circle" },
303
303
  },
304
304
  ];
@@ -600,7 +600,7 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
600
600
  color = "var(--danger-color)";
601
601
  break;
602
602
  case "Flaky":
603
- color = "var(--neutral-500)";
603
+ color = "#00ccd3";
604
604
  break;
605
605
  case "Skipped":
606
606
  color = "var(--warning-color)";
@@ -1234,7 +1234,7 @@ function generateWorkerDistributionChart(results) {
1234
1234
  const seriesString = JSON.stringify([
1235
1235
  { name: "Passed", data: passedData, color: "var(--success-color)" },
1236
1236
  { name: "Failed", data: failedData, color: "var(--danger-color)" },
1237
- { name: "Flaky", data: flakyData, color: "var(--neutral-500)" },
1237
+ { name: "Flaky", data: flakyData, color: "#00ccd3" },
1238
1238
  { name: "Skipped", data: skippedData, color: "var(--warning-color)" },
1239
1239
  ]);
1240
1240
 
@@ -1327,7 +1327,7 @@ function generateWorkerDistributionChart(results) {
1327
1327
  if (test.status === 'passed') color = 'var(--success-color)';
1328
1328
  else if (test.status === 'failed') color = 'var(--danger-color)';
1329
1329
  else if (test.status === 'skipped') color = 'var(--warning-color)';
1330
- else if (test.status === 'flaky') color = 'var(--neutral-500)';
1330
+ else if (test.status === 'flaky') color = '#00ccd3';
1331
1331
 
1332
1332
  const escapedName = test.name.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
1333
1333
  testListHtml += \`<li style="color: \${color};"><span style="color: \${color}">[\${test.status.toUpperCase()}]</span> \${escapedName}</li>\`;
@@ -2120,7 +2120,7 @@ function generateSeverityDistributionChart(results) {
2120
2120
  const seriesData = [
2121
2121
  { name: "Passed", data: data.passed, color: "var(--success-color)" },
2122
2122
  { name: "Failed", data: data.failed, color: "var(--danger-color)" },
2123
- { name: "Flaky", data: data.flaky, color: "var(--neutral-500)" },
2123
+ { name: "Flaky", data: data.flaky, color: "#00ccd3" },
2124
2124
  { name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
2125
2125
  ];
2126
2126
 
@@ -2243,9 +2243,18 @@ function generateHTML(reportData, trendData = null) {
2243
2243
  const flakyCount = (results || []).filter(r => r.outcome === 'flaky').length;
2244
2244
 
2245
2245
  // Calculate retry statistics
2246
+ let retriedTestsCount = 0;
2246
2247
  const totalRetried = (results || []).reduce((acc, test) => {
2247
2248
  if (test.retryHistory && test.retryHistory.length > 0) {
2248
- return acc + test.retryHistory.length;
2249
+ // Filter out any "passed" or "skipped" entries in the history
2250
+ // We only count attempts that actually failed or timed out, triggering a retry.
2251
+ const unsuccessfulRetries = test.retryHistory.filter(attempt =>
2252
+ attempt.status === 'failed' || attempt.status === 'timedout' || attempt.status === 'flaky'
2253
+ );
2254
+ if (unsuccessfulRetries.length > 0) {
2255
+ retriedTestsCount++;
2256
+ }
2257
+ return acc + unsuccessfulRetries.length;
2249
2258
  }
2250
2259
  return acc;
2251
2260
  }, 0);
@@ -2331,8 +2340,8 @@ function generateHTML(reportData, trendData = null) {
2331
2340
  const severityBadge = `<span class="severity-badge" data-severity="${severity.toLowerCase()}">${severity}</span>`;
2332
2341
 
2333
2342
  // --- Retry Count Badge (only show if retries occurred) ---
2334
- const retryCount = test.retryHistory ? test.retryHistory.length : 0;
2335
- const retryBadge = retryCount > 0 ? `<span class="retry-badge">Retry Count: ${retryCount}</span>` : '';
2343
+ const retryCount = (test.retryHistory && test.retryHistory.length > 0) ? test.retryHistory.length : 0;
2344
+ const retryBadge = (test.retryHistory && test.retryHistory.length > 0) ? `<span class="retry-badge">Retry Count: ${retryCount}</span>` : '';
2336
2345
  const generateStepsHTML = (steps, depth = 0) => {
2337
2346
  if (!steps || steps.length === 0)
2338
2347
  return "<div class='no-steps'>No steps recorded for this test.</div>";
@@ -2428,6 +2437,7 @@ function generateHTML(reportData, trendData = null) {
2428
2437
  if(s === 'passed') colorVar = 'var(--success-color)';
2429
2438
  else if(s === 'failed') colorVar = 'var(--danger-color)';
2430
2439
  else if(s === 'skipped') colorVar = 'var(--warning-color)';
2440
+ else if(s === 'flaky') colorVar = '#00ccd3';
2431
2441
 
2432
2442
  return `<span style="
2433
2443
  display: inline-block;
@@ -2794,7 +2804,8 @@ function generateHTML(reportData, trendData = null) {
2794
2804
  --success-color: #10b981; --success-dark: #059669; --success-light: #34d399;
2795
2805
  --danger-color: #ef4444; --danger-dark: #dc2626; --danger-light: #f87171;
2796
2806
  --warning-color: #f59e0b; --warning-dark: #d97706; --warning-light: #fbbf24;
2797
- --info-color: #3b82f6;
2807
+ --info-color: #3b82f6;
2808
+ --flaky-color: #00ccd3;
2798
2809
  --neutral-50: #fafafa; --neutral-100: #f5f5f5; --neutral-200: #e5e5e5; --neutral-300: #d4d4d4;
2799
2810
  --neutral-400: #a3a3a3; --neutral-500: #737373; --neutral-600: #525252; --neutral-700: #404040;
2800
2811
  --neutral-800: #262626; --neutral-900: #171717;
@@ -3472,19 +3483,19 @@ function generateHTML(reportData, trendData = null) {
3472
3483
  box-shadow: 0 4px 12px rgba(239, 68, 68, 0.2);
3473
3484
  }
3474
3485
  .summary-card.status-failed .value { color: #ef4444; }
3475
- .summary-card.status-flaky::before { background: var(--neutral-500); }
3486
+ .summary-card.status-flaky::before { background: #00ccd3; }
3476
3487
  .summary-card.status-skipped { background: rgba(245, 158, 11, 0.02); }
3477
3488
  .summary-card.status-skipped:hover {
3478
3489
  background: rgba(245, 158, 11, 0.15);
3479
3490
  box-shadow: 0 4px 12px rgba(245, 158, 11, 0.2);
3480
3491
  }
3481
3492
  .summary-card.status-skipped .value { color: #f59e0b; }
3482
- .summary-card.flaky-status { background: rgba(156, 163, 175, 0.05); }
3493
+ .summary-card.flaky-status { background: rgba(0, 204, 211, 0.05); }
3483
3494
  .summary-card.flaky-status:hover {
3484
- background: rgba(156, 163, 175, 0.15);
3485
- box-shadow: 0 4px 12px rgba(156, 163, 175, 0.2);
3495
+ background: rgba(0, 204, 211, 0.15);
3496
+ box-shadow: 0 4px 12px rgba(0, 204, 211, 0.2);
3486
3497
  }
3487
- .summary-card.flaky-status .value { color: #64748b; }
3498
+ .summary-card.flaky-status .value { color: #00ccd3; }
3488
3499
  .summary-card:not([class*='status-']) .value { color: #0f172a; }
3489
3500
  .dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items: start; }
3490
3501
  .dashboard-column {
@@ -3584,7 +3595,7 @@ function generateHTML(reportData, trendData = null) {
3584
3595
  }
3585
3596
  .suite-card.status-passed::before { background: var(--success-color); }
3586
3597
  .suite-card.status-failed::before { background: var(--danger-color); }
3587
- .suite-card.status-flaky::before { background: var(--neutral-500); }
3598
+ .suite-card.status-flaky::before { background: #00ccd3; }
3588
3599
  .suite-card.status-skipped::before { background: var(--warning-color); }
3589
3600
 
3590
3601
  /* Outcome Badge */
@@ -3600,8 +3611,8 @@ function generateHTML(reportData, trendData = null) {
3600
3611
  letter-spacing: 0.5px;
3601
3612
  }
3602
3613
  .outcome-badge.flaky {
3603
- background-color: #eab308; /* Yellow-500 */
3604
- color: #000;
3614
+ background-color: #00ccd3;
3615
+ color: #fff;
3605
3616
  }
3606
3617
 
3607
3618
  .suite-card-header {
@@ -3630,9 +3641,9 @@ function generateHTML(reportData, trendData = null) {
3630
3641
  }
3631
3642
  .status-indicator-dot.status-passed { background-color: var(--success-color); box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.15); }
3632
3643
  .status-indicator-dot.status-failed { background-color: var(--danger-color); box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.15); }
3633
- .status-indicator-dot.status-flaky { background-color: var(--neutral-500); box-shadow: 0 0 0 4px rgba(107, 114, 128, 0.15); }
3644
+ .status-indicator-dot.status-flaky { background-color: #00ccd3; box-shadow: 0 0 0 4px rgba(0, 204, 211, 0.15); }
3634
3645
  .status-indicator-dot.status-skipped { background-color: rgba(245, 158, 11, 0.1); color: var(--warning-dark); border: 1px solid rgba(245, 158, 11, 0.2); }
3635
- .status-flaky { background-color: #C0C0C0; color: #000000; border: 1px solid #A0A0A0; }
3646
+ .status-flaky { background-color: rgba(0, 204, 211, 0.1); color: #00ccd3; border: 1px solid #00ccd3; }
3636
3647
 
3637
3648
  .browser-tag {
3638
3649
  font-size: 0.8em;
@@ -3684,7 +3695,7 @@ function generateHTML(reportData, trendData = null) {
3684
3695
  .stat-pill svg { width: 14px; height: 14px; }
3685
3696
  .stat-pill.passed { color: var(--success-dark); }
3686
3697
  .stat-pill.failed { color: var(--danger-dark); }
3687
- .stat-pill.flaky { color: #4b5563; }
3698
+ .stat-pill.flaky { color: #00ccd3; }
3688
3699
  .stat-pill.skipped { color: var(--warning-dark); }
3689
3700
  .filters {
3690
3701
  display: flex;
@@ -3849,7 +3860,7 @@ function generateHTML(reportData, trendData = null) {
3849
3860
  border-radius: 0;
3850
3861
  font-size: 0.7em;
3851
3862
  font-weight: 800;
3852
- color: white;
3863
+ color: black;
3853
3864
  text-transform: uppercase;
3854
3865
  min-width: 100px;
3855
3866
  text-align: center;
@@ -4560,13 +4571,14 @@ function generateHTML(reportData, trendData = null) {
4560
4571
  }</div><div class="trend-percentage">${skipPercentage}%</div></div>
4561
4572
  <div class="summary-card flaky-status"><h3>Flaky</h3><div class="value">${runSummary.flaky || 0}</div>
4562
4573
  <div class="trend-percentage">${flakyPercentage}%</div></div>
4563
- <div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
4564
- runSummary.duration,
4565
- )}</div></div>
4566
- <div class="summary-card">
4567
- <h3>Total Retry Count</h3>
4568
- <div class="value">${totalRetried}</div>
4569
- </div>
4574
+ <div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
4575
+ runSummary.duration,
4576
+ )}</div><div class="trend-percentage">Avg. Test Duration ${avgTestDuration}</div></div>
4577
+ <div class="summary-card">
4578
+ <h3>Total Retry Count</h3>
4579
+ <div class="value">${totalRetried}</div>
4580
+ <div class="trend-percentage">Test Retried ${retriedTestsCount}</div>
4581
+ </div>
4570
4582
  <div class="summary-card">
4571
4583
  <h3>🌐 Browser Distribution <span style="font-size: 0.7em; color: var(--text-color-secondary); font-weight: 400;">(${browserBreakdown.length} total)</span></h3>
4572
4584
  <div class="browser-breakdown" style="max-height: 200px; overflow-y: auto; padding-right: 4px;">
@@ -342,7 +342,7 @@ function generateTestTrendsChart(trendData) {
342
342
  {
343
343
  name: "Flaky",
344
344
  data: runs.map((r) => r.flaky || 0),
345
- color: "var(--neutral-500)",
345
+ color: "#00ccd3",
346
346
  marker: { symbol: "circle" },
347
347
  },
348
348
  ];
@@ -668,7 +668,7 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
668
668
  color = "var(--danger-color)";
669
669
  break;
670
670
  case "Flaky":
671
- color = "var(--neutral-500)";
671
+ color = "#00ccd3";
672
672
  break;
673
673
  case "Skipped":
674
674
  color = "var(--warning-color)";
@@ -1395,7 +1395,7 @@ function generateWorkerDistributionChart(results) {
1395
1395
  { name: "Passed", data: passedData, color: "var(--success-color)" },
1396
1396
  { name: "Failed", data: failedData, color: "var(--danger-color)" },
1397
1397
  { name: "Skipped", data: skippedData, color: "var(--warning-color)" },
1398
- { name: "Flaky", data: flakyData, color: "var(--neutral-500)" },
1398
+ { name: "Flaky", data: flakyData, color: "#00ccd3" },
1399
1399
  ]);
1400
1400
 
1401
1401
  // The HTML now includes the chart container, the modal, and styles for the modal
@@ -2315,7 +2315,7 @@ function generateSeverityDistributionChart(results) {
2315
2315
  const seriesData = [
2316
2316
  { name: "Passed", data: data.passed, color: "var(--success-color)" },
2317
2317
  { name: "Failed", data: data.failed, color: "var(--danger-color)" },
2318
- { name: "Flaky", data: data.flaky, color: "var(--neutral-500)" },
2318
+ { name: "Flaky", data: data.flaky, color: "#00ccd3" },
2319
2319
  { name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
2320
2320
  ];
2321
2321
 
@@ -2468,9 +2468,18 @@ function generateHTML(reportData, trendData = null) {
2468
2468
  const flakyCount = (results || []).filter(r => r.outcome === 'flaky').length;
2469
2469
 
2470
2470
  // Calculate retry statistics
2471
+ let retriedTestsCount = 0;
2471
2472
  const totalRetried = (results || []).reduce((acc, test) => {
2472
2473
  if (test.retryHistory && test.retryHistory.length > 0) {
2473
- return acc + test.retryHistory.length;
2474
+ // Filter out any "passed" or "skipped" entries in the history
2475
+ // We only count attempts that actually failed or timed out, triggering a retry.
2476
+ const unsuccessfulRetries = test.retryHistory.filter(attempt =>
2477
+ attempt.status === 'failed' || attempt.status === 'timedout' || attempt.status === 'flaky'
2478
+ );
2479
+ if (unsuccessfulRetries.length > 0) {
2480
+ retriedTestsCount++;
2481
+ }
2482
+ return acc + unsuccessfulRetries.length;
2474
2483
  }
2475
2484
  return acc;
2476
2485
  }, 0);
@@ -2570,8 +2579,8 @@ function generateHTML(reportData, trendData = null) {
2570
2579
  const severityBadge = `<span class="severity-badge" data-severity="${severity.toLowerCase()}">${severity}</span>`;
2571
2580
 
2572
2581
  // --- Retry Count Badge (only show if retries occurred) ---
2573
- const retryCount = test.retryHistory ? test.retryHistory.length : 0;
2574
- const retryBadge = retryCount > 0 ? `<span class="retry-badge">Retry Count: ${retryCount}</span>` : '';
2582
+ const retryCount = (test.retryHistory && test.retryHistory.length > 0) ? test.retryHistory.length : 0;
2583
+ const retryBadge = (test.retryHistory && test.retryHistory.length > 0) ? `<span class="retry-badge">Retry Count: ${retryCount}</span>` : '';
2575
2584
 
2576
2585
  // --- Step Generation ---
2577
2586
  const generateStepsHTML = (steps, depth = 0) => {
@@ -2649,6 +2658,7 @@ function generateHTML(reportData, trendData = null) {
2649
2658
  if(s === 'passed') colorVar = 'var(--success-color)';
2650
2659
  else if(s === 'failed') colorVar = 'var(--danger-color)';
2651
2660
  else if(s === 'skipped') colorVar = 'var(--warning-color)';
2661
+ else if(s === 'flaky') colorVar = '#00ccd3';
2652
2662
 
2653
2663
  return `<span style="
2654
2664
  display: inline-block;
@@ -3017,7 +3027,8 @@ function generateHTML(reportData, trendData = null) {
3017
3027
  --success-color: #34d399; --success-dark: #10b981; --success-light: #6ee7b7;
3018
3028
  --danger-color: #f87171; --danger-dark: #ef4444; --danger-light: #fca5a5;
3019
3029
  --warning-color: #fbbf24; --warning-dark: #f59e0b; --warning-light: #fcd34d;
3020
- --info-color: #9ca3af;
3030
+ --info-color: #9ca3af;
3031
+ --flaky-color: #00ccd3;
3021
3032
  --text-primary: #f9fafb; --text-secondary: #e5e7eb; --text-tertiary: #d1d5db;
3022
3033
  --bg-primary: #000000; --bg-secondary: #0a0a0a; --bg-tertiary: #050505;
3023
3034
  --bg-card: #0d0d0d; --bg-card-hover: #121212;
@@ -3471,14 +3482,14 @@ function generateHTML(reportData, trendData = null) {
3471
3482
  color: #f59e0b;
3472
3483
  }
3473
3484
  .summary-card.flaky-status {
3474
- background: rgba(156, 163, 175, 0.08);
3485
+ background: rgba(0, 204, 211, 0.05);
3475
3486
  }
3476
3487
  .summary-card.flaky-status:hover {
3477
- background: rgba(156, 163, 175, 0.15);
3478
- box-shadow: 0 4px 12px rgba(156, 163, 175, 0.2);
3488
+ background: rgba(0, 204, 211, 0.15);
3489
+ box-shadow: 0 4px 12px rgba(0, 204, 211, 0.2);
3479
3490
  }
3480
3491
  .summary-card.flaky-status .value {
3481
- color: #9ca3af;
3492
+ color: #00ccd3;
3482
3493
  }
3483
3494
  .summary-card:not([class*='status-']) .value {
3484
3495
  color: #f9fafb;
@@ -3602,7 +3613,7 @@ function generateHTML(reportData, trendData = null) {
3602
3613
  }
3603
3614
  .suite-card.status-passed::before { background: var(--success-color); }
3604
3615
  .suite-card.status-failed::before { background: var(--danger-color); }
3605
- .suite-card.status-flaky::before { background: var(--neutral-500); }
3616
+ .suite-card.status-flaky::before { background: #00ccd3; }
3606
3617
  .suite-card.status-skipped::before { background: var(--warning-color); }
3607
3618
 
3608
3619
  .suite-card.status-skipped::before { background: var(--warning-color); }
@@ -3620,8 +3631,8 @@ function generateHTML(reportData, trendData = null) {
3620
3631
  letter-spacing: 0.5px;
3621
3632
  }
3622
3633
  .outcome-badge.flaky {
3623
- background-color: #eab308; /* Yellow-500 */
3624
- color: #000;
3634
+ background-color: #00ccd3;
3635
+ color: #fff;
3625
3636
  }
3626
3637
 
3627
3638
  .suite-card-header {
@@ -3705,7 +3716,7 @@ function generateHTML(reportData, trendData = null) {
3705
3716
  .stat-pill svg { width: 14px; height: 14px; }
3706
3717
  .stat-pill.passed { color: var(--success-dark); }
3707
3718
  .stat-pill.failed { color: var(--danger-dark); }
3708
- .stat-pill.flaky { color: #4b5563; }
3719
+ .stat-pill.flaky { color: #00ccd3; }
3709
3720
  .stat-pill.skipped { color: var(--warning-dark); }
3710
3721
  color: #93c5fd;
3711
3722
  padding: 6px 12px;
@@ -3901,9 +3912,8 @@ function generateHTML(reportData, trendData = null) {
3901
3912
  background: var(--warning-color);
3902
3913
  }
3903
3914
  .status-badge.status-flaky {
3904
- background-color: #C0C0C0;
3905
- color: #000000;
3906
- border: 1px solid #A0A0A0;
3915
+ background-color: #00ccd3;
3916
+ color: #fff;
3907
3917
  }
3908
3918
  .status-badge.status-unknown {
3909
3919
  background: var(--dark-gray-color);
@@ -4539,8 +4549,8 @@ function generateHTML(reportData, trendData = null) {
4539
4549
  background-color: #f59e0b;
4540
4550
  }
4541
4551
  .status-badge-small.status-flaky {
4542
- background-color: #C0C0C0;
4543
- color: #000000;
4552
+ background-color: #00ccd3;
4553
+ color: #fff;
4544
4554
  }
4545
4555
  .status-badge-small.status-unknown {
4546
4556
  background-color: var(--dark-gray-color);
@@ -6246,10 +6256,11 @@ function generateHTML(reportData, trendData = null) {
6246
6256
  <div class="trend-percentage">${flakyPercentage}%</div></div>
6247
6257
  <div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
6248
6258
  runSummary.duration,
6249
- )}</div></div>
6259
+ )}</div><div class="trend-percentage">Avg. Test Duration ${avgTestDuration}</div></div>
6250
6260
  <div class="summary-card">
6251
6261
  <h3>Total Retry Count</h3>
6252
6262
  <div class="value">${totalRetried}</div>
6263
+ <div class="trend-percentage">Test Retried ${retriedTestsCount}</div>
6253
6264
  </div>
6254
6265
  <div class="summary-card">
6255
6266
  <h3>🌐 Browser Distribution <span style="font-size: 0.7em; color: var(--text-color-secondary); font-weight: 400;">(${browserBreakdown.length} total)</span></h3>
package/dist/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { PlaywrightPulseReporter } from "./playwright-pulse-reporter";
2
- export default PlaywrightPulseReporter;
3
- export { PlaywrightPulseReporter };
4
- export * from "../types";
5
- export * from "../lib/report-types";
package/dist/index.js DELETED
@@ -1,26 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.PlaywrightPulseReporter = void 0;
18
- // src/reporter/index.ts
19
- const playwright_pulse_reporter_1 = require("./playwright-pulse-reporter");
20
- Object.defineProperty(exports, "PlaywrightPulseReporter", { enumerable: true, get: function () { return playwright_pulse_reporter_1.PlaywrightPulseReporter; } });
21
- // Export the reporter class as the default export for CommonJS compatibility
22
- // and also as a named export for potential ES module consumers.
23
- exports.default = playwright_pulse_reporter_1.PlaywrightPulseReporter;
24
- // You can also export other related types or utilities if needed
25
- __exportStar(require("../types"), exports); // Re-export shared types if they are used by the reporter consumers
26
- __exportStar(require("../lib/report-types"), exports);
@@ -1,116 +0,0 @@
1
- 'use server';
2
- import * as fs from 'fs/promises';
3
- import * as path from 'path';
4
- const reportFileName = 'playwright-pulse-report.json'; // Use a constant for the filename
5
- const reportFilePath = path.resolve(process.cwd(), reportFileName); // Default path relative to cwd
6
- let cachedReportData = null;
7
- let lastReadTime = null;
8
- const CACHE_DURATION = 5000; // Cache duration in milliseconds (e.g., 5 seconds)
9
- export async function readReportDataInternal() {
10
- const now = Date.now();
11
- // Use cache if it's recent and not forced refresh
12
- if (cachedReportData && lastReadTime && (now - lastReadTime < CACHE_DURATION)) {
13
- // console.log("Returning cached report data.");
14
- return cachedReportData;
15
- }
16
- // console.log(`Attempting to read report file from: ${reportFilePath}`);
17
- try {
18
- const fileContent = await fs.readFile(reportFilePath, 'utf-8');
19
- let parsedData;
20
- try {
21
- parsedData = JSON.parse(fileContent);
22
- }
23
- catch (parseError) {
24
- console.error(`Error parsing JSON from ${reportFilePath}:`, parseError);
25
- throw new Error(`Invalid JSON in report file: ${parseError.message}`);
26
- }
27
- // --- Date Reviver ---
28
- // Function to convert ISO date strings back to Date objects recursively
29
- const reviveDates = (key, value) => {
30
- // Matches ISO 8601 date format (YYYY-MM-DDTHH:mm:ss.sssZ)
31
- const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
32
- if (typeof value === 'string' && isoDateRegex.test(value)) {
33
- const date = new Date(value);
34
- // Check if the parsed date is valid before returning
35
- if (!isNaN(date.getTime())) {
36
- return date;
37
- }
38
- }
39
- return value;
40
- };
41
- // Re-parse with the date reviver
42
- const reportData = JSON.parse(fileContent, reviveDates);
43
- // Basic validation after parsing and date revival
44
- if (!reportData || typeof reportData !== 'object') {
45
- throw new Error('Report file is empty or not a valid object.');
46
- }
47
- if (!reportData.metadata || typeof reportData.metadata.generatedAt !== 'string') { // generatedAt should remain string from JSON
48
- throw new Error('Invalid or missing metadata in report file.');
49
- }
50
- if (!Array.isArray(reportData.results)) {
51
- throw new Error('Missing or invalid "results" array in report file.');
52
- }
53
- // Optional: Validate run data if present
54
- if (reportData.run && typeof reportData.run !== 'object') {
55
- throw new Error('Invalid "run" data in report file.');
56
- }
57
- if (reportData.run && !(reportData.run.timestamp instanceof Date)) {
58
- console.warn('Warning: Run timestamp was not correctly revived to a Date object.');
59
- // Potentially attempt fallback parsing or throw error
60
- reportData.run.timestamp = new Date(reportData.run.timestamp); // Attempt fallback
61
- if (isNaN(reportData.run.timestamp.getTime())) {
62
- throw new Error('Invalid run timestamp format.');
63
- }
64
- }
65
- // Validate dates within results and steps
66
- reportData.results.forEach((result, index) => {
67
- if (!(result.startTime instanceof Date) || !(result.endTime instanceof Date)) {
68
- console.warn(`Warning: Invalid start/end time for result index ${index}. Attempting fallback parsing.`);
69
- result.startTime = new Date(result.startTime);
70
- result.endTime = new Date(result.endTime);
71
- if (isNaN(result.startTime.getTime()) || isNaN(result.endTime.getTime())) {
72
- throw new Error(`Invalid start/end time in result index ${index}.`);
73
- }
74
- }
75
- if (Array.isArray(result.steps)) {
76
- result.steps.forEach((step, stepIndex) => {
77
- if (!(step.startTime instanceof Date) || !(step.endTime instanceof Date)) {
78
- console.warn(`Warning: Invalid start/end time for step index ${stepIndex} in result index ${index}. Attempting fallback parsing.`);
79
- step.startTime = new Date(step.startTime);
80
- step.endTime = new Date(step.endTime);
81
- if (isNaN(step.startTime.getTime()) || isNaN(step.endTime.getTime())) {
82
- throw new Error(`Invalid start/end time in step index ${stepIndex}, result index ${index}.`);
83
- }
84
- }
85
- });
86
- }
87
- else {
88
- result.steps = []; // Initialize if steps array is missing
89
- }
90
- });
91
- cachedReportData = reportData;
92
- lastReadTime = now;
93
- // console.log("Successfully read, parsed, and cached report file.");
94
- return reportData;
95
- }
96
- catch (error) {
97
- if (error.code === 'ENOENT') {
98
- console.warn(`Report file not found at ${reportFilePath}. Returning empty data structure. Ensure Playwright tests ran with the reporter enabled and the file exists.`);
99
- // Return a valid, empty structure
100
- const defaultReport = {
101
- run: null,
102
- results: [],
103
- metadata: { generatedAt: new Date().toISOString() }
104
- };
105
- cachedReportData = defaultReport; // Cache the default empty state
106
- lastReadTime = now;
107
- return defaultReport;
108
- }
109
- else {
110
- // Log the specific error for debugging
111
- console.error(`Error processing report file at ${reportFilePath}:`, error);
112
- // Propagate a user-friendly error
113
- throw new Error(`Failed to load report data: ${error.message}`);
114
- }
115
- }
116
- }