@empiricalrun/playwright-utils 0.50.1 → 0.52.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.52.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 73b88a2: fix: relative attachment for pw-utils
8
+
9
+ ## 0.51.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 7584f64: fix: incremental blob reporter does not kill process group
14
+
3
15
  ## 0.50.1
4
16
 
5
17
  ### Patch Changes
@@ -89,7 +89,7 @@ class AttachmentCleanup {
89
89
  _onEvict = null;
90
90
  constructor(limitBytes = getDefaultDiskLimitBytes()) {
91
91
  this._limitBytes = limitBytes;
92
- logger_1.logger.info(`[AttachmentCleanup] Initialized with limit: ${formatBytes(limitBytes)}`);
92
+ logger_1.logger.debug(`[AttachmentCleanup] Initialized with limit: ${formatBytes(limitBytes)}`);
93
93
  this._diskLogTimer = setInterval(() => {
94
94
  this._logDiskStatus();
95
95
  }, DISK_LOG_INTERVAL_MS);
@@ -139,7 +139,7 @@ class AttachmentCleanup {
139
139
  this._evictedCount++;
140
140
  this._evictedBytes += oldest.size;
141
141
  this._totalSize -= oldest.size;
142
- logger_1.logger.info(`[AttachmentCleanup] Evicted ${oldest.path} (${formatBytes(oldest.size)}), totalSize=${formatBytes(this._totalSize)}, pending=${this._pending.size}, uploaded=${this._uploaded.length}`);
142
+ logger_1.logger.debug(`[AttachmentCleanup] Evicted ${oldest.path} (${formatBytes(oldest.size)}), totalSize=${formatBytes(this._totalSize)}, pending=${this._pending.size}, uploaded=${this._uploaded.length}`);
143
143
  this._onEvict?.(oldest.path);
144
144
  }
145
145
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACzD,GAAG,EAAE,cAAc,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CA2BD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3E;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,cAAc,CAAC;AAOtD;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC1C,OAAO,CAAC,SAAS,CAAC,CAoDpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,SAAS,EAAE,CAAC,CAUtB"}
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACzD,GAAG,EAAE,cAAc,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CA2BD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3E;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,cAAc,CAAC;AAOtD;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC1C,OAAO,CAAC,SAAS,CAAC,CAiDpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,SAAS,EAAE,CAAC,CAUtB"}
@@ -75,9 +75,6 @@ async function runPlaywrightWithSigint(ctx, opts) {
75
75
  TOTAL_SHARDS: "1",
76
76
  },
77
77
  stdio: ["pipe", "pipe", "pipe"],
78
- // Must match production: detached creates a new process group so the
79
- // reporter's SIGKILL on -process.pid only kills browsers + Playwright,
80
- // not the test runner.
81
78
  detached: true,
82
79
  });
83
80
  let stdout = "";
@@ -43,6 +43,7 @@ export declare function filterSuiteByCompletedTests(suite: JsonSuite, completedT
43
43
  export declare function hasValidReport(reportLines: string[]): boolean;
44
44
  export declare function buildUrlsJson(attachmentUrlMap: Map<string, string>): Record<string, string>;
45
45
  export declare function isLocalTesting(): boolean;
46
+ export declare function toResourcePath(absolutePath: string): string;
46
47
  export declare function embedAttachment(attachmentPath: string | undefined, resultId: string, stagingDir: string): string | null;
47
48
  export {};
48
49
  //# sourceMappingURL=ibr-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ibr-utils.d.ts","sourceRoot":"","sources":["../../src/reporter/ibr-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAO3E,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,CAAC;AAIF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAChD,WAAW,EAAE,CAOf;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO3E;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,EAC1B,IAAI,EAAE,QAAQ,GACb,MAAM,CAOR;AAID,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAIF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD,CAAC;AAIF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAAC;CACvC,CAAC;AAIF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAc3E;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAsBvE;AAMD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,SAAS,EAChB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC5B,SAAS,GAAG,IAAI,CAmBlB;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAK7D;AAED,wBAAgB,aAAa,CAC3B,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAkBf"}
1
+ {"version":3,"file":"ibr-utils.d.ts","sourceRoot":"","sources":["../../src/reporter/ibr-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAO3E,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,CAAC;AAIF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAChD,WAAW,EAAE,CAOf;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO3E;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,EAC1B,IAAI,EAAE,QAAQ,GACb,MAAM,CAOR;AAID,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAIF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD,CAAC;AAIF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,CAAC;CACvC,CAAC;AAIF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAc3E;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAsBvE;AAMD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,SAAS,EAChB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC5B,SAAS,GAAG,IAAI,CAmBlB;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAK7D;AAED,wBAAgB,aAAa,CAC3B,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAkBf"}
@@ -13,6 +13,7 @@ exports.filterSuiteByCompletedTests = filterSuiteByCompletedTests;
13
13
  exports.hasValidReport = hasValidReport;
14
14
  exports.buildUrlsJson = buildUrlsJson;
15
15
  exports.isLocalTesting = isLocalTesting;
16
+ exports.toResourcePath = toResourcePath;
16
17
  exports.embedAttachment = embedAttachment;
17
18
  const crypto_1 = __importDefault(require("crypto"));
18
19
  const fs_1 = __importDefault(require("fs"));
@@ -126,6 +127,11 @@ function buildUrlsJson(attachmentUrlMap) {
126
127
  function isLocalTesting() {
127
128
  return process.env.LOCAL_TEST === "true";
128
129
  }
130
+ function toResourcePath(absolutePath) {
131
+ const ext = path_1.default.extname(absolutePath);
132
+ const hash = crypto_1.default.createHash("sha1").update(absolutePath).digest("hex");
133
+ return `resources/${hash}${ext}`;
134
+ }
129
135
  function embedAttachment(attachmentPath, resultId, stagingDir) {
130
136
  if (!attachmentPath || !fs_1.default.existsSync(attachmentPath))
131
137
  return null;
@@ -19,18 +19,19 @@ declare class IncrementalBlobReporter implements Reporter {
19
19
  private _resultIdMap;
20
20
  private _stepIdMap;
21
21
  private _uploader;
22
- private _flushPromise;
23
22
  private _completedTestIds;
24
23
  private _completedResultIds;
25
24
  private _testRetries;
26
25
  private _testResults;
27
26
  private _startTime;
27
+ private _finalizePromise;
28
28
  constructor();
29
29
  private _sigintHandler;
30
30
  private _setupSignalHandler;
31
31
  private _removeSignalHandler;
32
- private _flushAndUpload;
33
- private _finalizeReportForInterrupt;
32
+ private _buildSyntheticResult;
33
+ private _finalizeAndMaybeUpload;
34
+ private _finalizeReport;
34
35
  private _computeCompletedSets;
35
36
  private _removeIncompleteTestArtifacts;
36
37
  private _patchProjectSuites;
@@ -1 +1 @@
1
- {"version":3,"file":"incremental-blob-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/incremental-blob-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAuBnC;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD,cAAM,uBAAwB,YAAW,QAAQ;IAC/C,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,WAAW,CAIjB;IACF,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,YAAY,CAGN;IACd,OAAO,CAAC,UAAU,CAAsB;;IAexC,OAAO,CAAC,cAAc,CAA6B;IAEnD,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,oBAAoB;YAOd,eAAe;IAwC7B,OAAO,CAAC,2BAA2B;IAyBnC,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,8BAA8B;IAStC,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,KAAK,QAAQ,GAKnB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,aAAa,GAExB;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;YAUP,SAAS;IAwBvB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAgB/C,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAIrD,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAyBnD,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAYrE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAY7D,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB9C;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAI5D,aAAa,IAAI,OAAO;CAGzB;AAID,wBAAgB,0BAA0B,IAAI,uBAAuB,GAAG,IAAI,CAE3E;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"incremental-blob-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/incremental-blob-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAwBnC;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD,cAAM,uBAAwB,YAAW,QAAQ;IAC/C,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,WAAW,CAIjB;IACF,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,YAAY,CAGN;IACd,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,gBAAgB,CAA8B;;IAetD,OAAO,CAAC,cAAc,CAA6B;IAEnD,OAAO,CAAC,mBAAmB;IA+B3B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,qBAAqB;YASf,uBAAuB;IAqErC,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,8BAA8B;IAStC,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,KAAK,QAAQ,GAKnB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,aAAa,GAExB;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;YAUP,SAAS;IAwBvB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAgB/C,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAIrD,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAyBnD,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAYrE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAY7D,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB9C;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAI5D,aAAa,IAAI,OAAO;CAGzB;AAID,wBAAgB,0BAA0B,IAAI,uBAAuB,GAAG,IAAI,CAE3E;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED,eAAe,uBAAuB,CAAC"}
@@ -25,12 +25,12 @@ class IncrementalBlobReporter {
25
25
  _resultIdMap = new Map(); // testId+retry -> resultId
26
26
  _stepIdMap = new Map(); // step -> stepId
27
27
  _uploader = null;
28
- _flushPromise = null;
29
28
  _completedTestIds = new Set();
30
29
  _completedResultIds = new Set();
31
30
  _testRetries = new Map(); // testId → configured retries
32
31
  _testResults = new Map(); // testId → retryIndex → {resultId, status, expectedStatus}
33
32
  _startTime = Date.now();
33
+ _finalizePromise = null;
34
34
  constructor() {
35
35
  if (process.env.SHARD_INDEX && process.env.TOTAL_SHARDS) {
36
36
  this._shardIndex = Number.parseInt(process.env.SHARD_INDEX, 10);
@@ -47,28 +47,25 @@ class IncrementalBlobReporter {
47
47
  _sigintHandler = null;
48
48
  _setupSignalHandler() {
49
49
  this._sigintHandler = () => {
50
- if ((0, reporter_state_1.isFinalized)())
50
+ if (this._finalizePromise || (0, reporter_state_1.isFinalized)()) {
51
51
  return;
52
- console.log("[IncrementalBlobReporter] SIGINT received, flushing and uploading...");
53
- this._flushPromise = this._flushAndUpload()
52
+ }
53
+ console.log("[IncrementalBlobReporter] SIGINT received, finalizing...");
54
+ this._finalizePromise = this._finalizeAndMaybeUpload({
55
+ result: this._buildSyntheticResult("interrupted"),
56
+ source: "SIGINT",
57
+ markGlobalFinalized: true,
58
+ pruneIncompleteTests: true,
59
+ upload: true,
60
+ })
54
61
  .then(() => {
55
- console.log("[IncrementalBlobReporter] Flush and upload complete on SIGINT, exiting");
62
+ console.log("[IncrementalBlobReporter] Finalize and upload complete on SIGINT");
56
63
  })
57
64
  .catch((err) => {
58
- console.error("[IncrementalBlobReporterFailure] Flush/upload failed on SIGINT:", err);
65
+ console.error("[IncrementalBlobReporterFailure] Finalize/upload failed on SIGINT:", err);
59
66
  })
60
67
  .finally(() => {
61
68
  this._removeSignalHandler();
62
- // Kill the entire process group (browsers + self) to avoid
63
- // orphaned browser processes writing to test-results/ after exit.
64
- // Using process.exit(0) alone only exits the Node process, leaving
65
- // browser processes alive which blocks cleanup on worker warm reuse.
66
- try {
67
- process.kill(-process.pid, "SIGKILL");
68
- }
69
- catch {
70
- process.exit(0);
71
- }
72
69
  });
73
70
  };
74
71
  process.on("SIGINT", this._sigintHandler);
@@ -79,8 +76,23 @@ class IncrementalBlobReporter {
79
76
  this._sigintHandler = null;
80
77
  }
81
78
  }
82
- async _flushAndUpload() {
83
- (0, reporter_state_1.setFinalized)();
79
+ _buildSyntheticResult(status) {
80
+ const now = Date.now();
81
+ return {
82
+ status,
83
+ startTime: new Date(this._startTime),
84
+ duration: now - this._startTime,
85
+ };
86
+ }
87
+ async _finalizeAndMaybeUpload({ result, source, markGlobalFinalized, pruneIncompleteTests, upload, }) {
88
+ const finalizeStart = Date.now();
89
+ const logFinalizeComplete = () => {
90
+ const durationMs = Date.now() - finalizeStart;
91
+ console.log(`[IncrementalBlobReporter] Finalize from ${source} completed in ${durationMs}ms (${(durationMs / 1_000).toFixed(1)}s)`);
92
+ };
93
+ if (markGlobalFinalized) {
94
+ (0, reporter_state_1.setFinalized)();
95
+ }
84
96
  if (!(0, ibr_utils_1.hasValidReport)(this._reportLines)) {
85
97
  console.warn("[IncrementalBlobReporterFailure] No onBegin received, skipping upload (nothing to merge)");
86
98
  return;
@@ -89,39 +101,37 @@ class IncrementalBlobReporter {
89
101
  if (this._uploader) {
90
102
  await this._uploader.waitForUploads();
91
103
  }
92
- this._finalizeReportForInterrupt();
104
+ this._finalizeReport(result, pruneIncompleteTests);
93
105
  await this._writeZip();
106
+ if (!upload) {
107
+ logFinalizeComplete();
108
+ return;
109
+ }
94
110
  if (!this._uploader) {
95
111
  console.warn("[IncrementalBlobReporterFailure] No uploader available, skipping upload");
112
+ logFinalizeComplete();
96
113
  return;
97
114
  }
98
115
  await this._uploader.uploadFile(this._zipPath, `blobs/incremental-report-${this._shardIndex}.zip`);
99
116
  await this._uploader.waitForUploads();
100
- console.log("[IncrementalBlobReporter] Flush and upload complete on SIGINT");
117
+ console.log(`[IncrementalBlobReporter] Finalize and upload complete from ${source}`);
101
118
  if ((0, ibr_utils_1.isLocalTesting)()) {
102
119
  await (0, local_test_1.mergeForLocalTest)(this._currentWorkingDir, this._outputDir);
103
120
  }
121
+ logFinalizeComplete();
104
122
  }
105
- _finalizeReportForInterrupt() {
106
- // Determine which tests are fully complete (passed or all retries finished)
107
- this._computeCompletedSets();
108
- // Remove unrun tests from onProject suites
109
- this._patchProjectSuites();
110
- // Remove events (onTestBegin, onStepBegin, etc.) for incomplete tests
111
- this._removeIncompleteTestArtifacts();
112
- // Push onEnd directly (bypass _appendEvent which checks isFinalized)
113
- const now = Date.now();
114
- const onEndEvent = {
115
- method: "onEnd",
116
- params: {
117
- result: {
118
- status: "interrupted",
119
- startTime: this._startTime,
120
- duration: now - this._startTime,
121
- },
122
- },
123
- };
124
- this._reportLines.push(JSON.stringify(onEndEvent));
123
+ _finalizeReport(result, pruneIncompleteTests) {
124
+ if (pruneIncompleteTests) {
125
+ // Determine which tests are fully complete (passed or all retries finished)
126
+ this._computeCompletedSets();
127
+ // Remove unrun tests from onProject suites
128
+ this._patchProjectSuites();
129
+ // Remove events (onTestBegin, onStepBegin, etc.) for incomplete tests
130
+ this._removeIncompleteTestArtifacts();
131
+ }
132
+ // Push onEnd directly. SIGINT marks the reporter finalized before this,
133
+ // so using _appendEvent would drop the synthetic onEnd event.
134
+ this._reportLines.push(JSON.stringify((0, lifecycle_events_1.buildOnEndEvent)(result)));
125
135
  }
126
136
  _computeCompletedSets() {
127
137
  for (const [testId, results] of this._testResults) {
@@ -233,21 +243,29 @@ class IncrementalBlobReporter {
233
243
  }
234
244
  async onEnd(result) {
235
245
  this._removeSignalHandler();
236
- this._appendEvent((0, lifecycle_events_1.buildOnEndEvent)(result));
237
- // Wait for SIGINT flush to complete if it was started
238
- if (this._flushPromise) {
239
- await this._flushPromise;
246
+ // If SIGINT already started the partial-report finalization, let that
247
+ // finish instead of appending a second onEnd or racing another zip write.
248
+ if (this._finalizePromise) {
249
+ await this._finalizePromise;
250
+ return;
240
251
  }
241
- // Final zip write
242
- await this._uploader?.waitForUploads();
243
- await this._writeZip();
252
+ this._finalizePromise = this._finalizeAndMaybeUpload({
253
+ result,
254
+ source: "onEnd",
255
+ markGlobalFinalized: false,
256
+ pruneIncompleteTests: false,
257
+ // Normal completion already has Playwright's blob/html reporters.
258
+ // Only the SIGINT path uploads this incremental fallback blob.
259
+ upload: false,
260
+ });
261
+ await this._finalizePromise;
244
262
  console.log(`[IncrementalBlobReporter] Finished with status: ${result.status}`);
245
263
  }
246
264
  /**
247
265
  * Add a single attachment URL mapping
248
266
  */
249
267
  addAttachmentUrl(localPath, remoteUrl) {
250
- this._attachmentUrlMap.set(localPath, remoteUrl);
268
+ this._attachmentUrlMap.set((0, ibr_utils_1.toResourcePath)(localPath), remoteUrl);
251
269
  }
252
270
  printsToStdio() {
253
271
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-events.d.ts","sourceRoot":"","sources":["../../src/reporter/lifecycle-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAenC,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,KAAK,EACZ,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,SAAS,EAAE,CAmDb;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,KAAK,EACnB,iBAAiB,EAAE,MAAM,GACxB,SAAS,CA+BX;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,SAAS,CAeX;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,UAAU,EAAE,MAAM,GACjB,SAAS,EAAE,CA+Cb;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAC/B,SAAS,CAsBX;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAC/B,SAAS,CAuBX;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAW7D"}
1
+ {"version":3,"file":"lifecycle-events.d.ts","sourceRoot":"","sources":["../../src/reporter/lifecycle-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAgBnC,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,KAAK,EACZ,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,SAAS,EAAE,CAmDb;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,KAAK,EACnB,iBAAiB,EAAE,MAAM,GACxB,SAAS,CA+BX;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,SAAS,CAeX;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,UAAU,EAAE,MAAM,GACjB,SAAS,EAAE,CAqDb;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAC/B,SAAS,CAsBX;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAC/B,SAAS,CAuBX;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAW7D"}
@@ -113,6 +113,13 @@ function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
113
113
  // onAttach for each attachment (matching Playwright's blob format)
114
114
  for (const attachment of result.attachments) {
115
115
  const embeddedPath = (0, ibr_utils_1.embedAttachment)(attachment.path, resultId, stagingDir);
116
+ let attachmentPath;
117
+ if (embeddedPath) {
118
+ attachmentPath = embeddedPath;
119
+ }
120
+ else if (attachment.path) {
121
+ attachmentPath = (0, ibr_utils_1.toResourcePath)(attachment.path);
122
+ }
116
123
  events.push({
117
124
  method: "onAttach",
118
125
  params: {
@@ -122,7 +129,7 @@ function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
122
129
  {
123
130
  name: attachment.name,
124
131
  contentType: attachment.contentType,
125
- path: embeddedPath ?? attachment.path,
132
+ path: attachmentPath,
126
133
  },
127
134
  ],
128
135
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.50.1",
3
+ "version": "0.52.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -47,8 +47,8 @@
47
47
  "rimraf": "^6.0.1",
48
48
  "ts-morph": "^23.0.0",
49
49
  "@empiricalrun/cua": "^0.4.1",
50
- "@empiricalrun/dashboard-client": "^0.3.0",
51
50
  "@empiricalrun/llm": "^0.27.0",
51
+ "@empiricalrun/dashboard-client": "^0.3.0",
52
52
  "@empiricalrun/r2-uploader": "^0.9.1",
53
53
  "@empiricalrun/reporter": "^0.29.1"
54
54
  },