@arghajit/dummy 0.1.0

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.
Files changed (33) hide show
  1. package/README.md +259 -0
  2. package/dist/index.d.ts +5 -0
  3. package/dist/index.js +26 -0
  4. package/dist/lib/report-types.d.ts +8 -0
  5. package/dist/lib/report-types.js +2 -0
  6. package/dist/playwright-pulse-reporter.d.ts +26 -0
  7. package/dist/playwright-pulse-reporter.js +304 -0
  8. package/dist/reporter/attachment-utils.d.ts +10 -0
  9. package/dist/reporter/attachment-utils.js +192 -0
  10. package/dist/reporter/index.d.ts +5 -0
  11. package/dist/reporter/index.js +9 -0
  12. package/dist/reporter/lib/report-types.d.ts +8 -0
  13. package/dist/reporter/lib/report-types.js +2 -0
  14. package/dist/reporter/playwright-pulse-reporter.d.ts +27 -0
  15. package/dist/reporter/playwright-pulse-reporter.js +454 -0
  16. package/dist/reporter/reporter/playwright-pulse-reporter.d.ts +1 -0
  17. package/dist/reporter/reporter/playwright-pulse-reporter.js +398 -0
  18. package/dist/reporter/types/index.d.ts +52 -0
  19. package/dist/reporter/types/index.js +2 -0
  20. package/dist/types/index.d.ts +65 -0
  21. package/dist/types/index.js +2 -0
  22. package/package.json +73 -0
  23. package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max-1.png +0 -0
  24. package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max.png +0 -0
  25. package/screenshots/Email-report.jpg +0 -0
  26. package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-1.png +0 -0
  27. package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-2.png +0 -0
  28. package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html.png +0 -0
  29. package/screenshots/image.png +0 -0
  30. package/scripts/generate-static-report.mjs +2279 -0
  31. package/scripts/generate-trend.mjs +165 -0
  32. package/scripts/merge-pulse-report.js +81 -0
  33. package/scripts/sendReport.js +335 -0
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # Playwright Pluse Report
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.*
5
+
6
+ <a href="https://pulse-report.netlify.app/" target="_blank"><h3>Live Demo</h3></a>
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
18
+
19
+ ## 📸 Screenshots
20
+
21
+ ### 🖥️ Desktop View
22
+
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>
24
+
25
+ ### 📱 Mobile View
26
+
27
+ <div align="center" style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;">
28
+
29
+ <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"/>
31
+ <p align="center"><strong>Dashboard Overview</strong></p>
32
+ </a>
33
+
34
+ <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"/>
36
+ <p align="center"><strong>Test Details</strong></p>
37
+ </a>
38
+
39
+ </div>
40
+
41
+ ### Email Report Example
42
+
43
+ [![Email Report](./screenshots//Email-report.jpg)](https://postimg.cc/DmCPgtqh)
44
+
45
+ ## 🛠️ How It Works
46
+
47
+ 1. **Reporter Collection**:
48
+ - Custom reporter collects detailed results during test execution
49
+ - Handles sharding by merging `.pulse-shard-results-*.json` files
50
+
51
+ 2. **JSON Output**:
52
+ - Generates comprehensive `playwright-pulse-report.json`
53
+
54
+ 3. **Visualization Options**:
55
+ - **Static HTML**: Self-contained report file with all data
56
+ - **Email**: Send formatted reports to stakeholders
57
+
58
+ ## 🏁 Quick Start
59
+
60
+ ### 1. Installation
61
+
62
+ ```bash
63
+ npm install @arghajit/playwright-pulse-reporter@latest --save-dev
64
+ # or
65
+ yarn add @arghajit/playwright-pulse-reporter@latest --dev
66
+ # or
67
+ pnpm add @arghajit/playwright-pulse-reporter@latest --save-dev
68
+ ```
69
+
70
+ ### 2. Configure Playwright
71
+
72
+ ```typescript
73
+ // playwright.config.ts
74
+ import { defineConfig } from '@playwright/test';
75
+ import * as path from 'path';
76
+
77
+
78
+ const PULSE_REPORT_DIR = path.resolve(__dirname, 'pulse-report');
79
+
80
+ export default defineConfig({
81
+ reporter: [
82
+ ['list'],
83
+ ['@arghajit/playwright-pulse-reporter', {
84
+ outputDir: PULSE_REPORT_DIR
85
+ }]
86
+ ],
87
+ // Other configurations...
88
+ });
89
+ ```
90
+
91
+ ### 3. Generate Reports
92
+
93
+ After running tests:
94
+
95
+ ```bash
96
+ npx generate-pulse-report # Generates static HTML
97
+ npx send-email # Sends email report
98
+ ```
99
+
100
+ ## 📊 Report Options
101
+
102
+ ### Option 1: Static HTML Report
103
+
104
+ ```bash
105
+ npx generate-pulse-report
106
+ ```
107
+
108
+ - Generates `playwright-pulse-static-report.html`
109
+ - Self-contained, no server required
110
+ - Preserves all dashboard functionality
111
+
112
+ ### Option 2: Email Report
113
+
114
+ 1. Configure `.env`:
115
+
116
+ ```bash
117
+ SENDER_EMAIL_1=recipient1@example.com
118
+ SENDER_EMAIL_2=recipient2@example.com
119
+ # ... up to 5 recipients
120
+ ```
121
+
122
+ 2. Send report:
123
+
124
+ ```bash
125
+ npx send-email
126
+ ```
127
+
128
+ ## 🤖 AI Analysis
129
+
130
+ The dashboard includes AI-powered test analysis that provides:
131
+
132
+ - Test flakiness detection
133
+ - Performance bottlenecks
134
+ - Failure pattern recognition
135
+ - Suggested optimizations
136
+
137
+ ## ⚙️ CI/CD Integration
138
+
139
+ ### Basic Workflow
140
+
141
+ ```yaml
142
+ # Upload Pulse report from each shard (per matrix.config.type)
143
+ - name: Upload Pulse Report results
144
+ if: success() || failure()
145
+ uses: actions/upload-artifact@v4
146
+ with:
147
+ name: pulse-report
148
+ path: pulse-report/
149
+
150
+ # Download all pulse-report-* artifacts after all shards complete
151
+ - name: Download Pulse Report artifacts
152
+ uses: actions/download-artifact@v4
153
+ with:
154
+ pattern: pulse-report
155
+ path: downloaded-artifacts
156
+
157
+ # Merge all sharded JSON reports into one final output
158
+ - name: Generate Pulse Report
159
+ run: |
160
+ npm run script merge-report
161
+ npm run script generate-report
162
+
163
+ # Upload final merged report as CI artifact
164
+ - name: Upload Pulse report
165
+ uses: actions/upload-artifact@v4
166
+ with:
167
+ name: pulse-report
168
+ ```
169
+
170
+ ### Sharded Workflow
171
+
172
+ ```yaml
173
+ # Upload Pulse report from each shard (per matrix.config.type)
174
+ - name: Upload Pulse Report results
175
+ if: success() || failure()
176
+ uses: actions/upload-artifact@v4
177
+ with:
178
+ name: pulse-report-${{ matrix.config.type }}
179
+ path: pulse-report/
180
+
181
+ # Download all pulse-report-* artifacts after all shards complete
182
+ - name: Download Pulse Report artifacts
183
+ uses: actions/download-artifact@v4
184
+ with:
185
+ pattern: pulse-report-*
186
+ path: downloaded-artifacts
187
+
188
+ # Organize reports into a single folder and rename for merging
189
+ - name: Organize Pulse Report
190
+ run: |
191
+ mkdir -p pulse-report
192
+ for dir in downloaded-artifacts/pulse-report-*; do
193
+ config_type=$(basename "$dir" | sed 's/pulse-report-//')
194
+ cp -r "$dir/attachments" "pulse-report/attachments"
195
+ cp "$dir/playwright-pulse-report.json" "pulse-report/playwright-pulse-report-${config_type}.json"
196
+ done
197
+
198
+ # Merge all sharded JSON reports into one final output
199
+ - name: Generate Pulse Report
200
+ run: |
201
+ npm run script merge-report
202
+ npm run script generate-report
203
+
204
+ # Upload final merged report as CI artifact
205
+ - name: Upload Pulse report
206
+ uses: actions/upload-artifact@v4
207
+ with:
208
+ name: pulse-report
209
+ path: pulse-report/
210
+
211
+ ```
212
+
213
+ ## 🧠 Notes
214
+
215
+ - Each shard generates its own playwright-pulse-report.json inside pulse-report/.
216
+ - Artifacts are named using the shard type (matrix.config.type).
217
+ - After the test matrix completes, reports are downloaded, renamed, and merged.
218
+ - 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
+
221
+ ## 📂 Key Files
222
+
223
+ ```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
235
+ ```
236
+
237
+ ## 🎉 What's New in v0.2.1
238
+
239
+ ### ✨ **Key Improvements**
240
+
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 |
246
+
247
+ ### 🚀 **Upgrade Now**
248
+
249
+ ```bash
250
+ npm install @arghajit/playwright-pulse-report@latest
251
+ ```
252
+
253
+ ## 📬 Support
254
+
255
+ For issues or feature requests, please [Contact Me](mailto:arghajitsingha47@gmail.com).
256
+
257
+ ---
258
+
259
+ <div align="center">Made by Arghajit Singha | MIT Licensed</div>
@@ -0,0 +1,5 @@
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 ADDED
@@ -0,0 +1,26 @@
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);
@@ -0,0 +1,8 @@
1
+ import type { TestResult, TestRun } from '@/types';
2
+ export interface PlaywrightPulseReport {
3
+ run: TestRun | null;
4
+ results: TestResult[];
5
+ metadata: {
6
+ generatedAt: string;
7
+ };
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,26 @@
1
+ import type { FullConfig, FullResult, Reporter, Suite, TestCase, TestResult as PwTestResult } from "@playwright/test/reporter";
2
+ export declare class PlaywrightPulseReporter implements Reporter {
3
+ private config;
4
+ private suite;
5
+ private results;
6
+ private runStartTime;
7
+ private outputDir;
8
+ private baseOutputFile;
9
+ private isSharded;
10
+ private shardIndex;
11
+ constructor(options?: {
12
+ outputFile?: string;
13
+ outputDir?: string;
14
+ });
15
+ printsToStdio(): boolean;
16
+ onBegin(config: FullConfig, suite: Suite): void;
17
+ onTestBegin(test: TestCase): void;
18
+ private processStep;
19
+ onTestEnd(test: TestCase, result: PwTestResult): void;
20
+ onError(error: any): void;
21
+ private _writeShardResults;
22
+ private _mergeShardResults;
23
+ private _cleanupTemporaryFiles;
24
+ private _ensureDirExists;
25
+ onEnd(result: FullResult): Promise<void>;
26
+ }
@@ -0,0 +1,304 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PlaywrightPulseReporter = void 0;
37
+ const fs = __importStar(require("fs/promises"));
38
+ const path = __importStar(require("path"));
39
+ // Helper to convert Playwright status to Pulse status
40
+ const convertStatus = (status) => {
41
+ if (status === "passed")
42
+ return "passed";
43
+ if (status === "failed" || status === "timedOut" || status === "interrupted")
44
+ return "failed";
45
+ return "skipped";
46
+ };
47
+ const TEMP_SHARD_FILE_PREFIX = ".pulse-shard-results-";
48
+ // Use standard ES module export
49
+ class PlaywrightPulseReporter {
50
+ constructor(options = {}) {
51
+ var _a;
52
+ this.results = []; // Holds results *per process* (main or shard)
53
+ this.baseOutputFile = "playwright-pulse-report.json";
54
+ this.isSharded = false;
55
+ this.shardIndex = undefined;
56
+ this.baseOutputFile = (_a = options.outputFile) !== null && _a !== void 0 ? _a : this.baseOutputFile;
57
+ // Initial outputDir setup (will be refined in onBegin)
58
+ const baseDir = options.outputDir
59
+ ? path.resolve(options.outputDir)
60
+ : process.cwd();
61
+ this.outputDir = baseDir;
62
+ console.log(`PlaywrightPulseReporter: Initial Output dir configured to ${this.outputDir}`);
63
+ }
64
+ printsToStdio() {
65
+ return this.shardIndex === undefined || this.shardIndex === 0;
66
+ }
67
+ onBegin(config, suite) {
68
+ this.config = config;
69
+ this.suite = suite;
70
+ this.runStartTime = Date.now();
71
+ const totalShards = parseInt(process.env.PLAYWRIGHT_SHARD_TOTAL || "1", 10);
72
+ this.isSharded = totalShards > 1;
73
+ if (process.env.PLAYWRIGHT_SHARD_INDEX !== undefined) {
74
+ this.shardIndex = parseInt(process.env.PLAYWRIGHT_SHARD_INDEX, 10);
75
+ }
76
+ const configDir = this.config.rootDir;
77
+ this.outputDir = this.outputDir
78
+ ? path.resolve(configDir, this.outputDir)
79
+ : path.resolve(configDir, "pulse-report");
80
+ console.log(`PlaywrightPulseReporter: Final Output dir resolved to ${this.outputDir}`);
81
+ if (this.shardIndex === undefined) {
82
+ // Main process
83
+ console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Outputting to ${this.outputDir}`);
84
+ this._cleanupTemporaryFiles().catch((err) => console.error("Pulse Reporter: Error cleaning up temp files:", err));
85
+ }
86
+ else {
87
+ // Shard process
88
+ console.log(`PlaywrightPulseReporter: Shard ${this.shardIndex + 1}/${totalShards} starting. Outputting temp results to ${this.outputDir}`);
89
+ }
90
+ }
91
+ onTestBegin(test) {
92
+ // Optional: Log test start
93
+ }
94
+ processStep(step, parentStatus) {
95
+ var _a;
96
+ const inherentStatus = parentStatus === "failed" || parentStatus === "skipped"
97
+ ? parentStatus
98
+ : convertStatus(step.error ? "failed" : "passed");
99
+ const duration = step.duration;
100
+ const startTime = new Date(step.startTime);
101
+ const endTime = new Date(startTime.getTime() + Math.max(0, duration));
102
+ return {
103
+ id: `${step.title}-${startTime.toISOString()}-${duration}-${Math.random()
104
+ .toString(16)
105
+ .slice(2)}`,
106
+ title: step.title,
107
+ status: inherentStatus,
108
+ duration: duration,
109
+ startTime: startTime,
110
+ endTime: endTime,
111
+ errorMessage: (_a = step.error) === null || _a === void 0 ? void 0 : _a.message,
112
+ screenshot: undefined, // Placeholder
113
+ };
114
+ }
115
+ onTestEnd(test, result) {
116
+ var _a, _b, _c, _d, _e;
117
+ const testStatus = convertStatus(result.status);
118
+ const startTime = new Date(result.startTime);
119
+ const endTime = new Date(startTime.getTime() + result.duration);
120
+ const processAllSteps = (steps, parentTestStatus) => {
121
+ let processed = [];
122
+ for (const step of steps) {
123
+ const processedStep = this.processStep(step, parentTestStatus);
124
+ processed.push(processedStep);
125
+ if (step.steps.length > 0) {
126
+ processed = processed.concat(processAllSteps(step.steps, processedStep.status));
127
+ }
128
+ }
129
+ return processed;
130
+ };
131
+ let codeSnippet = undefined;
132
+ try {
133
+ if ((_a = test.location) === null || _a === void 0 ? void 0 : _a.file) {
134
+ codeSnippet = `Test defined at: ${test.location.file}:${test.location.line}:${test.location.column}`;
135
+ }
136
+ }
137
+ catch (e) {
138
+ console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
139
+ }
140
+ const pulseResult = {
141
+ id: test.id ||
142
+ `${test.title}-${startTime.toISOString()}-${Math.random()
143
+ .toString(16)
144
+ .slice(2)}`,
145
+ runId: "TBD",
146
+ name: test.titlePath().join(" > "),
147
+ suiteName: test.parent.title,
148
+ status: testStatus,
149
+ duration: result.duration,
150
+ startTime: startTime,
151
+ endTime: endTime,
152
+ retries: result.retry,
153
+ steps: processAllSteps(result.steps, testStatus),
154
+ errorMessage: (_b = result.error) === null || _b === void 0 ? void 0 : _b.message,
155
+ stackTrace: (_c = result.error) === null || _c === void 0 ? void 0 : _c.stack,
156
+ codeSnippet: codeSnippet,
157
+ screenshot: (_d = result.attachments.find((a) => a.name === "screenshot")) === null || _d === void 0 ? void 0 : _d.path,
158
+ video: (_e = result.attachments.find((a) => a.name === "video")) === null || _e === void 0 ? void 0 : _e.path,
159
+ tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
160
+ };
161
+ this.results.push(pulseResult);
162
+ }
163
+ onError(error) {
164
+ var _a;
165
+ console.error(`PlaywrightPulseReporter: Error encountered (Shard: ${(_a = this.shardIndex) !== null && _a !== void 0 ? _a : "Main"}):`, error);
166
+ }
167
+ async _writeShardResults() {
168
+ if (this.shardIndex === undefined) {
169
+ console.warn("Pulse Reporter: _writeShardResults called in main process. Skipping.");
170
+ return;
171
+ }
172
+ const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${this.shardIndex}.json`);
173
+ try {
174
+ await this._ensureDirExists(this.outputDir);
175
+ await fs.writeFile(tempFilePath, JSON.stringify(this.results, null, 2));
176
+ }
177
+ catch (error) {
178
+ console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
179
+ }
180
+ }
181
+ async _mergeShardResults(finalRunData) {
182
+ console.log("Pulse Reporter: Merging results from shards...");
183
+ let allResults = [];
184
+ const totalShards = parseInt(process.env.PLAYWRIGHT_SHARD_TOTAL || "1", 10);
185
+ for (let i = 0; i < totalShards; i++) {
186
+ const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
187
+ try {
188
+ const content = await fs.readFile(tempFilePath, "utf-8");
189
+ const shardResults = JSON.parse(content);
190
+ shardResults.forEach((r) => (r.runId = finalRunData.id));
191
+ allResults = allResults.concat(shardResults);
192
+ }
193
+ catch (error) {
194
+ if (error && error.code === "ENOENT") {
195
+ console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}.`);
196
+ }
197
+ else {
198
+ console.warn(`Pulse Reporter: Could not read or parse results from shard ${i} (${tempFilePath}). Error: ${error}`);
199
+ }
200
+ }
201
+ }
202
+ console.log(`Pulse Reporter: Merged a total of ${allResults.length} results from ${totalShards} shards.`);
203
+ finalRunData.passed = allResults.filter((r) => r.status === "passed").length;
204
+ finalRunData.failed = allResults.filter((r) => r.status === "failed").length;
205
+ finalRunData.skipped = allResults.filter((r) => r.status === "skipped").length;
206
+ finalRunData.totalTests = allResults.length;
207
+ return {
208
+ run: finalRunData,
209
+ results: allResults,
210
+ metadata: { generatedAt: new Date().toISOString() },
211
+ };
212
+ }
213
+ async _cleanupTemporaryFiles() {
214
+ try {
215
+ await this._ensureDirExists(this.outputDir);
216
+ const files = await fs.readdir(this.outputDir);
217
+ const tempFiles = files.filter((f) => f.startsWith(TEMP_SHARD_FILE_PREFIX));
218
+ if (tempFiles.length > 0) {
219
+ console.log(`Pulse Reporter: Cleaning up ${tempFiles.length} temporary shard files...`);
220
+ await Promise.all(tempFiles.map((f) => fs.unlink(path.join(this.outputDir, f))));
221
+ }
222
+ }
223
+ catch (error) {
224
+ if (error && error.code !== "ENOENT") {
225
+ console.error("Pulse Reporter: Error cleaning up temporary files:", error);
226
+ }
227
+ }
228
+ }
229
+ async _ensureDirExists(dirPath) {
230
+ try {
231
+ await fs.mkdir(dirPath, { recursive: true });
232
+ }
233
+ catch (error) {
234
+ if (error && error.code !== "EEXIST") {
235
+ console.error(`Pulse Reporter: Failed to ensure directory exists: ${dirPath}`, error);
236
+ throw error;
237
+ }
238
+ }
239
+ }
240
+ async onEnd(result) {
241
+ var _a, _b, _c, _d, _e, _f;
242
+ if (this.shardIndex !== undefined) {
243
+ await this._writeShardResults();
244
+ console.log(`PlaywrightPulseReporter: Shard ${this.shardIndex + 1} finished.`);
245
+ return;
246
+ }
247
+ const runEndTime = Date.now();
248
+ const duration = runEndTime - this.runStartTime;
249
+ const runId = `run-${this.runStartTime}-${Math.random()
250
+ .toString(16)
251
+ .slice(2)}`;
252
+ const runData = {
253
+ id: runId,
254
+ timestamp: new Date(this.runStartTime),
255
+ totalTests: 0,
256
+ passed: 0,
257
+ failed: 0,
258
+ skipped: 0,
259
+ duration,
260
+ };
261
+ let finalReport;
262
+ if (this.isSharded) {
263
+ finalReport = await this._mergeShardResults(runData);
264
+ }
265
+ else {
266
+ this.results.forEach((r) => (r.runId = runId));
267
+ runData.passed = this.results.filter((r) => r.status === "passed").length;
268
+ runData.failed = this.results.filter((r) => r.status === "failed").length;
269
+ runData.skipped = this.results.filter((r) => r.status === "skipped").length;
270
+ runData.totalTests = this.results.length;
271
+ finalReport = {
272
+ run: runData,
273
+ results: this.results,
274
+ metadata: { generatedAt: new Date().toISOString() },
275
+ };
276
+ }
277
+ const finalRunStatus = ((_b = (_a = finalReport.run) === null || _a === void 0 ? void 0 : _a.failed) !== null && _b !== void 0 ? _b : 0 > 0) ? "failed" : "passed";
278
+ console.log(`PlaywrightPulseReporter: Test run finished with overall status: ${finalRunStatus}`);
279
+ console.log(` Passed: ${(_c = finalReport.run) === null || _c === void 0 ? void 0 : _c.passed}, Failed: ${(_d = finalReport.run) === null || _d === void 0 ? void 0 : _d.failed}, Skipped: ${(_e = finalReport.run) === null || _e === void 0 ? void 0 : _e.skipped}`);
280
+ console.log(` Total tests: ${(_f = finalReport.run) === null || _f === void 0 ? void 0 : _f.totalTests}`);
281
+ console.log(` Total time: ${(duration / 1000).toFixed(2)}s`);
282
+ const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
283
+ try {
284
+ await this._ensureDirExists(this.outputDir);
285
+ await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
286
+ if (value instanceof Date) {
287
+ return value.toISOString();
288
+ }
289
+ return value;
290
+ }, 2));
291
+ console.log(`PlaywrightPulseReporter: Final report written to ${finalOutputPath}`);
292
+ }
293
+ catch (error) {
294
+ console.error(`PlaywrightPulseReporter: Failed to write final report to ${finalOutputPath}`, error);
295
+ }
296
+ finally {
297
+ if (this.isSharded) {
298
+ await this._cleanupTemporaryFiles();
299
+ }
300
+ }
301
+ }
302
+ }
303
+ exports.PlaywrightPulseReporter = PlaywrightPulseReporter;
304
+ // No module.exports needed for ES modules
@@ -0,0 +1,10 @@
1
+ import type { TestResult as PwTestResult } from "@playwright/test/reporter";
2
+ import type { TestResult, PlaywrightPulseReporterOptions } from "../types";
3
+ /**
4
+ * Processes attachments from a Playwright TestResult and updates the PulseTestResult.
5
+ * @param testId A unique identifier for the test, used for folder naming.
6
+ * @param pwResult The TestResult object from Playwright.
7
+ * @param pulseResult The internal test result structure to update.
8
+ * @param config The reporter configuration options.
9
+ */
10
+ export declare function attachFiles(testId: string, pwResult: PwTestResult, pulseResult: TestResult, config: PlaywrightPulseReporterOptions): void;