@arghajit/dummy 0.3.16 → 0.3.18
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 +93 -85
- package/dist/reporter/playwright-pulse-reporter.d.ts +1 -0
- package/dist/reporter/playwright-pulse-reporter.js +46 -13
- package/dist/types/index.d.ts +3 -1
- package/package.json +4 -2
- package/scripts/generate-email-report.mjs +1 -1
- package/scripts/generate-report.mjs +409 -256
- package/scripts/generate-static-report.mjs +425 -219
- package/scripts/merge-pulse-report.js +6 -1
- package/dist/reporter/tsconfig.reporter.tsbuildinfo +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ _The ultimate Playwright reporter — Interactive dashboard with historical tren
|
|
|
10
10
|
|
|
11
11
|
## [Live Demo](https://arghajit47.github.io/playwright-pulse/demo.html)
|
|
12
12
|
|
|
13
|
-
## 
|
|
13
|
+
## 
|
|
14
14
|
|
|
15
15
|
## **Documentation**: [Pulse Report](https://arghajit47.github.io/playwright-pulse/)
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@ The project provides these utility commands:
|
|
|
21
21
|
| Command | Description |
|
|
22
22
|
|------------------------|-----------------------------------------------------------------------------|
|
|
23
23
|
| `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 |
|
|
24
|
-
| `generate-pulse-report`| Generates `playwright-pulse-static-report.html`, Self-contained, no server required, Preserves all dashboard functionality, all the attachments are
|
|
24
|
+
| `generate-pulse-report`| Generates `playwright-pulse-static-report.html`, Self-contained, no server required, Preserves all dashboard functionality, all the attachments are embedded in the report, no need to have attachments/ directory when viewing the report, with a dark theme and better initial load handling |
|
|
25
25
|
| `merge-pulse-report` | Combines multiple parallel test json reports, basically used in sharding |
|
|
26
26
|
| `generate-trend` | Analyzes historical trends in test results |
|
|
27
27
|
| `generate-email-report`| Generates email-friendly report versions |
|
|
@@ -101,24 +101,7 @@ npx merge-pulse-report --outputDir {YOUR_CUSTOM_REPORT_FOLDER}
|
|
|
101
101
|
|
|
102
102
|
**Important:** Make sure your `playwright.config.ts` custom directory matches the CLI script:
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
import { defineConfig } from "@playwright/test";
|
|
106
|
-
import * as path from "path";
|
|
107
|
-
|
|
108
|
-
const CUSTOM_REPORT_DIR = path.resolve(__dirname, "{YOUR_CUSTOM_REPORT_FOLDER}");
|
|
109
|
-
|
|
110
|
-
export default defineConfig({
|
|
111
|
-
reporter: [
|
|
112
|
-
["list"],
|
|
113
|
-
[
|
|
114
|
-
"@arghajit/playwright-pulse-report",
|
|
115
|
-
{
|
|
116
|
-
outputDir: CUSTOM_REPORT_DIR, // Must match CLI --outputDir
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
],
|
|
120
|
-
});
|
|
121
|
-
```
|
|
104
|
+

|
|
122
105
|
|
|
123
106
|
## 📊 Report Options
|
|
124
107
|
|
|
@@ -240,76 +223,100 @@ Under the hood, this will:
|
|
|
240
223
|
### Basic Workflow
|
|
241
224
|
|
|
242
225
|
```yaml
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
- name:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
226
|
+
# .github/workflows/playwright.yml
|
|
227
|
+
name: Playwright Tests
|
|
228
|
+
on:
|
|
229
|
+
push:
|
|
230
|
+
branches: [ main, master ]
|
|
231
|
+
pull_request:
|
|
232
|
+
branches: [ main, master ]
|
|
233
|
+
jobs:
|
|
234
|
+
test:
|
|
235
|
+
timeout-minutes: 60
|
|
236
|
+
runs-on: ubuntu-latest
|
|
237
|
+
steps:
|
|
238
|
+
- uses: actions/checkout@v4
|
|
239
|
+
- uses: actions/setup-node@v4
|
|
240
|
+
with:
|
|
241
|
+
node-version: lts/*
|
|
242
|
+
- name: Install dependencies
|
|
243
|
+
run: npm ci
|
|
244
|
+
- name: Install Playwright Browsers
|
|
245
|
+
run: npx playwright install --with-deps
|
|
246
|
+
- name: Run Playwright tests
|
|
247
|
+
run: npm run test
|
|
248
|
+
- uses: actions/upload-artifact@v4
|
|
249
|
+
if: always()
|
|
250
|
+
with:
|
|
251
|
+
name: playwright-report
|
|
252
|
+
path: playwright-report/
|
|
253
|
+
retention-days: 30
|
|
269
254
|
```
|
|
270
255
|
|
|
256
|
+
For more details, please refer to the [Pulse Report Basic CI/CD Integration](https://arghajit47.github.io/playwright-pulse/advanced-usage.html).
|
|
257
|
+
|
|
271
258
|
### Sharded Workflow
|
|
272
259
|
|
|
273
260
|
```yaml
|
|
274
|
-
#
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
261
|
+
# .github/workflows/playwright.yml
|
|
262
|
+
name: Playwright Tests with Pulse Report
|
|
263
|
+
on: [push]
|
|
264
|
+
jobs:
|
|
265
|
+
test:
|
|
266
|
+
timeout-minutes: 60
|
|
267
|
+
runs-on: ubuntu-latest
|
|
268
|
+
strategy:
|
|
269
|
+
fail-fast: false
|
|
270
|
+
matrix:
|
|
271
|
+
shard: [1, 2, 3, 4]
|
|
272
|
+
steps:
|
|
273
|
+
- uses: actions/checkout@v4
|
|
274
|
+
- uses: actions/setup-node@v4
|
|
275
|
+
with:
|
|
276
|
+
node-version: 18
|
|
277
|
+
- run: npm ci
|
|
278
|
+
- run: npx playwright install --with-deps
|
|
279
|
+
- run: npx playwright test --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
|
280
|
+
- uses: actions/upload-artifact@v4
|
|
281
|
+
if: always()
|
|
282
|
+
with:
|
|
283
|
+
name: pulse-report-shard-${{ matrix.shard }}
|
|
284
|
+
path: pulse-report/
|
|
285
|
+
retention-days: 1
|
|
286
|
+
|
|
287
|
+
merge-report:
|
|
288
|
+
needs: test
|
|
289
|
+
if: always()
|
|
290
|
+
runs-on: ubuntu-latest
|
|
291
|
+
steps:
|
|
292
|
+
- uses: actions/checkout@v4
|
|
293
|
+
- uses: actions/setup-node@v4
|
|
294
|
+
with:
|
|
295
|
+
node-version: 18
|
|
296
|
+
- run: npm ci
|
|
297
|
+
|
|
298
|
+
# Download all shard artifacts to a single directory
|
|
299
|
+
- uses: actions/download-artifact@v4
|
|
300
|
+
with:
|
|
301
|
+
path: all-reports
|
|
302
|
+
pattern: pulse-report-shard-*
|
|
303
|
+
|
|
304
|
+
# Merge all shard reports into a single report
|
|
305
|
+
- run: npx merge-pulse-report -o all-reports
|
|
306
|
+
|
|
307
|
+
# Generate the final HTML report
|
|
308
|
+
- run: npx generate-pulse-report -o all-reports
|
|
309
|
+
|
|
310
|
+
# Upload the final merged report
|
|
311
|
+
- uses: actions/upload-artifact@v4
|
|
312
|
+
with:
|
|
313
|
+
name: final-playwright-pulse-report
|
|
314
|
+
path: all-reports/
|
|
315
|
+
retention-days: 7
|
|
311
316
|
```
|
|
312
317
|
|
|
318
|
+
For more details, please refer to the [Pulse Report Sharded CI/CD Integration](https://arghajit47.github.io/playwright-pulse/sharding.html).
|
|
319
|
+
|
|
313
320
|
## 🧠 Notes
|
|
314
321
|
|
|
315
322
|
- <strong>`npm run generate-report` generates a HTML report ( screenshots/images will be taken in realtime from 'attachments/' directory ).</strong>
|
|
@@ -319,13 +326,14 @@ Under the hood, this will:
|
|
|
319
326
|
- After the test matrix completes, reports are downloaded, renamed, and merged.
|
|
320
327
|
- merge-report is a custom Node.js script that combines all JSON files into one.
|
|
321
328
|
|
|
322
|
-
## 
|
|
323
330
|
|
|
324
331
|
### 🚀 **Upgrade Now**
|
|
325
332
|
|
|
326
333
|
```bash
|
|
327
334
|
npm install @arghajit/playwright-pulse-report@latest
|
|
328
335
|
```
|
|
336
|
+
|
|
329
337
|
---
|
|
330
338
|
|
|
331
339
|
## ⚙️ Advanced Configuration
|
|
@@ -340,7 +348,7 @@ npx playwright test test1.spec.ts && npx playwright test test2.spec.ts
|
|
|
340
348
|
|
|
341
349
|
By default, In this above scenario, the report from test1 will be lost. To solve this, you can use the resetOnEachRun option.
|
|
342
350
|
|
|
343
|
-
```
|
|
351
|
+
```javascript
|
|
344
352
|
// playwright.config.ts
|
|
345
353
|
import { defineConfig } from "@playwright/test";
|
|
346
354
|
import * as path from "path";
|
|
@@ -17,6 +17,7 @@ export declare class PlaywrightPulseReporter implements Reporter {
|
|
|
17
17
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
18
18
|
onTestBegin(test: TestCase): void;
|
|
19
19
|
private _getSeverity;
|
|
20
|
+
private extractCodeSnippet;
|
|
20
21
|
private getBrowserDetails;
|
|
21
22
|
private processStep;
|
|
22
23
|
onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
|
|
@@ -116,6 +116,30 @@ class PlaywrightPulseReporter {
|
|
|
116
116
|
const severityAnnotation = annotations.find((a) => a.type === "pulse_severity");
|
|
117
117
|
return (severityAnnotation === null || severityAnnotation === void 0 ? void 0 : severityAnnotation.description) || "Medium";
|
|
118
118
|
}
|
|
119
|
+
async extractCodeSnippet(filePath, targetLine, targetColumn) {
|
|
120
|
+
try {
|
|
121
|
+
const fileContent = await fs.readFile(filePath, "utf-8");
|
|
122
|
+
const lines = fileContent.split("\n");
|
|
123
|
+
const contextLines = 1;
|
|
124
|
+
const startLine = Math.max(0, targetLine - contextLines - 1);
|
|
125
|
+
const endLine = Math.min(lines.length, targetLine + contextLines);
|
|
126
|
+
const snippetLines = [];
|
|
127
|
+
for (let i = startLine; i < endLine; i++) {
|
|
128
|
+
const lineNum = i + 1;
|
|
129
|
+
const isTargetLine = lineNum === targetLine;
|
|
130
|
+
const lineContent = lines[i] || "";
|
|
131
|
+
snippetLines.push(`${lineNum.toString().padStart(4)} | ${lineContent}`);
|
|
132
|
+
if (isTargetLine) {
|
|
133
|
+
const pointer = " ".repeat(4) + " | " + " ".repeat(targetColumn) + "^";
|
|
134
|
+
snippetLines.push(pointer);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return snippetLines.join("\n");
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
119
143
|
getBrowserDetails(test) {
|
|
120
144
|
var _a, _b, _c, _d;
|
|
121
145
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
@@ -166,7 +190,7 @@ class PlaywrightPulseReporter {
|
|
|
166
190
|
}
|
|
167
191
|
return finalString.trim();
|
|
168
192
|
}
|
|
169
|
-
async processStep(step, testId, browserDetails, testCase) {
|
|
193
|
+
async processStep(step, testId, browserDetails, testCase, isFailedStep = false) {
|
|
170
194
|
var _a, _b, _c, _d;
|
|
171
195
|
let stepStatus = "passed";
|
|
172
196
|
let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
|
|
@@ -180,8 +204,10 @@ class PlaywrightPulseReporter {
|
|
|
180
204
|
const startTime = new Date(step.startTime);
|
|
181
205
|
const endTime = new Date(startTime.getTime() + Math.max(0, duration));
|
|
182
206
|
let codeLocation = "";
|
|
207
|
+
let codeSnippet = undefined;
|
|
183
208
|
if (step.location) {
|
|
184
209
|
codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
|
|
210
|
+
codeSnippet = await this.extractCodeSnippet(step.location.file, step.location.line, step.location.column);
|
|
185
211
|
}
|
|
186
212
|
return {
|
|
187
213
|
id: `${testId}_step_${startTime.toISOString()}-${duration}-${(0, crypto_1.randomUUID)()}`,
|
|
@@ -194,29 +220,36 @@ class PlaywrightPulseReporter {
|
|
|
194
220
|
errorMessage: errorMessage,
|
|
195
221
|
stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
|
|
196
222
|
codeLocation: codeLocation || undefined,
|
|
223
|
+
codeSnippet: codeSnippet,
|
|
197
224
|
isHook: step.category === "hook",
|
|
198
225
|
hookType: step.category === "hook"
|
|
199
226
|
? step.title.toLowerCase().includes("before")
|
|
200
227
|
? "before"
|
|
201
228
|
: "after"
|
|
202
229
|
: undefined,
|
|
230
|
+
isFailedStep: isFailedStep,
|
|
203
231
|
steps: [],
|
|
204
232
|
};
|
|
205
233
|
}
|
|
206
234
|
async onTestEnd(test, result) {
|
|
207
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p
|
|
235
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
208
236
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
209
237
|
const browserDetails = this.getBrowserDetails(test);
|
|
210
238
|
const testStatus = convertStatus(result.status, test);
|
|
211
239
|
const startTime = new Date(result.startTime);
|
|
212
240
|
const endTime = new Date(startTime.getTime() + result.duration);
|
|
213
|
-
const processAllSteps = async (steps) => {
|
|
241
|
+
const processAllSteps = async (steps, parentFailed = false) => {
|
|
214
242
|
let processed = [];
|
|
243
|
+
let foundFailedStep = false;
|
|
215
244
|
for (const step of steps) {
|
|
216
|
-
const
|
|
245
|
+
const isThisStepFailed = !foundFailedStep && (step.error !== undefined || (testStatus === "failed" && !foundFailedStep));
|
|
246
|
+
if (isThisStepFailed) {
|
|
247
|
+
foundFailedStep = true;
|
|
248
|
+
}
|
|
249
|
+
const processedStep = await this.processStep(step, test.id, browserDetails, test, isThisStepFailed);
|
|
217
250
|
processed.push(processedStep);
|
|
218
251
|
if (step.steps && step.steps.length > 0) {
|
|
219
|
-
processedStep.steps = await processAllSteps(step.steps);
|
|
252
|
+
processedStep.steps = await processAllSteps(step.steps, isThisStepFailed);
|
|
220
253
|
}
|
|
221
254
|
}
|
|
222
255
|
return processed;
|
|
@@ -268,10 +301,10 @@ class PlaywrightPulseReporter {
|
|
|
268
301
|
endTime: endTime,
|
|
269
302
|
browser: browserDetails,
|
|
270
303
|
retries: result.retry,
|
|
271
|
-
steps:
|
|
272
|
-
errorMessage: (
|
|
273
|
-
stackTrace: (
|
|
274
|
-
snippet: (
|
|
304
|
+
steps: result.steps ? await processAllSteps(result.steps) : [],
|
|
305
|
+
errorMessage: (_h = result.error) === null || _h === void 0 ? void 0 : _h.message,
|
|
306
|
+
stackTrace: (_j = result.error) === null || _j === void 0 ? void 0 : _j.stack,
|
|
307
|
+
snippet: (_k = result.error) === null || _k === void 0 ? void 0 : _k.snippet,
|
|
275
308
|
codeSnippet: codeSnippet,
|
|
276
309
|
tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
|
|
277
310
|
severity: this._getSeverity(test.annotations),
|
|
@@ -281,7 +314,7 @@ class PlaywrightPulseReporter {
|
|
|
281
314
|
attachments: [],
|
|
282
315
|
stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
|
|
283
316
|
stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
|
|
284
|
-
annotations: ((
|
|
317
|
+
annotations: ((_l = test.annotations) === null || _l === void 0 ? void 0 : _l.length) > 0 ? test.annotations : undefined,
|
|
285
318
|
...testSpecificData,
|
|
286
319
|
};
|
|
287
320
|
for (const [index, attachment] of result.attachments.entries()) {
|
|
@@ -298,16 +331,16 @@ class PlaywrightPulseReporter {
|
|
|
298
331
|
await this._ensureDirExists(path.dirname(absoluteDestPath));
|
|
299
332
|
await fs.copyFile(attachment.path, absoluteDestPath);
|
|
300
333
|
if (attachment.contentType.startsWith("image/")) {
|
|
301
|
-
(
|
|
334
|
+
(_m = pulseResult.screenshots) === null || _m === void 0 ? void 0 : _m.push(relativeDestPath);
|
|
302
335
|
}
|
|
303
336
|
else if (attachment.contentType.startsWith("video/")) {
|
|
304
|
-
(
|
|
337
|
+
(_o = pulseResult.videoPath) === null || _o === void 0 ? void 0 : _o.push(relativeDestPath);
|
|
305
338
|
}
|
|
306
339
|
else if (attachment.name === "trace") {
|
|
307
340
|
pulseResult.tracePath = relativeDestPath;
|
|
308
341
|
}
|
|
309
342
|
else {
|
|
310
|
-
(
|
|
343
|
+
(_p = pulseResult.attachments) === null || _p === void 0 ? void 0 : _p.push({
|
|
311
344
|
name: attachment.name,
|
|
312
345
|
path: relativeDestPath,
|
|
313
346
|
contentType: attachment.contentType,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export interface TestStep {
|
|
|
11
11
|
errorMessage?: string;
|
|
12
12
|
stackTrace?: string;
|
|
13
13
|
codeLocation?: string;
|
|
14
|
+
codeSnippet?: string;
|
|
14
15
|
isHook?: boolean;
|
|
15
16
|
hookType?: "before" | "after";
|
|
17
|
+
isFailedStep?: boolean;
|
|
16
18
|
steps?: TestStep[];
|
|
17
19
|
}
|
|
18
20
|
export interface TestResult {
|
|
@@ -67,7 +69,7 @@ export interface TestRun {
|
|
|
67
69
|
failed: number;
|
|
68
70
|
skipped: number;
|
|
69
71
|
duration: number;
|
|
70
|
-
environment?: EnvDetails;
|
|
72
|
+
environment?: EnvDetails | EnvDetails[];
|
|
71
73
|
}
|
|
72
74
|
export interface TrendDataPoint {
|
|
73
75
|
date: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arghajit/dummy",
|
|
3
3
|
"author": "Arghajit Singha",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.18",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"homepage": "https://arghajit47.github.io/playwright-pulse/",
|
|
7
7
|
"repository": {
|
|
@@ -87,6 +87,8 @@
|
|
|
87
87
|
"@playwright/test": ">=1.40.0"
|
|
88
88
|
},
|
|
89
89
|
"overrides": {
|
|
90
|
-
"glob": "^13.0.0"
|
|
90
|
+
"glob": "^13.0.0",
|
|
91
|
+
"minimatch": "^10.1.2",
|
|
92
|
+
"@isaacs/brace-expansion": "^5.0.1"
|
|
91
93
|
}
|
|
92
94
|
}
|
|
@@ -608,7 +608,7 @@ function generateMinifiedHTML(reportData) {
|
|
|
608
608
|
<footer class="report-footer">
|
|
609
609
|
<div style="display: inline-flex; align-items: center; gap: 0.5rem;">
|
|
610
610
|
<span>Created for</span>
|
|
611
|
-
<a href="https://playwright-pulse
|
|
611
|
+
<a href="https://arghajit47.github.io/playwright-pulse/" target="_blank" rel="noopener noreferrer">
|
|
612
612
|
Pulse Email Report
|
|
613
613
|
</a>
|
|
614
614
|
</div>
|