@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 +120 -56
- package/dist/reporter/playwright-pulse-reporter.d.ts +2 -0
- package/dist/reporter/playwright-pulse-reporter.js +118 -13
- package/dist/types/index.d.ts +17 -0
- package/package.json +11 -51
- package/scripts/generate-email-report.mjs +714 -0
- package/scripts/generate-report.mjs +2277 -0
- package/scripts/generate-static-report.mjs +1137 -945
- package/scripts/merge-pulse-report.js +1 -0
- package/scripts/{sendReport.js → sendReport.mjs} +138 -71
- package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max-1.png +0 -0
- package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max.png +0 -0
- package/screenshots/Email-report.jpg +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-1.png +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-2.png +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html.png +0 -0
- package/screenshots/image.png +0 -0
package/README.md
CHANGED
|
@@ -1,54 +1,84 @@
|
|
|
1
1
|
# Playwright Pluse Report
|
|
2
2
|
|
|
3
|
-

|
|
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
|
-
|
|
6
|
+
## [Live Demo](https://pulse-report.netlify.app/)
|
|
7
7
|
|
|
8
|
-
##
|
|
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
|
+
## 
|
|
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;">
|
|
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="
|
|
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="
|
|
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
|
-
[](https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images//Email-report-mobile-template.jpeg)
|
|
55
|
+
|
|
56
|
+
[](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-
|
|
93
|
+
npm install @arghajit/playwright-pulse-report@latest --save-dev
|
|
64
94
|
# or
|
|
65
|
-
yarn add @arghajit/playwright-pulse-
|
|
95
|
+
yarn add @arghajit/playwright-pulse-report@latest --dev
|
|
66
96
|
# or
|
|
67
|
-
pnpm add @arghajit/playwright-pulse-
|
|
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
|
|
75
|
-
import * as path from
|
|
76
|
-
|
|
104
|
+
import { defineConfig } from "@playwright/test";
|
|
105
|
+
import * as path from "path";
|
|
77
106
|
|
|
78
|
-
const PULSE_REPORT_DIR = path.resolve(__dirname,
|
|
107
|
+
const PULSE_REPORT_DIR = path.resolve(__dirname, "pulse-report");
|
|
79
108
|
|
|
80
109
|
export default defineConfig({
|
|
81
110
|
reporter: [
|
|
82
|
-
[
|
|
83
|
-
[
|
|
84
|
-
|
|
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:
|
|
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
|
|
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
|
|
202
|
-
npm run
|
|
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
|
-
##
|
|
270
|
+
## 
|
|
271
|
+
|
|
272
|
+
### 🚀 **Upgrade Now**
|
|
222
273
|
|
|
223
274
|
```bash
|
|
224
|
-
playwright-pulse-
|
|
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
|
-
|
|
278
|
+
---
|
|
238
279
|
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
+
console.log(`Starting test: ${test.title}`);
|
|
109
111
|
}
|
|
110
|
-
|
|
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:
|
|
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
|
|
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
|
|
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,
|
|
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 (((
|
|
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) || ((
|
|
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:
|
|
283
|
+
browser: browserDetails,
|
|
204
284
|
retries: result.retry,
|
|
205
|
-
steps: ((
|
|
206
|
-
errorMessage: (
|
|
207
|
-
stackTrace: (
|
|
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}
|
|
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: {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
+
}
|