@empiricalrun/playwright-utils 0.64.0 → 0.65.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/api-worker-client.d.ts +3 -0
  3. package/dist/api-worker-client.d.ts.map +1 -1
  4. package/dist/api-worker-client.js +6 -0
  5. package/dist/generated/api-worker-openapi.d.ts +1775 -1279
  6. package/dist/generated/api-worker-openapi.d.ts.map +1 -1
  7. package/dist/overlay-tests/typesafe.spec.js +5 -3
  8. package/dist/overlay-tests/visual.spec.js +1 -1
  9. package/dist/reporter/attachment-cleanup-test-reporter.d.ts +3 -1
  10. package/dist/reporter/attachment-cleanup-test-reporter.d.ts.map +1 -1
  11. package/dist/reporter/attachment-cleanup-test-reporter.js +5 -1
  12. package/dist/reporter/attachment-cleanup.d.ts +1 -1
  13. package/dist/reporter/attachment-cleanup.d.ts.map +1 -1
  14. package/dist/reporter/attachment-cleanup.js +14 -7
  15. package/dist/reporter/attachment-key.d.ts +3 -0
  16. package/dist/reporter/attachment-key.d.ts.map +1 -0
  17. package/dist/reporter/attachment-key.js +31 -0
  18. package/dist/reporter/empirical-reporter.d.ts +1 -1
  19. package/dist/reporter/empirical-reporter.d.ts.map +1 -1
  20. package/dist/reporter/empirical-reporter.js +7 -6
  21. package/dist/reporter/harness.d.ts +6 -1
  22. package/dist/reporter/harness.d.ts.map +1 -1
  23. package/dist/reporter/harness.js +22 -13
  24. package/dist/reporter/incremental-blob-reporter.d.ts +1 -0
  25. package/dist/reporter/incremental-blob-reporter.d.ts.map +1 -1
  26. package/dist/reporter/incremental-blob-reporter.js +8 -4
  27. package/dist/reporter/presigned-upload.d.ts +15 -0
  28. package/dist/reporter/presigned-upload.d.ts.map +1 -0
  29. package/dist/reporter/presigned-upload.js +96 -0
  30. package/dist/reporter/uploader.d.ts +10 -1
  31. package/dist/reporter/uploader.d.ts.map +1 -1
  32. package/dist/reporter/uploader.js +51 -2
  33. package/dist/reporter/util.d.ts +3 -3
  34. package/dist/reporter/util.d.ts.map +1 -1
  35. package/dist/reporter/util.js +10 -8
  36. package/dist/test/expect/visual.d.ts.map +1 -1
  37. package/dist/test/expect/visual.js +6 -2
  38. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
  39. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +0 -2
  40. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.d.ts +0 -1
  41. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.d.ts.map +1 -1
  42. package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.js +0 -20
  43. package/dist/test/scripts/pw-locator-patch/vision/query.d.ts.map +1 -1
  44. package/dist/test/scripts/pw-locator-patch/vision/query.js +57 -5
  45. package/package.json +4 -5
  46. package/tsconfig.tsbuildinfo +1 -1
@@ -8,15 +8,21 @@ exports.getUploader = getUploader;
8
8
  exports.createUploader = createUploader;
9
9
  const r2_uploader_1 = require("@empiricalrun/r2-uploader");
10
10
  const path_1 = __importDefault(require("path"));
11
+ const api_worker_client_1 = require("../api-worker-client");
11
12
  const logger_1 = require("../logger");
13
+ const presigned_upload_1 = require("./presigned-upload");
14
+ const R2_REPORTS_BASE_URL = "https://reports-r2.empirical.run";
15
+ const R2_REPORTS_BUCKET = "test-report";
12
16
  class Uploader {
13
17
  _baseUrl;
14
18
  _uploadBucket;
19
+ _runId;
15
20
  _destinationDir;
16
21
  _credentials;
17
22
  constructor(config) {
18
23
  this._baseUrl = config.baseUrl;
19
24
  this._uploadBucket = config.uploadBucket;
25
+ this._runId = config.runId;
20
26
  this._destinationDir = `${config.projectSlug}/${config.runId}`;
21
27
  this._credentials = config.credentials;
22
28
  logger_1.logger.debug(`[Uploader] Initialized with destination: ${this._destinationDir}, provider: ${this._credentials.provider}`);
@@ -61,8 +67,31 @@ class Uploader {
61
67
  get urlPrefix() {
62
68
  return `${this._baseUrl}/${this._destinationDir}`;
63
69
  }
70
+ _presignedTask(credentials, files) {
71
+ return async () => {
72
+ try {
73
+ return await (0, presigned_upload_1.uploadPresigned)({
74
+ client: credentials.client,
75
+ runId: this._runId,
76
+ baseUrl: this._baseUrl,
77
+ destinationDir: this._destinationDir,
78
+ files,
79
+ });
80
+ }
81
+ catch (error) {
82
+ logger_1.logger.error("[Uploader] Error uploading files", error);
83
+ return {};
84
+ }
85
+ };
86
+ }
64
87
  async uploadFile(filePath, destinationPath, storageClass) {
65
88
  try {
89
+ if (this._credentials.provider === "r2-presigned") {
90
+ const result = await (0, r2_uploader_1.sendTaskToQueue)(this._presignedTask(this._credentials, [
91
+ { filePath, reportPath: destinationPath },
92
+ ]));
93
+ return (result && result[filePath]) || null;
94
+ }
66
95
  const fullDestinationPath = path_1.default.join(this._destinationDir, destinationPath);
67
96
  const sourceDir = path_1.default.dirname(filePath);
68
97
  const uploadTask = (0, r2_uploader_1.createUploadTask)({
@@ -90,6 +119,10 @@ class Uploader {
90
119
  }
91
120
  }
92
121
  async uploadDirectory(sourceDir, destinationPrefix) {
122
+ if (this._credentials.provider === "r2-presigned") {
123
+ await (0, r2_uploader_1.sendTaskToQueue)(this._presignedTask(this._credentials, (0, presigned_upload_1.directoryUploadFiles)(sourceDir, destinationPrefix)));
124
+ return;
125
+ }
93
126
  const fullDestinationDir = path_1.default.join(this._destinationDir, destinationPrefix);
94
127
  const uploadTask = (0, r2_uploader_1.createUploadTask)({
95
128
  sourceDir,
@@ -145,6 +178,9 @@ function createUploader() {
145
178
  logger_1.logger.debug(`[Uploader] REPORT_STORAGE_BACKEND must be "s3" or "r2" (got ${backend ?? "unset"})`);
146
179
  return null;
147
180
  }
181
+ if (backend === "r2" && process.env.REPORT_UPLOAD_PRESIGNED === "true") {
182
+ return createPresignedUploader(process.env.PROJECT_NAME, process.env.TEST_RUN_GITHUB_ACTION_ID);
183
+ }
148
184
  const enableS3 = backend === "s3";
149
185
  const credentials = Uploader.getCredentialsFromEnv(enableS3);
150
186
  if (!credentials) {
@@ -168,12 +204,12 @@ function createUploader() {
168
204
  }
169
205
  else {
170
206
  const accountId = process.env.R2_ACCOUNT_ID;
171
- const bucket = "test-report";
207
+ const bucket = R2_REPORTS_BUCKET;
172
208
  if (!accountId) {
173
209
  logger_1.logger.debug("[Uploader] R2_ACCOUNT_ID not configured");
174
210
  return null;
175
211
  }
176
- baseUrl = `https://reports-r2.empirical.run`;
212
+ baseUrl = R2_REPORTS_BASE_URL;
177
213
  uploadBucket = bucket;
178
214
  }
179
215
  return new Uploader({
@@ -184,3 +220,16 @@ function createUploader() {
184
220
  credentials,
185
221
  });
186
222
  }
223
+ function createPresignedUploader(projectSlug, runId) {
224
+ if (!process.env.EMPIRICALRUN_API_KEY) {
225
+ logger_1.logger.debug("[Uploader] EMPIRICALRUN_API_KEY is required for presigned uploads");
226
+ return null;
227
+ }
228
+ return new Uploader({
229
+ projectSlug,
230
+ runId,
231
+ baseUrl: R2_REPORTS_BASE_URL,
232
+ uploadBucket: R2_REPORTS_BUCKET,
233
+ credentials: { provider: "r2-presigned", client: new api_worker_client_1.ApiWorkerClient() },
234
+ });
235
+ }
@@ -20,8 +20,8 @@ export declare function suitesAndProjectForTest(test: TestCase): {
20
20
  export declare function sendTestCaseUpdateToDashboard(params: TestCaseRunEndEventV2): Promise<void>;
21
21
  export declare function sendTestCaseBeginToDashboard(params: TestCaseRunBeginEvent): Promise<void>;
22
22
  export declare function safelySerialiseJSON(obj: any, keyFilter?: (key: string) => boolean): any;
23
- export declare function updateHtmlZipFileAttachmentPaths(jsonFilePath: string, htmlFilePath: string, repoPath: string): Promise<void>;
24
- export declare function buildTestAttachmentMapFromSummary(summaryJsonFilePath: string, repoPath: string, doNotNormalize?: boolean): TestAttachmentMap;
23
+ export declare function updateHtmlZipFileAttachmentPaths(jsonFilePath: string, htmlFilePath: string, outputDirs: string[]): Promise<void>;
24
+ export declare function buildTestAttachmentMapFromSummary(summaryJsonFilePath: string, outputDirs: string[], doNotNormalize?: boolean): TestAttachmentMap;
25
25
  export declare function checkFileExistsAsync(filePath: string): Promise<boolean>;
26
- export declare function updateSummaryJsonAttachmentPaths(filePath: string, repoPath: string, urlPrefix: string): Promise<PlaywrightJSONReport | undefined>;
26
+ export declare function updateSummaryJsonAttachmentPaths(filePath: string, outputDirs: string[], urlPrefix: string): Promise<PlaywrightJSONReport | undefined>;
27
27
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAQhE,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,UAAU,IAAI,oBAAoB,EAElC,QAAQ,EACT,MAAM,2BAA2B,CAAC;AA0BnC,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB7D;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,UAQhC;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5E,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CA6CD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ;IAmBlD,MAAM;IACN,WAAW;EAEd;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACnC,GAAG,CAIL;AAED,wBAAsB,gCAAgC,CACpD,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,iBAkFjB;AAED,wBAAgB,iCAAiC,CAC/C,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,OAAe,GAC9B,iBAAiB,CAgBnB;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7E;AAED,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,6CA+BlB"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAQhE,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,UAAU,IAAI,oBAAoB,EAElC,QAAQ,EACT,MAAM,2BAA2B,CAAC;AA2BnC,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB7D;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,UAQhC;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5E,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CA6CD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ;IAmBlD,MAAM;IACN,WAAW;EAEd;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACnC,GAAG,CAIL;AAED,wBAAsB,gCAAgC,CACpD,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAAE,iBAkFrB;AAED,wBAAgB,iCAAiC,CAC/C,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,GAAE,OAAe,GAC9B,iBAAiB,CAkBnB;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7E;AAED,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,MAAM,6CA8BlB"}
@@ -22,6 +22,7 @@ const mime_1 = __importDefault(require("mime"));
22
22
  const path_1 = __importDefault(require("path"));
23
23
  const api_worker_client_1 = require("../api-worker-client");
24
24
  const logger_1 = require("../logger");
25
+ const attachment_key_1 = require("./attachment-key");
25
26
  // Mirrors https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/tools/cli-client/program.ts#L440
26
27
  function calculateSha1(buffer) {
27
28
  const hash = crypto_1.default.createHash("sha1");
@@ -173,8 +174,8 @@ async function sendTestCaseBeginToDashboard(params) {
173
174
  function safelySerialiseJSON(obj, keyFilter) {
174
175
  return JSON.parse(JSON.stringify(obj, (key, value) => (keyFilter?.(key) ? undefined : value)));
175
176
  }
176
- async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath, repoPath) {
177
- const testAttachmentMap = buildTestAttachmentMapFromSummary(jsonFilePath, repoPath);
177
+ async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath, outputDirs) {
178
+ const testAttachmentMap = buildTestAttachmentMapFromSummary(jsonFilePath, outputDirs);
178
179
  let htmlFile;
179
180
  try {
180
181
  htmlFile = await fs_1.default.promises.readFile(htmlFilePath, "utf8");
@@ -232,7 +233,7 @@ async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath, repo
232
233
  finally {
233
234
  }
234
235
  }
235
- function buildTestAttachmentMapFromSummary(summaryJsonFilePath, repoPath, doNotNormalize = false) {
236
+ function buildTestAttachmentMapFromSummary(summaryJsonFilePath, outputDirs, doNotNormalize = false) {
236
237
  const summaryJson = JSON.parse(fs_1.default.readFileSync(summaryJsonFilePath, "utf8"));
237
238
  return (0, reporter_1.buildTestAttachmentMap)(summaryJson, {
238
239
  transformAttachment: doNotNormalize
@@ -241,8 +242,10 @@ function buildTestAttachmentMapFromSummary(summaryJsonFilePath, repoPath, doNotN
241
242
  if (!attachment?.path) {
242
243
  return attachment;
243
244
  }
244
- const relativePath = path_1.default.relative(repoPath, attachment.path);
245
- return { ...attachment, path: path_1.default.join("data", relativePath) };
245
+ return {
246
+ ...attachment,
247
+ path: path_1.default.join("data", (0, attachment_key_1.attachmentKey)(attachment.path, outputDirs)),
248
+ };
246
249
  },
247
250
  });
248
251
  }
@@ -255,7 +258,7 @@ async function checkFileExistsAsync(filePath) {
255
258
  return false;
256
259
  }
257
260
  }
258
- async function updateSummaryJsonAttachmentPaths(filePath, repoPath, urlPrefix) {
261
+ async function updateSummaryJsonAttachmentPaths(filePath, outputDirs, urlPrefix) {
259
262
  if (!process.env.PROJECT_NAME || !process.env.TEST_RUN_GITHUB_ACTION_ID) {
260
263
  console.error("PROJECT_NAME and TEST_RUN_GITHUB_ACTION_ID must be set");
261
264
  return;
@@ -266,8 +269,7 @@ async function updateSummaryJsonAttachmentPaths(filePath, repoPath, urlPrefix) {
266
269
  for (const result of test.results || []) {
267
270
  for (const attachment of result.attachments || []) {
268
271
  if (attachment?.path) {
269
- const relativePath = path_1.default.relative(repoPath, attachment.path);
270
- attachment.path = `${urlPrefix}/${relativePath}`;
272
+ attachment.path = `${urlPrefix}/${(0, attachment_key_1.attachmentKey)(attachment.path, outputDirs)}`;
271
273
  }
272
274
  }
273
275
  }
@@ -1 +1 @@
1
- {"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAOhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAgE7C,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CAsFxB"}
1
+ {"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAOhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAsE7C,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CAsFxB"}
@@ -4,13 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.toLookRight = toLookRight;
7
- const vision_1 = require("@empiricalrun/llm/vision");
8
7
  const fs_1 = __importDefault(require("fs"));
9
8
  const openai_1 = __importDefault(require("openai"));
10
9
  const os_1 = __importDefault(require("os"));
11
10
  const path_1 = __importDefault(require("path"));
12
11
  const api_worker_client_1 = require("../../api-worker-client");
13
12
  const telemetry_1 = require("../../telemetry");
13
+ function asImageDataUrl(image) {
14
+ return image.startsWith("data:image")
15
+ ? image
16
+ : `data:image/png;base64,${image}`;
17
+ }
14
18
  function getCallerLine() {
15
19
  const stack = new Error().stack;
16
20
  if (!stack) {
@@ -91,7 +95,7 @@ async function toLookRight(target, pageDescription) {
91
95
  {
92
96
  type: "image_url",
93
97
  image_url: {
94
- url: (0, vision_1.imageFormatForProvider)("anthropic", base64Image),
98
+ url: asImageDataUrl(base64Image),
95
99
  },
96
100
  },
97
101
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAW9C,wBAAgB,gBAAgB,IAAI,SAAS,CAI5C;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;IAET,YAA6B,IAAI,EAAE,IAAI,EAAI;IAErC,WAAW,kBAIhB;IAEK,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf;YAEa,OAAO;YAwDP,QAAQ;CAkCvB;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":"AAAA,OAAO,EACL,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAU9C,wBAAgB,gBAAgB,IAAI,SAAS,CAI5C;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;IAET,YAA6B,IAAI,EAAE,IAAI,EAAI;IAErC,WAAW,kBAIhB;IAEK,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf;YAEa,OAAO;YAwDP,QAAQ;CAgCvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
@@ -91,11 +91,9 @@ class OverlayDismissAgent {
91
91
  }
92
92
  async runAgent(element, text, testFn) {
93
93
  const task = await (0, prompt_1.getTask)(this.page, element, text);
94
- const trace = (0, utils_1.traceThis)(task, testFn);
95
94
  const result = await testFn.step(utils_1.DISMISSAL_AGENT_STEP, () => (0, cua_1.createTestUsingComputerUseAgent)({
96
95
  page: this.page,
97
96
  task,
98
- trace,
99
97
  // We don't want to use page.pause approach here because
100
98
  // overlay dismissal can kick in during a browser agent tool call
101
99
  // and we need app to be able to use page.pause for agent actions.
@@ -8,5 +8,4 @@ export declare function dismissOverlayStepTitle(element: OverlayElement | undefi
8
8
  export declare function isProductFruitsOverlay(element: OverlayElement | undefined): boolean | undefined;
9
9
  export declare function textContent(pageRef: Page, element: OverlayElement | undefined): Promise<string | undefined>;
10
10
  export declare function annotateForReport(testFn: TestFn, description: string): void;
11
- export declare function traceThis(task: string, testFn: TestFn): import("@empiricalrun/llm").TraceClient | undefined;
12
11
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAElE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,UAO1E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+BAgDpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAMpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,uDAiBrD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAElE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,UAO1E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+BAgDpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAMpE"}
@@ -6,8 +6,6 @@ exports.dismissOverlayStepTitle = dismissOverlayStepTitle;
6
6
  exports.isProductFruitsOverlay = isProductFruitsOverlay;
7
7
  exports.textContent = textContent;
8
8
  exports.annotateForReport = annotateForReport;
9
- exports.traceThis = traceThis;
10
- const llm_1 = require("@empiricalrun/llm");
11
9
  function overlayDescription(element) {
12
10
  if (element) {
13
11
  const { interceptor, parent } = element;
@@ -85,21 +83,3 @@ function annotateForReport(testFn, description) {
85
83
  description,
86
84
  });
87
85
  }
88
- function traceThis(task, testFn) {
89
- const metadata = {
90
- projectName: process.env.PROJECT_NAME ?? "",
91
- testRunId: process.env.TEST_RUN_GITHUB_ACTION_ID ?? "",
92
- testCase: testFn.info().title,
93
- };
94
- const trace = (0, llm_1.createLangfuseTrace)({
95
- name: "overlay_dismiss",
96
- input: task,
97
- tags: ["overlay_dismiss"],
98
- metadata,
99
- });
100
- if (trace) {
101
- const traceUrl = trace.getTraceUrl();
102
- console.log(`Starting computer use agent: ${traceUrl}`);
103
- }
104
- return trace;
105
- }
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/vision/query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIhD,wBAAgB,UAAU,CAAC,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,QASzE"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/vision/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAiEhD,wBAAgB,UAAU,CAAC,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,QAMzE"}
@@ -1,15 +1,67 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.patchQuery = patchQuery;
4
- const vision_1 = require("@empiricalrun/llm/vision");
7
+ const openai_1 = __importDefault(require("openai"));
5
8
  const api_worker_client_1 = require("../../../../api-worker-client");
9
+ const telemetry_1 = require("../../../../telemetry");
10
+ const QUERY_MODEL = "gpt-4o-mini";
11
+ const SYSTEM_PROMPT = `
12
+ You are given an image and an instruction. Use the "explanation" to
13
+ think it through, and then respond with the "answer".`;
14
+ async function queryScreenshot(base64Image, instruction) {
15
+ const openai = new openai_1.default({ maxRetries: 5, ...(0, api_worker_client_1.llmProxyOpenAIConfig)() });
16
+ const completion = await openai.chat.completions.create({
17
+ model: QUERY_MODEL,
18
+ messages: [
19
+ { role: "system", content: SYSTEM_PROMPT },
20
+ {
21
+ role: "user",
22
+ content: [
23
+ {
24
+ type: "image_url",
25
+ image_url: { url: `data:image/png;base64,${base64Image}` },
26
+ },
27
+ { type: "text", text: `Instruction: ${instruction}` },
28
+ ],
29
+ },
30
+ ],
31
+ temperature: 0.5,
32
+ response_format: {
33
+ type: "json_schema",
34
+ json_schema: {
35
+ name: "your_response",
36
+ strict: true,
37
+ schema: {
38
+ type: "object",
39
+ properties: {
40
+ explanation: { type: "string" },
41
+ answer: { type: "string" },
42
+ },
43
+ required: ["explanation", "answer"],
44
+ additionalProperties: false,
45
+ },
46
+ },
47
+ },
48
+ });
49
+ await (0, telemetry_1.trackLLMResponseGenerated)({
50
+ modelName: QUERY_MODEL,
51
+ tokensInput: completion.usage?.prompt_tokens ?? 0,
52
+ tokensOutput: completion.usage?.completion_tokens ?? 0,
53
+ chatSessionSource: "vision_query",
54
+ });
55
+ const content = completion.choices[0]?.message?.content;
56
+ if (!content) {
57
+ throw new Error("Query failed: no response content from LLM.");
58
+ }
59
+ return JSON.parse(content).answer;
60
+ }
6
61
  function patchQuery(LocatorClass) {
7
62
  LocatorClass.prototype.query = async function (instruction) {
8
63
  await this.waitFor({ state: "visible" });
9
64
  const screenshot = await this.screenshot();
10
- const answer = await (0, vision_1.query)(screenshot.toString("base64"), instruction, {
11
- clientConfig: (0, api_worker_client_1.llmProxyOpenAIConfig)(),
12
- });
13
- return answer;
65
+ return queryScreenshot(screenshot.toString("base64"), instruction);
14
66
  };
15
67
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.64.0",
3
+ "version": "0.65.1",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -45,7 +45,7 @@
45
45
  "playwright-core": "1.63.0",
46
46
  "serve-handler": "^6.1.6",
47
47
  "typescript": "7.0.2",
48
- "@empiricalrun/shared-types": "0.22.0"
48
+ "@empiricalrun/shared-types": "0.23.2"
49
49
  },
50
50
  "dependencies": {
51
51
  "async-retry": "^1.3.3",
@@ -56,9 +56,8 @@
56
56
  "openapi-fetch": "0.14.1",
57
57
  "puppeteer-extra-plugin-recaptcha": "^3.6.8",
58
58
  "ts-morph": "^28.0.0",
59
- "@empiricalrun/cua": "^0.5.4",
60
- "@empiricalrun/llm": "^0.28.3",
61
- "@empiricalrun/r2-uploader": "^0.11.4",
59
+ "@empiricalrun/cua": "^0.6.0",
60
+ "@empiricalrun/r2-uploader": "^0.11.5",
62
61
  "@empiricalrun/reporter": "^0.30.3"
63
62
  },
64
63
  "scripts": {