@empiricalrun/playwright-utils 0.61.0 → 0.61.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAuBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAA2B;IAGrD,OAAO,CAAC,YAAY,CAAqB;IAEzC,cAEC;IAED,OAAO,CAAC,MAAM,EAAE,UAAU,QAEzB;IAED,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,6BAA6B,CA2CnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,QA+I3C;IAEK,KAAK,CAAC,MAAM,EAAE,UAAU,iBAiH7B;IAED,OAAO,CAAC,qBAAqB;YAkBf,gBAAgB;YAOhB,iBAAiB;CAqChC;eAEc,iBAAiB"}
1
+ {"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAuBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAA2B;IAGrD,OAAO,CAAC,YAAY,CAAqB;IAEzC,cAEC;IAED,OAAO,CAAC,MAAM,EAAE,UAAU,QAEzB;IAED,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,6BAA6B,CA2CnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,QAoJ3C;IAEK,KAAK,CAAC,MAAM,EAAE,UAAU,iBAoH7B;IAED,OAAO,CAAC,qBAAqB;YAkBf,gBAAgB;YAOhB,iBAAiB;CAqChC;eAEc,iBAAiB"}
@@ -62,7 +62,12 @@ class EmpiricalReporter {
62
62
  }
63
63
  };
64
64
  onTestEnd(test, result) {
65
- if ((0, reporter_state_1.isFinalized)()) {
65
+ // After the SIGINT salvage, an attempt that really finished (its body
66
+ // completed, only its teardown overlapped the signal) is still reported:
67
+ // the blob keeps it, and a ledger that misses it calls the test unfinished,
68
+ // so the resume shard re-runs it and the merged report holds it twice.
69
+ // Shutdown artifacts stay out, as they do in the blob.
70
+ if ((0, reporter_state_1.isFinalized)() && (0, reporter_state_1.isShutdownArtifact)(result.status, true)) {
66
71
  return;
67
72
  }
68
73
  if (!this._uploader) {
@@ -181,6 +186,9 @@ class EmpiricalReporter {
181
186
  }
182
187
  async onEnd(result) {
183
188
  if ((0, reporter_state_1.isFinalized)()) {
189
+ // The salvage owns the report upload. Only flush the test-case events
190
+ // sent during shutdown, so they land before the process exits.
191
+ await Promise.allSettled(this._pendingTestCaseEvents);
184
192
  return;
185
193
  }
186
194
  if (!this._uploader) {
@@ -62,6 +62,8 @@ export interface SignalRunOptions {
62
62
  killAfterMs?: number;
63
63
  /** How long to wait for the trigger before giving up and signalling anyway. */
64
64
  triggerTimeoutMs?: number;
65
+ /** Extra env for the runner, e.g. from `startDashboardStub`. */
66
+ env?: NodeJS.ProcessEnv;
65
67
  }
66
68
  /**
67
69
  * Spawns `playwright test` with the IBR reporter in its own process group,
@@ -132,4 +134,17 @@ export declare function runPlaywrightClean(ctx: ParityTestContext, opts: {
132
134
  * parsed summary. Distinct output names let one temp dir hold both merges.
133
135
  */
134
136
  export declare function mergeBlobDirToSummary(cwd: string, blobDir: string, outputName: string): Record<string, unknown>;
137
+ export interface DashboardStub {
138
+ /** `test_case_run_end` payloads EmpiricalReporter sent, in arrival order. */
139
+ testCaseEvents: () => Array<Record<string, any>>;
140
+ /** Runner env that points EmpiricalReporter's API calls and uploads here. */
141
+ env: NodeJS.ProcessEnv;
142
+ close: () => Promise<void>;
143
+ }
144
+ /**
145
+ * A local stand-in for api-worker and the report bucket, so EmpiricalReporter
146
+ * runs unmodified inside a real Playwright run. Records test-case events and
147
+ * accepts every upload.
148
+ */
149
+ export declare function startDashboardStub(): Promise<DashboardStub>;
135
150
  //# sourceMappingURL=harness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"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"}
@@ -16,13 +16,16 @@ exports.readEventsFromZip = readEventsFromZip;
16
16
  exports.setupParityFixture = setupParityFixture;
17
17
  exports.runPlaywrightClean = runPlaywrightClean;
18
18
  exports.mergeBlobDirToSummary = mergeBlobDirToSummary;
19
+ exports.startDashboardStub = startDashboardStub;
19
20
  const zip_1 = require("@empiricalrun/r2-uploader/zip");
20
21
  const child_process_1 = require("child_process");
21
22
  const fs_1 = __importDefault(require("fs"));
23
+ const http_1 = __importDefault(require("http"));
22
24
  const os_1 = __importDefault(require("os"));
23
25
  const path_1 = __importDefault(require("path"));
24
26
  const TEST_DATA_DIR = path_1.default.resolve(__dirname, "../../test-data");
25
27
  const REPORTER_PATH = path_1.default.resolve(__dirname, "../../dist/reporter/incremental-blob-reporter.js");
28
+ const EMPIRICAL_REPORTER_PATH = path_1.default.resolve(__dirname, "../../dist/reporter/empirical-reporter.js");
26
29
  const TEST_CASE_SYNC_REPORTER_PATH = path_1.default.resolve(__dirname, "../../dist/reporter/test-case-sync-reporter.js");
27
30
  const PLAYWRIGHT_BIN = path_1.default.resolve(__dirname, "../../node_modules/.bin/playwright");
28
31
  async function sleep(ms) {
@@ -60,7 +63,9 @@ async function setupReporterFixture({ suite, reporterPath, tempPrefix, }) {
60
63
  }
61
64
  else if (entry.name === "playwright.config.ts") {
62
65
  const content = await fs_1.default.promises.readFile(src, "utf8");
63
- await fs_1.default.promises.writeFile(dest, content.replace("__REPORTER_PATH__", reporterPath));
66
+ await fs_1.default.promises.writeFile(dest, content
67
+ .replace("__REPORTER_PATH__", reporterPath)
68
+ .replace("__EMPIRICAL_REPORTER_PATH__", EMPIRICAL_REPORTER_PATH));
64
69
  }
65
70
  else {
66
71
  await fs_1.default.promises.copyFile(src, dest);
@@ -106,7 +111,7 @@ const MARKER_RE = /@@ ([A-Z0-9-]+) @@/g;
106
111
  * runner detached and does `process.kill(-pid, signal)`.
107
112
  */
108
113
  async function runPlaywrightWithSignal(ctx, opts) {
109
- const { workers, waitForMarker, waitFor, delayBeforeSignalMs = 0, signal = "SIGINT", secondSignalAfterMs, killAfterMs, triggerTimeoutMs = 30_000, } = opts;
114
+ const { workers, waitForMarker, waitFor, delayBeforeSignalMs = 0, signal = "SIGINT", secondSignalAfterMs, killAfterMs, triggerTimeoutMs = 30_000, env, } = opts;
110
115
  const startedAt = Date.now();
111
116
  const since = () => Date.now() - startedAt;
112
117
  const child = (0, child_process_1.spawn)(PLAYWRIGHT_BIN, ["test", `--workers=${workers}`], {
@@ -117,6 +122,7 @@ async function runPlaywrightWithSignal(ctx, opts) {
117
122
  SHARD_INDEX: "1",
118
123
  TOTAL_SHARDS: "1",
119
124
  FORCE_COLOR: "0",
125
+ ...env,
120
126
  },
121
127
  stdio: ["pipe", "pipe", "pipe"],
122
128
  detached: true,
@@ -351,3 +357,63 @@ function mergeBlobDirToSummary(cwd, blobDir, outputName) {
351
357
  });
352
358
  return JSON.parse(fs_1.default.readFileSync(outputPath, "utf8"));
353
359
  }
360
+ /**
361
+ * A local stand-in for api-worker and the report bucket, so EmpiricalReporter
362
+ * runs unmodified inside a real Playwright run. Records test-case events and
363
+ * accepts every upload.
364
+ */
365
+ async function startDashboardStub() {
366
+ const events = [];
367
+ const server = http_1.default.createServer((req, res) => {
368
+ const chunks = [];
369
+ req.on("data", (c) => chunks.push(c));
370
+ req.on("end", () => {
371
+ if (req.url?.startsWith("/api/")) {
372
+ if (req.method === "PUT" && req.url === "/api/test-runs/events") {
373
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
374
+ if (body.name === "test_case_run_end") {
375
+ events.push(body.data);
376
+ }
377
+ }
378
+ res.writeHead(200, { "content-type": "application/json" });
379
+ res.end("{}");
380
+ return;
381
+ }
382
+ // Everything else is the S3 client; r2-uploader always goes multipart.
383
+ res.writeHead(200, { "content-type": "application/xml", etag: '"etag"' });
384
+ if (req.method === "POST" && req.url?.includes("?uploads")) {
385
+ res.end("<InitiateMultipartUploadResult><UploadId>upload</UploadId></InitiateMultipartUploadResult>");
386
+ }
387
+ else if (req.method === "POST") {
388
+ res.end('<CompleteMultipartUploadResult><ETag>"etag"</ETag></CompleteMultipartUploadResult>');
389
+ }
390
+ else {
391
+ res.end();
392
+ }
393
+ });
394
+ });
395
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
396
+ const url = `http://127.0.0.1:${server.address().port}`;
397
+ return {
398
+ testCaseEvents: () => [...events],
399
+ env: {
400
+ PROJECT_NAME: "reporter-test",
401
+ TEST_RUN_GITHUB_ACTION_ID: "1",
402
+ HAS_SHARDING: "true",
403
+ EMPIRICALRUN_API_KEY: "test-key",
404
+ DASHBOARD_DOMAIN: url,
405
+ // An IP endpoint makes the S3 client use path-style URLs, so uploads
406
+ // land on this server instead of a bucket subdomain.
407
+ REPORT_STORAGE_BACKEND: "s3",
408
+ S3_REGION: "us-east-1",
409
+ S3_UPLOAD_BUCKET: "reports",
410
+ AWS_ENDPOINT_URL_S3: url,
411
+ AWS_ACCESS_KEY_ID: "test",
412
+ AWS_SECRET_ACCESS_KEY: "test",
413
+ AWS_SESSION_TOKEN: undefined,
414
+ AWS_PROFILE: undefined,
415
+ REPORTS_BASE_URL: url,
416
+ },
417
+ close: () => new Promise((resolve) => server.close(() => resolve())),
418
+ };
419
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"incremental-blob-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/incremental-blob-reporter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"incremental-blob-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/incremental-blob-reporter.ts"],"names":[],"mappings":"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"}
@@ -151,15 +151,10 @@ class IncrementalBlobReporter {
151
151
  _computeCompletedSets() {
152
152
  for (const [testId, results] of this._testResults) {
153
153
  const maxRetries = this._testRetries.get(testId);
154
- // Shutdown artifacts are not finished retries: an "interrupted" result
155
- // is a test cut off mid-flight, and a post-signal "skipped" is a serial
156
- // sibling Playwright marks off while shutting down. The resume shard
157
- // re-runs both, so counting them as complete would put their results in
158
- // the final zip alongside the resume shard's, duplicating tests in the
159
- // merge. A post-signal passed/failed is real, though: the test finished
160
- // its body and only its aftermath overlapped the shutdown.
161
- const finished = new Map([...results].filter(([, r]) => r.status !== "interrupted" &&
162
- !(r.postSignal && r.status === "skipped")));
154
+ // Shutdown artifacts are not finished retries. Counting them as
155
+ // complete would put their results in the final zip alongside the
156
+ // resume shard's, duplicating tests in the merge.
157
+ const finished = new Map([...results].filter(([, r]) => !(0, reporter_state_1.isShutdownArtifact)(r.status, r.postSignal)));
163
158
  if ((0, reporter_1.areRetriesComplete)(maxRetries, finished)) {
164
159
  this._completedTestIds.add(testId);
165
160
  for (const { resultId } of finished.values()) {
@@ -1,3 +1,16 @@
1
1
  export declare function isFinalized(): boolean;
2
2
  export declare function setFinalized(): void;
3
+ /**
4
+ * Whether a result is a by-product of interruption shutdown rather than a
5
+ * finished attempt: an "interrupted" result is a test cut off mid-flight, and
6
+ * a post-signal "skipped" is a serial sibling Playwright marks off while
7
+ * shutting down. The resume shard re-runs both. A post-signal passed/failed is
8
+ * real, though: the test finished its body and only its aftermath overlapped
9
+ * the shutdown.
10
+ *
11
+ * Shared so the blob and the run ledger agree on which results count; when
12
+ * they disagree, the ledger calls a test unfinished that the blob already
13
+ * holds, and the merged report gets it twice.
14
+ */
15
+ export declare function isShutdownArtifact(status: string, postSignal: boolean): boolean;
3
16
  //# sourceMappingURL=reporter-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reporter-state.d.ts","sourceRoot":"","sources":["../../src/reporter/reporter-state.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
1
+ {"version":3,"file":"reporter-state.d.ts","sourceRoot":"","sources":["../../src/reporter/reporter-state.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAClB,OAAO,CAET"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isFinalized = isFinalized;
4
4
  exports.setFinalized = setFinalized;
5
+ exports.isShutdownArtifact = isShutdownArtifact;
5
6
  let _finalized = false;
6
7
  function isFinalized() {
7
8
  return _finalized;
@@ -9,3 +10,18 @@ function isFinalized() {
9
10
  function setFinalized() {
10
11
  _finalized = true;
11
12
  }
13
+ /**
14
+ * Whether a result is a by-product of interruption shutdown rather than a
15
+ * finished attempt: an "interrupted" result is a test cut off mid-flight, and
16
+ * a post-signal "skipped" is a serial sibling Playwright marks off while
17
+ * shutting down. The resume shard re-runs both. A post-signal passed/failed is
18
+ * real, though: the test finished its body and only its aftermath overlapped
19
+ * the shutdown.
20
+ *
21
+ * Shared so the blob and the run ledger agree on which results count; when
22
+ * they disagree, the ledger calls a test unfinished that the blob already
23
+ * holds, and the merged report gets it twice.
24
+ */
25
+ function isShutdownArtifact(status, postSignal) {
26
+ return status === "interrupted" || (postSignal && status === "skipped");
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.61.0",
3
+ "version": "0.61.1",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -45,7 +45,7 @@
45
45
  "openapi-typescript": "7.6.1",
46
46
  "playwright-core": "1.63.0",
47
47
  "serve-handler": "^6.1.6",
48
- "@empiricalrun/shared-types": "0.20.2"
48
+ "@empiricalrun/shared-types": "0.20.3"
49
49
  },
50
50
  "dependencies": {
51
51
  "@babel/code-frame": "^7.24.7",
@@ -58,10 +58,10 @@
58
58
  "puppeteer-extra-plugin-recaptcha": "^3.6.8",
59
59
  "rimraf": "^6.0.1",
60
60
  "ts-morph": "^23.0.0",
61
- "@empiricalrun/cua": "^0.5.0",
62
61
  "@empiricalrun/llm": "^0.28.0",
63
62
  "@empiricalrun/r2-uploader": "^0.10.3",
64
- "@empiricalrun/reporter": "^0.30.2"
63
+ "@empiricalrun/reporter": "^0.30.2",
64
+ "@empiricalrun/cua": "^0.5.0"
65
65
  },
66
66
  "scripts": {
67
67
  "dev": "pnpm gen:api-worker-openapi && tsgo --build --watch",