@empiricalrun/playwright-utils 0.39.1 → 0.39.2

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 (37) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/config/index.d.ts.map +1 -1
  3. package/dist/config/index.js +5 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/mailosaur-client.d.ts +5 -0
  8. package/dist/mailosaur-client.d.ts.map +1 -1
  9. package/dist/reporter/empirical-reporter.d.ts.map +1 -1
  10. package/dist/reporter/empirical-reporter.js +19 -0
  11. package/dist/telemetry.d.ts +10 -1
  12. package/dist/telemetry.d.ts.map +1 -1
  13. package/dist/telemetry.js +19 -2
  14. package/dist/test/expect/index.d.ts +4 -0
  15. package/dist/test/expect/index.d.ts.map +1 -0
  16. package/dist/test/expect/index.js +7 -0
  17. package/dist/test/expect/types.d.ts +5 -0
  18. package/dist/test/expect/types.d.ts.map +1 -0
  19. package/dist/test/expect/types.js +2 -0
  20. package/dist/test/expect/visual.d.ts +4 -0
  21. package/dist/test/expect/visual.d.ts.map +1 -0
  22. package/dist/test/{expect.js → expect/visual.js} +1 -1
  23. package/dist/test/expect/webhook.d.ts +7 -0
  24. package/dist/test/expect/webhook.d.ts.map +1 -0
  25. package/dist/test/expect/webhook.js +41 -0
  26. package/dist/test/index.d.ts +2 -0
  27. package/dist/test/index.d.ts.map +1 -1
  28. package/dist/test/index.js +1 -1
  29. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
  30. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +7 -3
  31. package/dist/webhook.d.ts +20 -0
  32. package/dist/webhook.d.ts.map +1 -0
  33. package/dist/webhook.js +116 -0
  34. package/package.json +3 -3
  35. package/tsconfig.tsbuildinfo +1 -1
  36. package/dist/test/expect.d.ts +0 -7
  37. package/dist/test/expect.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.39.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 5350bc6: feat: add webhook testing helpers
8
+ - cc010af: fix: max failures should be shards aware
9
+
3
10
  ## 0.39.1
4
11
 
5
12
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgBzD,wBAAgB,gBAAgB,IAAI,MAAM,CAazC;AA6BD,eAAO,MAAM,UAAU,EAAE,oBAyBxB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,iBAarB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgBzD,wBAAgB,gBAAgB,IAAI,MAAM,CAazC;AA6BD,eAAO,MAAM,UAAU,EAAE,oBA4BxB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,iBAarB,CAAC"}
@@ -71,8 +71,11 @@ exports.baseConfig = {
71
71
  retries: process.env.CI ? 2 : 0,
72
72
  // Let playwright choose max workers based on size of infra
73
73
  workers: undefined,
74
- // maxFailures counts retries as individual failures, so this is equivalent to 20 failed tests
75
- maxFailures: process.env.CI ? 30 : undefined,
74
+ // maxFailures counts retries as individual failures, so this is equivalent to ~20 failed tests
75
+ // When sharding, distribute the limit across shards (rounded up)
76
+ maxFailures: process.env.CI
77
+ ? Math.ceil(30 / (parseInt(process.env.TOTAL_SHARDS, 10) || 1))
78
+ : undefined,
76
79
  timeout: 15 * 60 * 1_000,
77
80
  expect: {
78
81
  timeout: 15_000,
package/dist/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export * from "./email";
7
7
  export * from "./kv";
8
8
  export * from "./playwright-extensions";
9
9
  export * from "./postgres";
10
+ export * from "./webhook";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -29,3 +29,4 @@ __exportStar(require("./email"), exports);
29
29
  __exportStar(require("./kv"), exports);
30
30
  __exportStar(require("./playwright-extensions"), exports);
31
31
  __exportStar(require("./postgres"), exports);
32
+ __exportStar(require("./webhook"), exports);
@@ -14,6 +14,11 @@ export interface IDashboardAPIClient {
14
14
  path: string;
15
15
  body?: unknown;
16
16
  }): Promise<T>;
17
+ callWebhookSiteProxy<T>(opts: {
18
+ method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
19
+ path: string;
20
+ body?: unknown;
21
+ }): Promise<T>;
17
22
  getBaseUrl(): string;
18
23
  }
19
24
  type Link = {
@@ -1 +1 @@
1
- {"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACrD,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE;QACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAkCF,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,CACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC;IAAE,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAA;CAAE,CACrD,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAW1B,WAAW;IA0CnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACrD,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE;QACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAkCF,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,CACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC;IAAE,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAA;CAAE,CACrD,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAW1B,WAAW;IA0CnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
@@ -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,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAkBnC,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;;IAM1C,OAAO,CAAC,6BAA6B,CAkCnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAoFtC,KAAK,CAAC,MAAM,EAAE,UAAU;IA8G9B,OAAO,CAAC,gBAAgB;YAoBV,gBAAgB;YAOhB,iBAAiB;CAchC;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAmBnC,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;;IAM1C,OAAO,CAAC,6BAA6B,CAkCnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAoFtC,KAAK,CAAC,MAAM,EAAE,UAAU;IA8G9B,OAAO,CAAC,gBAAgB;YAoBV,gBAAgB;YAOhB,iBAAiB;CAmChC;AAED,eAAe,iBAAiB,CAAC"}
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const logger_1 = require("../logger");
9
+ const telemetry_1 = require("../telemetry");
9
10
  const blob_utils_1 = require("./blob-utils");
10
11
  const uploader_1 = require("./uploader");
11
12
  const util_1 = require("./util");
@@ -198,12 +199,30 @@ class EmpiricalReporter {
198
199
  async _uploadBlobReport(blobDir) {
199
200
  if (!this._uploader)
200
201
  return;
202
+ const uploadStartTime = Date.now();
201
203
  try {
202
204
  const files = await fs_1.default.promises.readdir(blobDir);
203
205
  logger_1.logger.info(`[Empirical Reporter] Uploading blob report from ${blobDir}, files: ${files.join(", ")}`);
206
+ let sizeBytes = 0;
207
+ for (const file of files) {
208
+ const stat = await fs_1.default.promises.stat(path_1.default.join(blobDir, file));
209
+ sizeBytes += stat.size;
210
+ }
204
211
  await this._uploader.uploadDirectory(blobDir, "blobs");
205
212
  await this._uploader.waitForUploads();
206
213
  logger_1.logger.info("[Empirical Reporter] Blob report uploaded successfully");
214
+ const testRunId = process.env.TEST_RUN_GITHUB_ACTION_ID;
215
+ if (testRunId) {
216
+ const shardIndex = process.env.SHARD_INDEX
217
+ ? parseInt(process.env.SHARD_INDEX, 10)
218
+ : undefined;
219
+ await (0, telemetry_1.trackReportUpload)({
220
+ testRunId,
221
+ durationMs: Date.now() - uploadStartTime,
222
+ shardIndex,
223
+ sizeBytes,
224
+ });
225
+ }
207
226
  }
208
227
  catch (error) {
209
228
  logger_1.logger.error("[Empirical Reporter] Failed to upload blob report:", error);
@@ -1,11 +1,19 @@
1
1
  import { TelemetryEvent } from "@empiricalrun/shared-types/telemetry";
2
2
  export declare function trackEvent(event: TelemetryEvent): Promise<void>;
3
+ interface TrackReportUploadParams {
4
+ testRunId: string;
5
+ durationMs: number;
6
+ shardIndex?: number;
7
+ sizeBytes: number;
8
+ }
9
+ export declare function trackReportUpload(params: TrackReportUploadParams): Promise<void>;
3
10
  export declare function trackOverlayDismissed(opts: {
4
11
  original: string | undefined;
5
12
  description: string;
6
13
  text: string | undefined;
7
14
  cache: boolean;
8
15
  success: boolean;
16
+ durationMs: number;
9
17
  testName?: string;
10
18
  tokens?: {
11
19
  input: number;
@@ -15,5 +23,6 @@ export declare function trackOverlayDismissed(opts: {
15
23
  input: number;
16
24
  output: number;
17
25
  };
18
- }): void;
26
+ }): Promise<void>;
27
+ export {};
19
28
  //# sourceMappingURL=telemetry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAKtE,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,GAAG,IAAI,CAqBP"}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAKtE,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAED,UAAU,uBAAuB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB"}
package/dist/telemetry.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.trackEvent = trackEvent;
4
+ exports.trackReportUpload = trackReportUpload;
4
5
  exports.trackOverlayDismissed = trackOverlayDismissed;
5
6
  const TELEMETRY_WORKER_URL = "https://telemetry-worker.empirical-run.workers.dev";
6
7
  async function trackEvent(event) {
@@ -25,9 +26,24 @@ async function trackEvent(event) {
25
26
  // Don't throw - telemetry failures shouldn't break the application
26
27
  }
27
28
  }
28
- function trackOverlayDismissed(opts) {
29
+ async function trackReportUpload(params) {
30
+ const environment = process.env.ENVIRONMENT || "unknown";
31
+ await trackEvent({
32
+ name: "test_worker.segment.report_upload",
33
+ properties: {
34
+ test_run_id: params.testRunId,
35
+ duration_ms: params.durationMs,
36
+ size_bytes: params.sizeBytes,
37
+ environment,
38
+ ...(params.shardIndex !== undefined && {
39
+ shard_index: params.shardIndex,
40
+ }),
41
+ },
42
+ });
43
+ }
44
+ async function trackOverlayDismissed(opts) {
29
45
  const environment = process.env.TEST_RUN_ENVIRONMENT || "unknown";
30
- void trackEvent({
46
+ await trackEvent({
31
47
  name: "overlay.dismissed",
32
48
  properties: {
33
49
  environment,
@@ -44,6 +60,7 @@ function trackOverlayDismissed(opts) {
44
60
  tokens_output: opts.tokens?.output ?? 0,
45
61
  cost_input: opts.cost?.input ?? 0,
46
62
  cost_output: opts.cost?.output ?? 0,
63
+ duration_ms: opts.durationMs,
47
64
  },
48
65
  });
49
66
  }
@@ -0,0 +1,4 @@
1
+ export type { MatcherResult } from "./types";
2
+ export { toLookRight } from "./visual";
3
+ export { toHaveReceivedWebhook, type WebhookMatcherOptions } from "./webhook";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/expect/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toHaveReceivedWebhook = exports.toLookRight = void 0;
4
+ var visual_1 = require("./visual");
5
+ Object.defineProperty(exports, "toLookRight", { enumerable: true, get: function () { return visual_1.toLookRight; } });
6
+ var webhook_1 = require("./webhook");
7
+ Object.defineProperty(exports, "toHaveReceivedWebhook", { enumerable: true, get: function () { return webhook_1.toHaveReceivedWebhook; } });
@@ -0,0 +1,5 @@
1
+ export type MatcherResult = {
2
+ message: () => string;
3
+ pass: boolean;
4
+ };
5
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/test/expect/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import type { Page } from "@playwright/test";
2
+ import type { MatcherResult } from "./types";
3
+ export declare function toLookRight(page: Page, pageDescription: string): Promise<MatcherResult>;
4
+ //# sourceMappingURL=visual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAM7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA0B7C,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CAkFxB"}
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toLookRight = toLookRight;
4
4
  const llm_1 = require("@empiricalrun/llm");
5
5
  const vision_1 = require("@empiricalrun/llm/vision");
6
- const mouse_pointer_1 = require("./scripts/mouse-pointer");
6
+ const mouse_pointer_1 = require("../scripts/mouse-pointer");
7
7
  const SYSTEM_PROMPT = `
8
8
  You are a software QA tester who specializes in visual testing. You look at a
9
9
  UI screenshot and evaluate whether the UI is rendered correctly. While you don't
@@ -0,0 +1,7 @@
1
+ import type { MatcherResult } from "./types";
2
+ export type WebhookMatcherOptions = {
3
+ content: string | string[];
4
+ timeout?: number;
5
+ };
6
+ export declare function toHaveReceivedWebhook(webhookUrl: string, options: WebhookMatcherOptions): Promise<MatcherResult>;
7
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/test/expect/webhook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,aAAa,CAAC,CAmCxB"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toHaveReceivedWebhook = toHaveReceivedWebhook;
4
+ const logger_1 = require("../../logger");
5
+ const webhook_1 = require("../../webhook");
6
+ const POLL_INTERVAL = 6_000;
7
+ const DEFAULT_TIMEOUT = 300_000;
8
+ async function toHaveReceivedWebhook(webhookUrl, options) {
9
+ const { content, timeout = DEFAULT_TIMEOUT } = options;
10
+ const deadline = Date.now() + timeout;
11
+ let lastError;
12
+ while (true) {
13
+ try {
14
+ const requests = await (0, webhook_1.queryWebhookRequests)(webhookUrl, content);
15
+ if (requests[0]) {
16
+ return {
17
+ message: () => `Expected not to receive webhook matching content ${JSON.stringify(content)}, but received it`,
18
+ pass: true,
19
+ };
20
+ }
21
+ lastError = undefined;
22
+ }
23
+ catch (e) {
24
+ lastError = e;
25
+ logger_1.logger.error("Webhook provider error during polling", e);
26
+ }
27
+ const remaining = deadline - Date.now();
28
+ if (remaining <= 0)
29
+ break;
30
+ await new Promise((r) => setTimeout(r, Math.min(POLL_INTERVAL, remaining)));
31
+ }
32
+ return {
33
+ message: () => {
34
+ const base = `Webhook not received for content ${JSON.stringify(content)} within ${timeout}ms`;
35
+ return lastError
36
+ ? `${base}. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`
37
+ : base;
38
+ },
39
+ pass: false,
40
+ };
41
+ }
@@ -1,6 +1,7 @@
1
1
  import type { BrowserContext, BrowserContextOptions, expect, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from "@playwright/test";
2
2
  import { KVClient } from "../kv";
3
3
  import { PostgresClient } from "../postgres";
4
+ import { type WebhookMatcherOptions } from "./expect";
4
5
  import { injectLocatorHighlightScripts } from "./scripts";
5
6
  import { HighlighterOpts } from "./types";
6
7
  import { setVideoLabel } from "./video-labels";
@@ -8,6 +9,7 @@ declare global {
8
9
  namespace PlaywrightTest {
9
10
  interface Matchers<R> {
10
11
  toLookRight(description: string): Promise<R>;
12
+ toHaveReceivedWebhook(options: WebhookMatcherOptions): Promise<R>;
11
13
  }
12
14
  }
13
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SAC9C;KACF;CACF;AAED,QAAA,MAAM,YAAY,GAAa,gBAAgB,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,GACnB,QAAQ,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,EACD,UAAS,eAAgC,uHA2G1C,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC7C,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SACnE;KACF;CACF;AAED,QAAA,MAAM,YAAY,GAAa,gBAAgB,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,GACnB,QAAQ,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,EACD,UAAS,eAAgC,uHA2G1C,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
@@ -13,7 +13,7 @@ Object.defineProperty(exports, "injectLocatorHighlightScripts", { enumerable: tr
13
13
  const video_labels_1 = require("./video-labels");
14
14
  Object.defineProperty(exports, "setVideoLabel", { enumerable: true, get: function () { return video_labels_1.setVideoLabel; } });
15
15
  const extendExpect = function (expectInstance) {
16
- expectInstance.extend({ toLookRight: expect_1.toLookRight });
16
+ expectInstance.extend({ toLookRight: expect_1.toLookRight, toHaveReceivedWebhook: expect_1.toHaveReceivedWebhook });
17
17
  return expectInstance;
18
18
  };
19
19
  exports.extendExpect = extendExpect;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;gBAEoB,IAAI,EAAE,IAAI;IAEjC,WAAW;IAMX,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;YAqDF,QAAQ;CA6BvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;gBAEoB,IAAI,EAAE,IAAI;IAEjC,WAAW;IAMX,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;YAyDF,QAAQ;CA6BvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
@@ -27,6 +27,7 @@ class OverlayDismissAgent {
27
27
  }
28
28
  }
29
29
  async run(originalError, reporter, testFn) {
30
+ const startTime = Date.now();
30
31
  let element;
31
32
  try {
32
33
  element = extractInterceptingElement(originalError.message);
@@ -51,28 +52,31 @@ class OverlayDismissAgent {
51
52
  text,
52
53
  });
53
54
  if (success) {
54
- (0, telemetry_1.trackOverlayDismissed)({
55
+ await (0, telemetry_1.trackOverlayDismissed)({
55
56
  ...eventProps,
56
57
  cache: true,
57
58
  success: true,
59
+ durationMs: Date.now() - startTime,
58
60
  });
59
61
  return;
60
62
  }
61
63
  const usage = await this.runAgent(element, text, testFn);
62
- (0, telemetry_1.trackOverlayDismissed)({
64
+ await (0, telemetry_1.trackOverlayDismissed)({
63
65
  ...eventProps,
64
66
  cache: false,
65
67
  success: true,
66
68
  tokens: usage.tokens,
67
69
  cost: usage.cost,
70
+ durationMs: Date.now() - startTime,
68
71
  });
69
72
  }
70
73
  catch (agentError) {
71
74
  reporter(`Error during overlay dismissal agent execution: ${agentError.toString()}`);
72
- (0, telemetry_1.trackOverlayDismissed)({
75
+ await (0, telemetry_1.trackOverlayDismissed)({
73
76
  ...eventProps,
74
77
  cache: false,
75
78
  success: false,
79
+ durationMs: Date.now() - startTime,
76
80
  });
77
81
  throw originalError;
78
82
  }
@@ -0,0 +1,20 @@
1
+ export type WebhookRequest = {
2
+ uuid: string;
3
+ token_id: string;
4
+ method: string;
5
+ content: string;
6
+ headers: Record<string, string[]>;
7
+ url: string;
8
+ query: Record<string, string> | null;
9
+ ip: string;
10
+ hostname: string;
11
+ user_agent: string;
12
+ size: number;
13
+ created_at: string;
14
+ updated_at: string;
15
+ };
16
+ export declare function getWebhookUrl(options?: {
17
+ expiry?: number | null;
18
+ }): Promise<string>;
19
+ export declare function queryWebhookRequests(webhookUrl: string, content: string | string[]): Promise<WebhookRequest[]>;
20
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAaF,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACvC,OAAO,CAAC,MAAM,CAAC,CAuCjB;AA2FD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAM3B"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWebhookUrl = getWebhookUrl;
4
+ exports.queryWebhookRequests = queryWebhookRequests;
5
+ const dashboard_1 = require("@empiricalrun/test-gen/dashboard");
6
+ const WEBHOOK_SITE_BASE_URL = "https://webhook.site";
7
+ const DEFAULT_TOKEN_EXPIRY = 604800;
8
+ async function getWebhookUrl(options = {}) {
9
+ const expiry = options.expiry === undefined ? DEFAULT_TOKEN_EXPIRY : options.expiry;
10
+ if (process.env.EMPIRICALRUN_API_KEY) {
11
+ const apiClient = new dashboard_1.DashboardAPIClient({
12
+ authType: "project-api-key",
13
+ });
14
+ const result = await apiClient.callWebhookSiteProxy({
15
+ method: "POST",
16
+ path: "/token",
17
+ body: { expiry },
18
+ });
19
+ return `${WEBHOOK_SITE_BASE_URL}/${result.uuid}`;
20
+ }
21
+ const apiKey = process.env.WEBHOOK_SITE_API_KEY;
22
+ if (!apiKey) {
23
+ throw new Error("Either EMPIRICALRUN_API_KEY or WEBHOOK_SITE_API_KEY must be set");
24
+ }
25
+ const response = await fetch(`${WEBHOOK_SITE_BASE_URL}/token`, {
26
+ method: "POST",
27
+ headers: {
28
+ "Api-Key": apiKey,
29
+ "Content-Type": "application/json",
30
+ },
31
+ body: JSON.stringify({ expiry }),
32
+ });
33
+ if (!response.ok) {
34
+ const body = await response.text().catch(() => "");
35
+ throw new Error(`webhook.site error [${response.status} ${response.statusText}]: ${body}`);
36
+ }
37
+ const { uuid } = await response.json();
38
+ return `${WEBHOOK_SITE_BASE_URL}/${uuid}`;
39
+ }
40
+ function extractToken(webhookUrl) {
41
+ let url;
42
+ try {
43
+ url = new URL(webhookUrl);
44
+ }
45
+ catch {
46
+ throw new Error(`Invalid webhook URL: ${webhookUrl}`);
47
+ }
48
+ const parts = url.pathname.split("/").filter(Boolean);
49
+ const token = parts[parts.length - 1];
50
+ if (!token) {
51
+ throw new Error(`Invalid webhook URL (no token in path): ${webhookUrl}`);
52
+ }
53
+ return token;
54
+ }
55
+ function escapeQueryTerm(term) {
56
+ return term.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
57
+ }
58
+ function buildContentQuery(content) {
59
+ const terms = Array.isArray(content) ? content : [content];
60
+ if (terms.length === 0 || terms.some((t) => t.length === 0)) {
61
+ throw new Error("content must be a non-empty string or string[]");
62
+ }
63
+ return terms.map((t) => `content:"${escapeQueryTerm(t)}"`).join(" AND ");
64
+ }
65
+ async function queryViaProxy(token, content) {
66
+ const apiClient = new dashboard_1.DashboardAPIClient({
67
+ authType: "project-api-key",
68
+ });
69
+ const params = new URLSearchParams({
70
+ query: buildContentQuery(content),
71
+ sorting: "newest",
72
+ per_page: "20",
73
+ });
74
+ const result = await apiClient.callWebhookSiteProxy({
75
+ method: "GET",
76
+ path: `/token/${token}/requests?${params.toString()}`,
77
+ });
78
+ if (!result.data || !Array.isArray(result.data)) {
79
+ throw new Error("webhook.site proxy response missing 'data' array");
80
+ }
81
+ return result.data;
82
+ }
83
+ async function queryDirect(token, content) {
84
+ const apiKey = process.env.WEBHOOK_SITE_API_KEY;
85
+ if (!apiKey) {
86
+ throw new Error("Either EMPIRICALRUN_API_KEY or WEBHOOK_SITE_API_KEY must be set");
87
+ }
88
+ const params = new URLSearchParams({
89
+ query: buildContentQuery(content),
90
+ sorting: "newest",
91
+ per_page: "20",
92
+ });
93
+ const rawResponse = await fetch(`https://webhook.site/token/${token}/requests?${params.toString()}`, {
94
+ headers: {
95
+ "Api-Key": apiKey,
96
+ },
97
+ });
98
+ if (!rawResponse.ok) {
99
+ const body = await rawResponse.text().catch(() => "");
100
+ throw new Error(`webhook.site error [${rawResponse.status} ${rawResponse.statusText}]: ${body}`);
101
+ }
102
+ const json = await rawResponse.json().catch((e) => {
103
+ throw new Error(`webhook.site returned invalid JSON: ${String(e)}`);
104
+ });
105
+ if (!json || !Array.isArray(json.data)) {
106
+ throw new Error("webhook.site response missing 'data' array");
107
+ }
108
+ return json.data;
109
+ }
110
+ async function queryWebhookRequests(webhookUrl, content) {
111
+ const token = extractToken(webhookUrl);
112
+ if (process.env.EMPIRICALRUN_API_KEY) {
113
+ return queryViaProxy(token, content);
114
+ }
115
+ return queryDirect(token, content);
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.39.1",
3
+ "version": "0.39.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -43,8 +43,8 @@
43
43
  "puppeteer-extra-plugin-recaptcha": "^3.6.8",
44
44
  "rimraf": "^6.0.1",
45
45
  "@empiricalrun/llm": "^0.25.2",
46
- "@empiricalrun/r2-uploader": "^0.9.1",
47
- "@empiricalrun/test-gen": "^0.79.3"
46
+ "@empiricalrun/test-gen": "^0.79.3",
47
+ "@empiricalrun/r2-uploader": "^0.9.1"
48
48
  },
49
49
  "scripts": {
50
50
  "dev": "tsc --build --watch",
@@ -1 +1 @@
1
- {"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
1
+ {"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
@@ -1,7 +0,0 @@
1
- import type { Page } from "@playwright/test";
2
- export type VisualMatcherResult = {
3
- message: () => string;
4
- pass: boolean;
5
- };
6
- export declare function toLookRight(page: Page, pageDescription: string): Promise<VisualMatcherResult>;
7
- //# sourceMappingURL=expect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/test/expect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAO7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AA0BF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}