@arghajit/dummy 0.3.36 → 0.3.37
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.
|
@@ -4,6 +4,7 @@ export declare class PlaywrightPulseReporter implements Reporter {
|
|
|
4
4
|
private config;
|
|
5
5
|
private suite;
|
|
6
6
|
private results;
|
|
7
|
+
private _pendingTestEnds;
|
|
7
8
|
private runStartTime;
|
|
8
9
|
private options;
|
|
9
10
|
private outputDir;
|
|
@@ -21,12 +22,28 @@ export declare class PlaywrightPulseReporter implements Reporter {
|
|
|
21
22
|
private getBrowserDetails;
|
|
22
23
|
private processStep;
|
|
23
24
|
onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
|
|
25
|
+
private _processTestEnd;
|
|
24
26
|
private _getFinalizedResults;
|
|
25
27
|
onError(error: any): void;
|
|
26
28
|
private _getEnvDetails;
|
|
27
29
|
private _writeShardResults;
|
|
28
30
|
private _mergeShardResults;
|
|
29
31
|
private _cleanupTemporaryFiles;
|
|
32
|
+
/**
|
|
33
|
+
* Removes all individual run JSON files from the `pulse-results/` directory
|
|
34
|
+
* that were left over from previous test sessions.
|
|
35
|
+
*
|
|
36
|
+
* When `resetOnEachRun: false`, each run writes its own timestamped JSON to
|
|
37
|
+
* `pulse-results/` and then `_mergeAllRunReports()` merges them all. However,
|
|
38
|
+
* if files from *older* sessions accumulate there (e.g. because a previous run
|
|
39
|
+
* was interrupted before the post-merge cleanup, or because the user ran tests
|
|
40
|
+
* on a previous day), `_getFinalizedResults()` de-duplicates by `test.id` and
|
|
41
|
+
* collapses results from both sessions into a single entry — producing a
|
|
42
|
+
* `totalTests` count lower than the actual number of tests that ran.
|
|
43
|
+
*
|
|
44
|
+
* Cleaning up at `onBegin` time guarantees each run starts with a fresh slate.
|
|
45
|
+
*/
|
|
46
|
+
private _cleanupStaleRunReports;
|
|
30
47
|
private _ensureDirExists;
|
|
31
48
|
onEnd(result: FullResult): Promise<void>;
|
|
32
49
|
private _mergeAllRunReports;
|
|
@@ -69,6 +69,7 @@ class PlaywrightPulseReporter {
|
|
|
69
69
|
constructor(options = {}) {
|
|
70
70
|
var _a, _b, _c;
|
|
71
71
|
this.results = [];
|
|
72
|
+
this._pendingTestEnds = [];
|
|
72
73
|
this.baseOutputFile = "playwright-pulse-report.json";
|
|
73
74
|
this.isSharded = false;
|
|
74
75
|
this.shardIndex = undefined;
|
|
@@ -99,14 +100,21 @@ class PlaywrightPulseReporter {
|
|
|
99
100
|
? this.config.shard.current - 1
|
|
100
101
|
: undefined;
|
|
101
102
|
this._ensureDirExists(this.outputDir)
|
|
102
|
-
.then(() => {
|
|
103
|
+
.then(async () => {
|
|
103
104
|
if (this.printsToStdio()) {
|
|
104
105
|
console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
|
|
105
106
|
if (this.shardIndex === undefined ||
|
|
106
107
|
(this.isSharded && this.shardIndex === 0)) {
|
|
107
|
-
|
|
108
|
+
await this._cleanupTemporaryFiles();
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
// When not resetting on each run, clear stale individual run files
|
|
112
|
+
// from previous sessions. Without this, _mergeAllRunReports() reads
|
|
113
|
+
// ALL accumulated files and de-duplicates by test.id, which collapses
|
|
114
|
+
// results from different sessions into fewer entries than expected.
|
|
115
|
+
if (!this.resetOnEachRun) {
|
|
116
|
+
await this._cleanupStaleRunReports();
|
|
117
|
+
}
|
|
110
118
|
})
|
|
111
119
|
.catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
|
|
112
120
|
}
|
|
@@ -120,19 +128,19 @@ class PlaywrightPulseReporter {
|
|
|
120
128
|
extractCodeSnippet(filePath, targetLine, targetColumn) {
|
|
121
129
|
var _a;
|
|
122
130
|
try {
|
|
123
|
-
const fsSync = require(
|
|
131
|
+
const fsSync = require("fs");
|
|
124
132
|
if (!fsSync.existsSync(filePath)) {
|
|
125
|
-
return
|
|
133
|
+
return "";
|
|
126
134
|
}
|
|
127
|
-
const content = fsSync.readFileSync(filePath,
|
|
128
|
-
const lines = content.split(
|
|
135
|
+
const content = fsSync.readFileSync(filePath, "utf8");
|
|
136
|
+
const lines = content.split("\n");
|
|
129
137
|
if (targetLine < 1 || targetLine > lines.length) {
|
|
130
|
-
return
|
|
138
|
+
return "";
|
|
131
139
|
}
|
|
132
|
-
return ((_a = lines[targetLine - 1]) === null || _a === void 0 ? void 0 : _a.trim()) ||
|
|
140
|
+
return ((_a = lines[targetLine - 1]) === null || _a === void 0 ? void 0 : _a.trim()) || "";
|
|
133
141
|
}
|
|
134
142
|
catch (e) {
|
|
135
|
-
return
|
|
143
|
+
return "";
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
getBrowserDetails(test) {
|
|
@@ -199,7 +207,7 @@ class PlaywrightPulseReporter {
|
|
|
199
207
|
const startTime = new Date(step.startTime);
|
|
200
208
|
const endTime = new Date(startTime.getTime() + Math.max(0, duration));
|
|
201
209
|
let codeLocation = "";
|
|
202
|
-
let codeSnippet =
|
|
210
|
+
let codeSnippet = "";
|
|
203
211
|
if (step.location) {
|
|
204
212
|
codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
|
|
205
213
|
codeSnippet = this.extractCodeSnippet(step.location.file, step.location.line, step.location.column);
|
|
@@ -226,6 +234,15 @@ class PlaywrightPulseReporter {
|
|
|
226
234
|
};
|
|
227
235
|
}
|
|
228
236
|
async onTestEnd(test, result) {
|
|
237
|
+
// Track this async call so onEnd() can wait for all in-flight processing
|
|
238
|
+
// before it reads this.results. This prevents the race condition where
|
|
239
|
+
// onEnd() fires before an interrupted test's onTestEnd() has finished
|
|
240
|
+
// its async attachment I/O and pushed to this.results.
|
|
241
|
+
const p = this._processTestEnd(test, result);
|
|
242
|
+
this._pendingTestEnds.push(p);
|
|
243
|
+
await p;
|
|
244
|
+
}
|
|
245
|
+
async _processTestEnd(test, result) {
|
|
229
246
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
230
247
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
231
248
|
const browserDetails = this.getBrowserDetails(test);
|
|
@@ -240,8 +257,8 @@ class PlaywrightPulseReporter {
|
|
|
240
257
|
// Existing behavior: fail if flaky (implied by user request "existing status field should remain failed")
|
|
241
258
|
// If outcome is flaky, status should be 'failed' to indicate initial failure, but final_status is 'passed'
|
|
242
259
|
let testStatus = finalStatus;
|
|
243
|
-
if (outcome ===
|
|
244
|
-
testStatus =
|
|
260
|
+
if (outcome === "flaky") {
|
|
261
|
+
testStatus = "flaky";
|
|
245
262
|
}
|
|
246
263
|
const startTime = new Date(result.startTime);
|
|
247
264
|
const endTime = new Date(startTime.getTime() + result.duration);
|
|
@@ -256,7 +273,7 @@ class PlaywrightPulseReporter {
|
|
|
256
273
|
}
|
|
257
274
|
return processed;
|
|
258
275
|
};
|
|
259
|
-
let codeSnippet =
|
|
276
|
+
let codeSnippet = "";
|
|
260
277
|
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)) {
|
|
261
278
|
codeSnippet = this.extractCodeSnippet(test.location.file, test.location.line, test.location.column);
|
|
262
279
|
}
|
|
@@ -488,6 +505,39 @@ class PlaywrightPulseReporter {
|
|
|
488
505
|
}
|
|
489
506
|
}
|
|
490
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Removes all individual run JSON files from the `pulse-results/` directory
|
|
510
|
+
* that were left over from previous test sessions.
|
|
511
|
+
*
|
|
512
|
+
* When `resetOnEachRun: false`, each run writes its own timestamped JSON to
|
|
513
|
+
* `pulse-results/` and then `_mergeAllRunReports()` merges them all. However,
|
|
514
|
+
* if files from *older* sessions accumulate there (e.g. because a previous run
|
|
515
|
+
* was interrupted before the post-merge cleanup, or because the user ran tests
|
|
516
|
+
* on a previous day), `_getFinalizedResults()` de-duplicates by `test.id` and
|
|
517
|
+
* collapses results from both sessions into a single entry — producing a
|
|
518
|
+
* `totalTests` count lower than the actual number of tests that ran.
|
|
519
|
+
*
|
|
520
|
+
* Cleaning up at `onBegin` time guarantees each run starts with a fresh slate.
|
|
521
|
+
*/
|
|
522
|
+
async _cleanupStaleRunReports() {
|
|
523
|
+
const pulseResultsDir = path.join(this.outputDir, INDIVIDUAL_REPORTS_SUBDIR);
|
|
524
|
+
try {
|
|
525
|
+
const files = await fs.readdir(pulseResultsDir);
|
|
526
|
+
const staleFiles = files.filter((f) => f.startsWith("playwright-pulse-report-") && f.endsWith(".json"));
|
|
527
|
+
if (staleFiles.length > 0) {
|
|
528
|
+
await Promise.all(staleFiles.map((f) => fs.unlink(path.join(pulseResultsDir, f))));
|
|
529
|
+
if (this.printsToStdio()) {
|
|
530
|
+
console.log(`PlaywrightPulseReporter: Cleaned up ${staleFiles.length} stale run report(s) from ${pulseResultsDir}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
// ENOENT simply means no previous runs exist — that's fine
|
|
536
|
+
if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
|
|
537
|
+
console.warn("Pulse Reporter: Warning during cleanup of stale run reports:", error.message);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
491
541
|
async _ensureDirExists(dirPath) {
|
|
492
542
|
try {
|
|
493
543
|
await fs.mkdir(dirPath, { recursive: true });
|
|
@@ -500,6 +550,11 @@ class PlaywrightPulseReporter {
|
|
|
500
550
|
}
|
|
501
551
|
}
|
|
502
552
|
async onEnd(result) {
|
|
553
|
+
// Wait for ALL in-flight onTestEnd calls to finish before reading this.results.
|
|
554
|
+
// This guards against Playwright calling onEnd() concurrently with (or just
|
|
555
|
+
// before) the last onTestEnd() finishing its async attachment I/O — which
|
|
556
|
+
// would cause that test to be silently dropped from the report.
|
|
557
|
+
await Promise.allSettled(this._pendingTestEnds);
|
|
503
558
|
if (this.shardIndex !== undefined) {
|
|
504
559
|
await this._writeShardResults();
|
|
505
560
|
return;
|
|
@@ -660,6 +715,16 @@ class PlaywrightPulseReporter {
|
|
|
660
715
|
if (this.printsToStdio()) {
|
|
661
716
|
console.log(`PlaywrightPulseReporter: ✅ Merged report with ${finalMergedResults.length} total results saved to ${finalOutputPath}`);
|
|
662
717
|
}
|
|
718
|
+
// Clean up the pulse-results directory after a successful merge
|
|
719
|
+
try {
|
|
720
|
+
await fs.rm(pulseResultsDir, { recursive: true, force: true });
|
|
721
|
+
if (this.printsToStdio()) {
|
|
722
|
+
console.log(`PlaywrightPulseReporter: Cleaned up individual reports directory at ${pulseResultsDir}`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
catch (cleanupErr) {
|
|
726
|
+
console.warn(`Pulse Reporter: Could not clean up individual reports directory. Error: ${cleanupErr.message}`);
|
|
727
|
+
}
|
|
663
728
|
}
|
|
664
729
|
catch (err) {
|
|
665
730
|
console.error(`Pulse Reporter: Failed to write final merged report to ${finalOutputPath}. Error: ${err.message}`);
|
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.37",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"homepage": "https://arghajit47.github.io/playwright-pulse/",
|
|
7
7
|
"repository": {
|
|
@@ -98,4 +98,4 @@
|
|
|
98
98
|
"safe-buffer": "^5.2.1",
|
|
99
99
|
"string_decoder": "^1.3.0"
|
|
100
100
|
}
|
|
101
|
-
}
|
|
101
|
+
}
|