@arghajit/dummy 0.3.28 → 0.3.33

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
  });
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
42
42
  const crypto_1 = require("crypto");
43
43
  const ua_parser_js_1 = __importDefault(require("ua-parser-js"));
44
44
  const os = __importStar(require("os"));
45
+ const compression_utils_1 = require("../utils/compression-utils");
45
46
  const convertStatus = (status, testCase) => {
46
47
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
47
48
  return "failed";
@@ -325,7 +326,10 @@ class PlaywrightPulseReporter {
325
326
  const relativeDestPath = path.join(ATTACHMENTS_SUBDIR, testSubfolder, uniqueFileName);
326
327
  const absoluteDestPath = path.join(this.outputDir, relativeDestPath);
327
328
  await this._ensureDirExists(path.dirname(absoluteDestPath));
329
+ // Copy file first
328
330
  await fs.copyFile(attachment.path, absoluteDestPath);
331
+ // Compress in-place (preserves path/name)
332
+ await (0, compression_utils_1.compressAttachment)(absoluteDestPath, attachment.contentType);
329
333
  if (attachment.contentType.startsWith("image/")) {
330
334
  (_m = pulseResult.screenshots) === null || _m === void 0 ? void 0 : _m.push(relativeDestPath);
331
335
  }
@@ -362,7 +366,10 @@ class PlaywrightPulseReporter {
362
366
  attempts.sort((a, b) => a.retries - b.retries);
363
367
  const firstAttempt = attempts[0];
364
368
  const retryAttempts = attempts.slice(1);
365
- if (retryAttempts.length > 0) {
369
+ // Only populate retryHistory if there were actual failures that triggered retries
370
+ // If all attempts passed, we don't need to show retry history
371
+ const hasActualRetries = retryAttempts.length > 0 && retryAttempts.some(attempt => attempt.status === 'failed' || attempt.status === 'flaky' || firstAttempt.status === 'failed' || firstAttempt.status === 'flaky');
372
+ if (hasActualRetries) {
366
373
  firstAttempt.retryHistory = retryAttempts;
367
374
  // Calculate final status and outcome from the last attempt if retries exist
368
375
  const lastAttempt = attempts[attempts.length - 1];
@@ -374,8 +381,9 @@ class PlaywrightPulseReporter {
374
381
  }
375
382
  }
376
383
  else {
377
- // If no retries, ensure final_status is undefined (as requested)
384
+ // If no actual retries (all attempts passed), ensure final_status and retryHistory are removed
378
385
  delete firstAttempt.final_status;
386
+ delete firstAttempt.retryHistory;
379
387
  }
380
388
  finalResults.push(firstAttempt);
381
389
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Compression utilities for images
3
+ * Uses sharp for image compression (works cross-platform with no external dependencies)
4
+ */
5
+ /**
6
+ * Compress an image file in-place
7
+ * @param filePath - Absolute path to the image file
8
+ * @param options - Compression options
9
+ */
10
+ export declare function compressImage(filePath: string, options?: {
11
+ quality?: number;
12
+ }): Promise<void>;
13
+ /**
14
+ * Compress an attachment file (auto-detects type)
15
+ * Note: Only compresses images. Videos are already compressed by Playwright.
16
+ * @param filePath - Absolute path to the file
17
+ * @param contentType - MIME content type
18
+ */
19
+ export declare function compressAttachment(filePath: string, contentType: string): Promise<void>;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ // src/utils/compression-utils.ts
3
+ /**
4
+ * Compression utilities for images
5
+ * Uses sharp for image compression (works cross-platform with no external dependencies)
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.compressImage = compressImage;
42
+ exports.compressAttachment = compressAttachment;
43
+ const fs = __importStar(require("fs/promises"));
44
+ const path = __importStar(require("path"));
45
+ /**
46
+ * Compress an image file in-place
47
+ * @param filePath - Absolute path to the image file
48
+ * @param options - Compression options
49
+ */
50
+ async function compressImage(filePath, options = {}) {
51
+ try {
52
+ const sharp = require('sharp');
53
+ const quality = options.quality || 75;
54
+ const ext = path.extname(filePath).toLowerCase();
55
+ // Read original file
56
+ const imageBuffer = await fs.readFile(filePath);
57
+ let compressedBuffer;
58
+ if (ext === '.png') {
59
+ // Compress PNG
60
+ compressedBuffer = await sharp(imageBuffer)
61
+ .png({ quality, compressionLevel: 9 })
62
+ .toBuffer();
63
+ }
64
+ else if (ext === '.jpg' || ext === '.jpeg') {
65
+ // Compress JPEG
66
+ compressedBuffer = await sharp(imageBuffer)
67
+ .jpeg({ quality, mozjpeg: true })
68
+ .toBuffer();
69
+ }
70
+ else if (ext === '.webp') {
71
+ // Compress WebP
72
+ compressedBuffer = await sharp(imageBuffer)
73
+ .webp({ quality })
74
+ .toBuffer();
75
+ }
76
+ else {
77
+ // Unsupported format, skip compression
78
+ console.log(`Compression skipped for unsupported format: ${ext}`);
79
+ return;
80
+ }
81
+ // Only overwrite if compression actually reduced size
82
+ if (compressedBuffer.length < imageBuffer.length) {
83
+ await fs.writeFile(filePath, compressedBuffer);
84
+ const savedBytes = imageBuffer.length - compressedBuffer.length;
85
+ const savedPercent = ((savedBytes / imageBuffer.length) * 100).toFixed(1);
86
+ console.log(`Compressed ${path.basename(filePath)}: ${savedPercent}% smaller`);
87
+ }
88
+ else {
89
+ console.log(`Skipped ${path.basename(filePath)}: compression didn't reduce size`);
90
+ }
91
+ }
92
+ catch (error) {
93
+ console.warn(`Failed to compress image ${filePath}:`, error.message);
94
+ // File remains unchanged
95
+ }
96
+ }
97
+ /**
98
+ * Compress an attachment file (auto-detects type)
99
+ * Note: Only compresses images. Videos are already compressed by Playwright.
100
+ * @param filePath - Absolute path to the file
101
+ * @param contentType - MIME content type
102
+ */
103
+ async function compressAttachment(filePath, contentType) {
104
+ if (contentType.startsWith('image/')) {
105
+ await compressImage(filePath, { quality: 75 });
106
+ }
107
+ // Videos are skipped - already compressed by Playwright as WebM
108
+ }
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.33",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "homepage": "https://arghajit47.github.io/playwright-pulse/",
7
7
  "repository": {
@@ -73,6 +73,7 @@
73
73
  "node-fetch": "^3.3.2",
74
74
  "nodemailer": "^7.0.3",
75
75
  "patch-package": "^8.0.0",
76
+ "sharp": "^0.33.5",
76
77
  "ua-parser-js": "^1.0.41",
77
78
  "zod": "^3.24.2"
78
79
  },
@@ -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";