@arghajit/playwright-pulse-report 0.2.1 → 0.2.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.
package/README.md CHANGED
@@ -1,54 +1,84 @@
1
1
  # Playwright Pluse Report
2
2
 
3
- ![Playwright Pulse Report](./screenshots/image.png)
4
- *The ultimate Playwright reporter — Interactive dashboard with historical trend analytics, CI/CD-ready standalone HTML reports, and sharding support for scalable test execution.*
3
+ ![Playwright Pulse Report](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/image.png)
4
+ _The ultimate Playwright reporter — Interactive dashboard with historical trend analytics, CI/CD-ready standalone HTML reports, and sharding support for scalable test execution._
5
5
 
6
- <a href="https://pulse-report.netlify.app/" target="_blank"><h3>Live Demo</h3></a>
6
+ ## [Live Demo](https://pulse-report.netlify.app/)
7
7
 
8
- ## 🌟 Features
9
-
10
- - **Custom Playwright Reporter** - Collects detailed test results with rich metadata
11
- - **Sharding Support** - Seamlessly handles parallel test execution across shards
12
- - **Two Reporting Options**:
13
- - **Standalone HTML Report** - Self-contained, shareable single file
14
- - **Email Report** - Attached test summary with generated test report
15
- - **CI/CD Ready** - GitHub Actions workflow examples included
16
- - **Email Integration** - Send reports directly to stakeholders
17
- - **AI Analysis** - Get insights into your test results
8
+ ## ![Features](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/features.svg)
18
9
 
19
10
  ## 📸 Screenshots
20
11
 
21
12
  ### 🖥️ Desktop View
22
13
 
23
- <div align="center" style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;"> <a href="https://postimg.cc/180cym6c" target="_blank"> <img src="./screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html.png" alt="Dashboard Overview" width="300"/> <p align="center"><strong>Dashboard Overview</strong></p> </a> <a href="https://postimg.cc/V5TFRHmM" target="_blank"> <img src="./screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-1.png" alt="Test Details" width="300"/> <p align="center"><strong>Test Details</strong></p> </a> <a href="https://postimg.cc/XXTwFGkk" target="_blank"> <img src="./screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-2.png" alt="Filter View" width="300"/> <p align="center"><strong>Filter View</strong></p> </a> </div>
14
+ <div align="center" style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;">
15
+ <a href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//playwright-pulse-static-report-desktop.html.png" target="_blank"> <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//playwright-pulse-static-report-desktop.html.png" alt="Dashboard Overview" width="300"/>
16
+ <p align="center"><strong>Dashboard Overview</strong></p>
17
+ </a>
18
+ <a href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-run-desktop.png" target="_blank"> <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-run-desktop.png" alt="Test Details" width="300"/>
19
+ <p align="center"><strong>Test Details</strong>
20
+ </p>
21
+ </a>
22
+ <a href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-error-desktop.png" target="_blank"> <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-run-desktop.png" alt="Test Failure Details" width="300"/>
23
+ <p align="center"><strong>Test Failure Details</strong>
24
+ </p>
25
+ </a>
26
+ <a href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-trends-desktop.png" target="_blank"> <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Test-trends-desktop.png" alt="Filter View" width="300"/>
27
+ <p align="center"><strong>Test Trends</strong></p>
28
+ </a>
29
+ </div>
24
30
 
25
31
  ### 📱 Mobile View
26
32
 
27
33
  <div align="center" style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;">
28
34
 
29
35
  <a href="https://postimg.cc/CzJBLR5N" target="_blank">
30
- <img src="./screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max.png" alt="Mobile Overview" width="300"/>
36
+ <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//playwright-pulse-static-report-Dashboard.html.png" alt="Mobile Dashboard Overview" width="300"/>
31
37
  <p align="center"><strong>Dashboard Overview</strong></p>
32
38
  </a>
33
39
 
34
40
  <a href="https://postimg.cc/G8YTczT8" target="_blank">
35
- <img src="./screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max-1.png" alt="Test Details" width="300"/>
41
+ <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//playwright-pulse-static-report_Test-results.html.png" alt="Test Details" width="300"/>
36
42
  <p align="center"><strong>Test Details</strong></p>
37
43
  </a>
38
44
 
45
+ <a href="https://postimg.cc/G8YTczT8" target="_blank">
46
+ <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//playwright-pulse-static-report-Trends.html.png" alt="Test Trends" width="300"/>
47
+ <p align="center"><strong>Test Trends</strong></p>
48
+ </a>
49
+
39
50
  </div>
40
51
 
41
52
  ### Email Report Example
42
53
 
43
- [![Email Report](./screenshots//Email-report.jpg)](https://postimg.cc/DmCPgtqh)
54
+ [![Email Report Template](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Email-report-mobile-template.jpeg)](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Email-report-mobile-template.jpeg)
55
+
56
+ [![Email Report](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//pulse-email-summary.html.png)](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//pulse-email-summary.html.png)
57
+
58
+ ## Available Scripts
59
+
60
+ The project provides these utility commands:
61
+
62
+ | Command | Description |
63
+ |------------------------|-----------------------------------------------------------------------------|
64
+ | `generate-report` | Generates playwright-pulse-report.html, Loads screenshots and images dynamically from the attachments/ directory, Produces a lighter HTML file with faster initial load, Requires attachments/ directory to be present when viewing the report |
65
+ | `generate-pulse-report`| Generates `playwright-pulse-static-report.html`, Self-contained, no server required, Preserves all dashboard functionality |
66
+ | `merge-pulse-report` | Combines multiple parallel test json reports, basically used in sharding |
67
+ | `generate-trend` | Analyzes historical trends in test results |
68
+ | `generate-email-report`| Generates email-friendly report versions |
69
+ | `send-email` | Generates email-friendly report versions & Distributes report via email |
70
+
71
+ Run with `npm run <command>`
44
72
 
45
73
  ## 🛠️ How It Works
46
74
 
47
75
  1. **Reporter Collection**:
76
+
48
77
  - Custom reporter collects detailed results during test execution
49
78
  - Handles sharding by merging `.pulse-shard-results-*.json` files
50
79
 
51
80
  2. **JSON Output**:
81
+
52
82
  - Generates comprehensive `playwright-pulse-report.json`
53
83
 
54
84
  3. **Visualization Options**:
@@ -60,29 +90,31 @@
60
90
  ### 1. Installation
61
91
 
62
92
  ```bash
63
- npm install @arghajit/playwright-pulse-reporter@latest --save-dev
93
+ npm install @arghajit/playwright-pulse-report@latest --save-dev
64
94
  # or
65
- yarn add @arghajit/playwright-pulse-reporter@latest --dev
95
+ yarn add @arghajit/playwright-pulse-report@latest --dev
66
96
  # or
67
- pnpm add @arghajit/playwright-pulse-reporter@latest --save-dev
97
+ pnpm add @arghajit/playwright-pulse-report@latest --save-dev
68
98
  ```
69
99
 
70
100
  ### 2. Configure Playwright
71
101
 
72
102
  ```typescript
73
103
  // playwright.config.ts
74
- import { defineConfig } from '@playwright/test';
75
- import * as path from 'path';
76
-
104
+ import { defineConfig } from "@playwright/test";
105
+ import * as path from "path";
77
106
 
78
- const PULSE_REPORT_DIR = path.resolve(__dirname, 'pulse-report');
107
+ const PULSE_REPORT_DIR = path.resolve(__dirname, "pulse-report");
79
108
 
80
109
  export default defineConfig({
81
110
  reporter: [
82
- ['list'],
83
- ['@arghajit/playwright-pulse-reporter', {
84
- outputDir: PULSE_REPORT_DIR
85
- }]
111
+ ["list"],
112
+ [
113
+ "@arghajit/playwright-pulse-report",
114
+ {
115
+ outputDir: PULSE_REPORT_DIR,
116
+ },
117
+ ],
86
118
  ],
87
119
  // Other configurations...
88
120
  });
@@ -99,9 +131,11 @@ npx send-email # Sends email report
99
131
 
100
132
  ## 📊 Report Options
101
133
 
102
- ### Option 1: Static HTML Report
134
+ ### Option 1: Static HTML Report (Embedded Attachments)
103
135
 
104
136
  ```bash
137
+ npm run generate-pulse-report
138
+ or,
105
139
  npx generate-pulse-report
106
140
  ```
107
141
 
@@ -109,7 +143,20 @@ npx generate-pulse-report
109
143
  - Self-contained, no server required
110
144
  - Preserves all dashboard functionality
111
145
 
112
- ### Option 2: Email Report
146
+ ### Option 2: HTML Report (Attachment-based)
147
+
148
+ ```bash
149
+ npm run generate-report
150
+ or,
151
+ npx generate-report
152
+ ```
153
+
154
+ - Generates playwright-pulse-report.html
155
+ - Loads screenshots and images dynamically from the attachments/ directory
156
+ - Produces a lighter HTML file with faster initial load
157
+ - Requires attachments/ directory to be present when viewing the report
158
+
159
+ ### Option 3: Email Report
113
160
 
114
161
  1. Configure `.env`:
115
162
 
@@ -125,6 +172,8 @@ npx generate-pulse-report
125
172
  npx send-email
126
173
  ```
127
174
 
175
+ NOTE: The email will be send with a light-weight html file, which can be opened in mail preview application.
176
+
128
177
  ## 🤖 AI Analysis
129
178
 
130
179
  The dashboard includes AI-powered test analysis that provides:
@@ -158,7 +207,7 @@ The dashboard includes AI-powered test analysis that provides:
158
207
  - name: Generate Pulse Report
159
208
  run: |
160
209
  npm run script merge-report
161
- npm run script generate-report
210
+ npm run generate-report [or, npm run generate-pulse-report]
162
211
 
163
212
  # Upload final merged report as CI artifact
164
213
  - name: Upload Pulse report
@@ -198,8 +247,8 @@ The dashboard includes AI-powered test analysis that provides:
198
247
  # Merge all sharded JSON reports into one final output
199
248
  - name: Generate Pulse Report
200
249
  run: |
201
- npm run script merge-report
202
- npm run script generate-report
250
+ npm run merge-report
251
+ npm run generate-report [or, npm run generate-pulse-report]
203
252
 
204
253
  # Upload final merged report as CI artifact
205
254
  - name: Upload Pulse report
@@ -207,53 +256,68 @@ The dashboard includes AI-powered test analysis that provides:
207
256
  with:
208
257
  name: pulse-report
209
258
  path: pulse-report/
210
-
211
259
  ```
212
260
 
213
261
  ## 🧠 Notes
214
262
 
263
+ - <strong>`npm run generate-report` generates a HTML report ( screenshots/images will be taken in realtime from 'attachments/' directory ).</strong>
264
+ - <strong>`npm run generate-pulse-report` generates a fully self-contained static HTML report( All screenshots and images are embedded directly into the HTML using base64 encoding, which simplifies distribution but may result in larger file sizes and longer load times ).</strong>
215
265
  - Each shard generates its own playwright-pulse-report.json inside pulse-report/.
216
266
  - Artifacts are named using the shard type (matrix.config.type).
217
267
  - After the test matrix completes, reports are downloaded, renamed, and merged.
218
268
  - merge-report is a custom Node.js script that combines all JSON files into one.
219
- - generate-report can build a static HTML dashboard if needed.
220
269
 
221
- ## 📂 Key Files
270
+ ## ![Features](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//pulse-folder-structures.svg)
271
+
272
+ ### 🚀 **Upgrade Now**
222
273
 
223
274
  ```bash
224
- playwright-pulse-reporter/
225
- ├── src/
226
- │ ├── reporter/ # Reporter implementation
227
- │ └── app/ # Next.js dashboard
228
- ├── scripts/
229
- │ └── generate-static-report.mjs # HTML generator
230
- | └── generate-trend.mjs # Generate Trends
231
- | └── merge-pulse-report.mjs # merge sharded reports
232
- | └── sendReport.mjs # Send email report
233
- ├── pulse-report/ # Generated reports
234
- └── sample-report.json # Example data
275
+ npm install @arghajit/playwright-pulse-report@latest
235
276
  ```
236
277
 
237
- ## 🎉 What's New in v0.2.1
278
+ ---
238
279
 
239
- ### **Key Improvements**
280
+ <img src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//pulse-logo.png" alt="pulse dashboard" title="pulse dashboard" height="35px" width="60px" align="left" padding="5px"/>
281
+ <h2>Pulse Dashboard</h2>
240
282
 
241
- | Feature | Description |
242
- |---------|-------------|
243
- | **🎨 Refined UI** | Completely redesigned static HTML reports for better readability and navigation |
244
- | **📊 History Trends** | Visual analytics for:<br>• Test History for last 15 runs<br>• Test suite pass/fail rates<br>• Duration trends<br>• Individual test flakiness |
245
- | **🛠️ Project Fixes** | Corrected project name display in test suite components |
283
+ **Real-time Playwright Test Monitoring & Analysis**
246
284
 
247
- ### 🚀 **Upgrade Now**
285
+ A Next.js component & CLI tool for visualizing Playwright test executions. Provides real-time insights, historical trends, and failure analysis.
286
+
287
+ **Key Features**:
288
+
289
+ - Interactive test result visualization
290
+ - Historical trend analysis
291
+ - Failure pattern identification
292
+
293
+ **Quick Start**:
248
294
 
249
295
  ```bash
250
- npm install @arghajit/playwright-pulse-report@latest
296
+ npx pulse-dashboard
297
+ or,
298
+ npm run pulse-dashboard
251
299
  ```
252
300
 
301
+ *(Run from project root containing `pulse-report/` directory)*
302
+
303
+ **NPM Package**: [pulse-dashboard](https://www.npmjs.com/package/pulse-dashboard)
304
+
305
+ **Tech Stack**: Next.js, TypeScript, Tailwind CSS, Playwright
306
+
307
+ *Part of the Playwright Pulse Report ecosystem*
308
+
309
+ ---
310
+
253
311
  ## 📬 Support
254
312
 
255
313
  For issues or feature requests, please [Contact Me](mailto:arghajitsingha47@gmail.com).
256
314
 
257
315
  ---
258
316
 
317
+ ## 🙌🏼 Thank you
318
+
319
+ Special Thanks to [@Suman Vishwakarma](https://www.linkedin.com/in/suman-vishwakarma-426108185/), for continuous UAT feedback.
320
+
321
+ ---
322
+
259
323
  <div align="center">Made by Arghajit Singha | MIT Licensed</div>
@@ -15,9 +15,11 @@ export declare class PlaywrightPulseReporter implements Reporter {
15
15
  printsToStdio(): boolean;
16
16
  onBegin(config: FullConfig, suite: Suite): void;
17
17
  onTestBegin(test: TestCase): void;
18
+ private getBrowserDetails;
18
19
  private processStep;
19
20
  onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
20
21
  onError(error: any): void;
22
+ private _getEnvDetails;
21
23
  private _writeShardResults;
22
24
  private _mergeShardResults;
23
25
  private _cleanupTemporaryFiles;
@@ -39,6 +39,8 @@ const fs = __importStar(require("fs/promises"));
39
39
  const path = __importStar(require("path"));
40
40
  const crypto_1 = require("crypto");
41
41
  const attachment_utils_1 = require("./attachment-utils"); // Use relative path
42
+ const ua_parser_js_1 = require("ua-parser-js"); // Added UAParser import
43
+ const os = __importStar(require("os"));
42
44
  const convertStatus = (status, testCase) => {
43
45
  if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
44
46
  return "failed";
@@ -105,9 +107,61 @@ class PlaywrightPulseReporter {
105
107
  .catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
106
108
  }
107
109
  onTestBegin(test) {
108
- // console.log(`Starting test: ${test.title}`);
110
+ console.log(`Starting test: ${test.title}`);
109
111
  }
110
- async processStep(step, testId, browserName, testCase) {
112
+ getBrowserDetails(test) {
113
+ var _a, _b, _c, _d;
114
+ const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project(); // project() can return undefined if not in a project context
115
+ const projectConfig = project === null || project === void 0 ? void 0 : project.use; // This is where options like userAgent, defaultBrowserType are
116
+ const userAgent = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.userAgent;
117
+ const configuredBrowserType = (_b = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.browserName) === null || _b === void 0 ? void 0 : _b.toLowerCase();
118
+ const parser = new ua_parser_js_1.UAParser(userAgent);
119
+ const result = parser.getResult();
120
+ let browserName = result.browser.name;
121
+ const browserVersion = result.browser.version
122
+ ? ` v${result.browser.version.split(".")[0]}`
123
+ : ""; // Major version
124
+ const osName = result.os.name ? ` on ${result.os.name}` : "";
125
+ const osVersion = result.os.version
126
+ ? ` ${result.os.version.split(".")[0]}`
127
+ : ""; // Major version
128
+ const deviceType = result.device.type; // "mobile", "tablet", etc.
129
+ let finalString;
130
+ // If UAParser couldn't determine browser name, fallback to configured type
131
+ if (browserName === undefined) {
132
+ browserName = configuredBrowserType;
133
+ finalString = `${browserName}`;
134
+ }
135
+ else {
136
+ // Specific refinements for mobile based on parsed OS and device type
137
+ if (deviceType === "mobile" || deviceType === "tablet") {
138
+ if ((_c = result.os.name) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes("android")) {
139
+ if (browserName.toLowerCase().includes("chrome"))
140
+ browserName = "Chrome Mobile";
141
+ else if (browserName.toLowerCase().includes("firefox"))
142
+ browserName = "Firefox Mobile";
143
+ else if (result.engine.name === "Blink" && !result.browser.name)
144
+ browserName = "Android WebView";
145
+ else if (browserName &&
146
+ !browserName.toLowerCase().includes("mobile")) {
147
+ // Keep it as is, e.g. "Samsung Browser" is specific enough
148
+ }
149
+ else {
150
+ browserName = "Android Browser"; // default for android if not specific
151
+ }
152
+ }
153
+ else if ((_d = result.os.name) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes("ios")) {
154
+ browserName = "Mobile Safari";
155
+ }
156
+ }
157
+ else if (browserName === "Electron") {
158
+ browserName = "Electron App";
159
+ }
160
+ finalString = `${browserName}${browserVersion}${osName}${osVersion}`;
161
+ }
162
+ return finalString.trim();
163
+ }
164
+ async processStep(step, testId, browserDetails, testCase) {
111
165
  var _a, _b, _c, _d;
112
166
  let stepStatus = "passed";
113
167
  let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
@@ -132,7 +186,7 @@ class PlaywrightPulseReporter {
132
186
  duration: duration,
133
187
  startTime: startTime,
134
188
  endTime: endTime,
135
- browser: browserName,
189
+ browser: browserDetails,
136
190
  errorMessage: errorMessage,
137
191
  stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
138
192
  codeLocation: codeLocation || undefined,
@@ -146,9 +200,9 @@ class PlaywrightPulseReporter {
146
200
  };
147
201
  }
148
202
  async onTestEnd(test, result) {
149
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
203
+ var _a, _b, _c, _d, _e, _f, _g, _h;
150
204
  const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
151
- const browserName = ((_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) || (project === null || project === void 0 ? void 0 : project.name) || "unknown";
205
+ const browserDetails = this.getBrowserDetails(test);
152
206
  const testStatus = convertStatus(result.status, test);
153
207
  const startTime = new Date(result.startTime);
154
208
  const endTime = new Date(startTime.getTime() + result.duration);
@@ -160,7 +214,7 @@ class PlaywrightPulseReporter {
160
214
  const processAllSteps = async (steps) => {
161
215
  let processed = [];
162
216
  for (const step of steps) {
163
- const processedStep = await this.processStep(step, testIdForFiles, browserName, test);
217
+ const processedStep = await this.processStep(step, testIdForFiles, browserDetails, test);
164
218
  processed.push(processedStep);
165
219
  if (step.steps && step.steps.length > 0) {
166
220
  processedStep.steps = await processAllSteps(step.steps);
@@ -170,7 +224,7 @@ class PlaywrightPulseReporter {
170
224
  };
171
225
  let codeSnippet = undefined;
172
226
  try {
173
- if (((_c = test.location) === null || _c === void 0 ? void 0 : _c.file) && ((_d = test.location) === null || _d === void 0 ? void 0 : _d.line) && ((_e = test.location) === null || _e === void 0 ? void 0 : _e.column)) {
227
+ if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) && ((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) && ((_d = test.location) === null || _d === void 0 ? void 0 : _d.column)) {
174
228
  const relativePath = path.relative(this.config.rootDir, test.location.file);
175
229
  codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
176
230
  }
@@ -191,20 +245,46 @@ class PlaywrightPulseReporter {
191
245
  });
192
246
  }
193
247
  const uniqueTestId = test.id;
248
+ // --- REFINED THIS SECTION for testData ---
249
+ const maxWorkers = this.config.workers;
250
+ let mappedWorkerId;
251
+ // First, check for the special case where a test is not assigned a worker (e.g., global setup failure).
252
+ if (result.workerIndex === -1) {
253
+ mappedWorkerId = -1; // Keep it as -1 to clearly identify this special case.
254
+ }
255
+ else if (maxWorkers && maxWorkers > 0) {
256
+ // If there's a valid worker, map it to the concurrency slot...
257
+ const zeroBasedId = result.workerIndex % maxWorkers;
258
+ // ...and then shift it to be 1-based (1 to n).
259
+ mappedWorkerId = zeroBasedId + 1;
260
+ }
261
+ else {
262
+ // Fallback for when maxWorkers is not defined: just use the original index (and shift to 1-based).
263
+ mappedWorkerId = result.workerIndex + 1;
264
+ }
265
+ const testSpecificData = {
266
+ workerId: mappedWorkerId,
267
+ uniqueWorkerIndex: result.workerIndex, // We'll keep the original for diagnostics
268
+ totalWorkers: maxWorkers,
269
+ configFile: this.config.configFile,
270
+ metadata: this.config.metadata
271
+ ? JSON.stringify(this.config.metadata)
272
+ : undefined,
273
+ };
194
274
  const pulseResult = {
195
275
  id: uniqueTestId,
196
276
  runId: "TBD",
197
277
  name: test.titlePath().join(" > "),
198
- suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_f = this.config.projects[0]) === null || _f === void 0 ? void 0 : _f.name) || "Default Suite",
278
+ suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_e = this.config.projects[0]) === null || _e === void 0 ? void 0 : _e.name) || "Default Suite",
199
279
  status: testStatus,
200
280
  duration: result.duration,
201
281
  startTime: startTime,
202
282
  endTime: endTime,
203
- browser: browserName,
283
+ browser: browserDetails,
204
284
  retries: result.retry,
205
- steps: ((_g = result.steps) === null || _g === void 0 ? void 0 : _g.length) ? await processAllSteps(result.steps) : [],
206
- errorMessage: (_h = result.error) === null || _h === void 0 ? void 0 : _h.message,
207
- stackTrace: (_j = result.error) === null || _j === void 0 ? void 0 : _j.stack,
285
+ steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
286
+ errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
287
+ stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
208
288
  codeSnippet: codeSnippet,
209
289
  tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
210
290
  screenshots: [],
@@ -212,6 +292,8 @@ class PlaywrightPulseReporter {
212
292
  tracePath: undefined,
213
293
  stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
214
294
  stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
295
+ // --- UPDATED THESE LINES from testSpecificData ---
296
+ ...testSpecificData,
215
297
  };
216
298
  try {
217
299
  (0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
@@ -236,6 +318,20 @@ class PlaywrightPulseReporter {
236
318
  console.error(error.stack);
237
319
  }
238
320
  }
321
+ _getEnvDetails() {
322
+ return {
323
+ host: os.hostname(),
324
+ os: `${os.platform()} ${os.release()}`,
325
+ cpu: {
326
+ model: os.cpus()[0] ? os.cpus()[0].model : "N/A", // Handle cases with no CPU info
327
+ cores: os.cpus().length,
328
+ },
329
+ memory: `${(os.totalmem() / 1024 ** 3).toFixed(2)}GB`, // Total RAM in GB
330
+ node: process.version,
331
+ v8: process.versions.v8,
332
+ cwd: process.cwd(),
333
+ };
334
+ }
239
335
  async _writeShardResults() {
240
336
  if (this.shardIndex === undefined) {
241
337
  return;
@@ -329,7 +425,9 @@ class PlaywrightPulseReporter {
329
425
  }
330
426
  const runEndTime = Date.now();
331
427
  const duration = runEndTime - this.runStartTime;
332
- const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
428
+ const runId = `run-${this.runStartTime}-581d5ad8-ce75-4ca5-94a6-ed29c466c815`; // Need not to change
429
+ // --- CALLING _getEnvDetails HERE ---
430
+ const environmentDetails = this._getEnvDetails();
333
431
  const runData = {
334
432
  id: runId,
335
433
  timestamp: new Date(this.runStartTime),
@@ -338,10 +436,16 @@ class PlaywrightPulseReporter {
338
436
  failed: 0,
339
437
  skipped: 0,
340
438
  duration,
439
+ // --- ADDED environmentDetails HERE ---
440
+ environment: environmentDetails,
341
441
  };
342
442
  let finalReport = undefined; // Initialize as undefined
343
443
  if (this.isSharded) {
344
444
  finalReport = await this._mergeShardResults(runData);
445
+ // Ensured environment details are on the final merged runData if not already
446
+ if (finalReport && finalReport.run && !finalReport.run.environment) {
447
+ finalReport.run.environment = environmentDetails;
448
+ }
345
449
  }
346
450
  else {
347
451
  this.results.forEach((r) => (r.runId = runId));
@@ -388,6 +492,7 @@ PlaywrightPulseReporter: Run Finished
388
492
  failed: 0,
389
493
  skipped: 0,
390
494
  duration: duration,
495
+ environment: environmentDetails,
391
496
  },
392
497
  results: [],
393
498
  metadata: {
@@ -36,6 +36,10 @@ export interface TestResult {
36
36
  tracePath?: string;
37
37
  stdout?: string[];
38
38
  stderr?: string[];
39
+ workerId?: number;
40
+ totalWorkers?: number;
41
+ configFile?: string;
42
+ metadata?: string;
39
43
  }
40
44
  export interface TestRun {
41
45
  id: string;
@@ -45,6 +49,7 @@ export interface TestRun {
45
49
  failed: number;
46
50
  skipped: number;
47
51
  duration: number;
52
+ environment?: EnvDetails;
48
53
  }
49
54
  export interface TrendDataPoint {
50
55
  date: string;
@@ -63,3 +68,15 @@ export interface PlaywrightPulseReporterOptions {
63
68
  outputDir?: string;
64
69
  base64Images?: boolean;
65
70
  }
71
+ export interface EnvDetails {
72
+ host: string;
73
+ os: string;
74
+ cpu: {
75
+ model: string;
76
+ cores: number;
77
+ };
78
+ memory: string;
79
+ node: string;
80
+ v8: string;
81
+ cwd: string;
82
+ }