@empiricalrun/playwright-utils 0.25.23 → 0.25.24

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,11 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.25.24
4
+
5
+ ### Patch Changes
6
+
7
+ - e5430e4: fix: cache overlay actions only if original action passes
8
+
3
9
  ## 0.25.23
4
10
 
5
11
  ### Patch Changes
@@ -63,7 +63,7 @@ async function executeFromCache({ page, dom, text, }) {
63
63
  return { success: false };
64
64
  }
65
65
  try {
66
- console.log(`Executing "${match.code}" for element: ${dom}`);
66
+ console.log(`Executing from cache: "${match.code}" for element: ${dom}`);
67
67
  // Ref: https://davidwalsh.name/async-function-class
68
68
  const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
69
69
  const exec = new AsyncFunction("page", match.code);
@@ -1,6 +1,13 @@
1
1
  import { Page } from "@playwright/test";
2
2
  import { OverlayElement } from "./types";
3
3
  export declare function isErrorSupported(errorMessage: string | undefined): boolean;
4
+ export declare class OverlayDismissAgent {
5
+ private readonly page;
6
+ private cachingCandidates;
7
+ constructor(page: Page);
8
+ saveToCache(): Promise<void>;
9
+ run(originalError: any, reporter: (description: string) => void): Promise<void>;
10
+ private runAgent;
11
+ }
4
12
  export declare function extractInterceptingElement(errorMessage: string): OverlayElement | undefined;
5
- export declare function attemptToDismissOverlay(originalError: any, page: Page, reporter: (description: string) => void): Promise<void>;
6
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAOzC,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AA8DD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B;AAwBD,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,GAAG,EAClB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,IAAI,CAAC,CAiBf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAOzC,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,GACtC,OAAO,CAAC,IAAI,CAAC;YA4BF,QAAQ;CAgCvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.attemptToDismissOverlay = exports.extractInterceptingElement = exports.isErrorSupported = void 0;
4
- const llm_1 = require("@empiricalrun/llm");
3
+ exports.extractInterceptingElement = exports.OverlayDismissAgent = exports.isErrorSupported = void 0;
5
4
  const run_1 = require("@empiricalrun/test-gen/agent/master/run");
6
5
  const cache_1 = require("./cache");
7
6
  const prompt_1 = require("./prompt");
@@ -15,62 +14,76 @@ function isErrorSupported(errorMessage) {
15
14
  return errorMessage.includes(ERROR_SUBSTRING_INTERCEPTION);
16
15
  }
17
16
  exports.isErrorSupported = isErrorSupported;
18
- function traceThis(task) {
19
- const metadata = {
20
- projectName: process.env.PROJECT_NAME ?? "",
21
- testRunId: process.env.TEST_RUN_GITHUB_ACTION_ID ?? "",
22
- };
23
- const trace = (0, llm_1.createLangfuseTrace)({
24
- name: "overlay_dismiss",
25
- input: task,
26
- tags: ["overlay_dismiss"],
27
- metadata,
28
- });
29
- if (trace) {
30
- const traceUrl = trace.getTraceUrl();
31
- console.log(`Starting computer use agent: ${traceUrl}`);
32
- }
33
- return trace;
34
- }
35
- async function runAgentOnOverlay(pageRef, element) {
36
- const text = await (0, utils_1.textContent)(pageRef, element);
37
- const task = await (0, prompt_1.getTask)(pageRef, element, text);
38
- const { success } = await (0, cache_1.executeFromCache)({
39
- page: pageRef,
40
- dom: element?.interceptor,
41
- text,
42
- });
43
- if (success) {
44
- return;
45
- }
46
- let generatedCode;
47
- if (PREFERRED_AGENT === "inhouse-master") {
48
- const result = await (0, run_1.createTestUsingMasterAgent)({
49
- task,
50
- page: pageRef,
51
- options: {
52
- useActionSpecificAnnotations: true,
53
- },
54
- });
55
- generatedCode = result.code;
56
- }
57
- else if (PREFERRED_AGENT === "openai-cua") {
58
- const trace = traceThis(task);
59
- const result = await (0, run_1.createTestUsingComputerUseAgent)({
60
- page: pageRef,
61
- task,
62
- trace,
63
- });
64
- generatedCode = result.code;
65
- }
66
- if (generatedCode) {
67
- await (0, cache_1.setCodeToCache)({
68
- dom: element?.interceptor,
69
- text,
70
- code: generatedCode,
71
- });
17
+ class OverlayDismissAgent {
18
+ page;
19
+ cachingCandidates = [];
20
+ constructor(page) {
21
+ this.page = page;
22
+ }
23
+ async saveToCache() {
24
+ for (const candidate of this.cachingCandidates) {
25
+ await (0, cache_1.setCodeToCache)(candidate);
26
+ }
27
+ }
28
+ async run(originalError, reporter) {
29
+ let element;
30
+ try {
31
+ element = extractInterceptingElement(originalError.message);
32
+ }
33
+ catch (err) {
34
+ // Ignoring this error
35
+ }
36
+ const description = (0, utils_1.overlayDescription)(element);
37
+ reporter(`Attempting to auto-dismiss overlay: ${description}`);
38
+ try {
39
+ const text = await (0, utils_1.textContent)(this.page, element);
40
+ const { success } = await (0, cache_1.executeFromCache)({
41
+ page: this.page,
42
+ dom: element?.interceptor,
43
+ text,
44
+ });
45
+ if (success) {
46
+ return;
47
+ }
48
+ await this.runAgent(element, text);
49
+ }
50
+ catch (agentError) {
51
+ reporter(`Error during overlay dismissal agent execution: ${agentError.toString()}`);
52
+ throw originalError;
53
+ }
54
+ }
55
+ async runAgent(element, text) {
56
+ const task = await (0, prompt_1.getTask)(this.page, element, text);
57
+ if (PREFERRED_AGENT === "inhouse-master") {
58
+ const result = await (0, run_1.createTestUsingMasterAgent)({
59
+ task,
60
+ page: this.page,
61
+ options: {
62
+ useActionSpecificAnnotations: true,
63
+ },
64
+ });
65
+ this.cachingCandidates.push({
66
+ dom: element?.interceptor,
67
+ text,
68
+ code: result.code,
69
+ });
70
+ }
71
+ else if (PREFERRED_AGENT === "openai-cua") {
72
+ const trace = (0, utils_1.traceThis)(task);
73
+ const result = await (0, run_1.createTestUsingComputerUseAgent)({
74
+ page: this.page,
75
+ task,
76
+ trace,
77
+ });
78
+ this.cachingCandidates.push({
79
+ dom: element?.interceptor,
80
+ text,
81
+ code: result.code,
82
+ });
83
+ }
72
84
  }
73
85
  }
86
+ exports.OverlayDismissAgent = OverlayDismissAgent;
74
87
  function extractInterceptingElement(errorMessage) {
75
88
  // This extract element and parent info from interception error message
76
89
  // Note that error message from the last retry is returned.
@@ -128,22 +141,3 @@ function findAllMatches(str, regex) {
128
141
  }
129
142
  return matches;
130
143
  }
131
- async function attemptToDismissOverlay(originalError, page, reporter) {
132
- let overlayElement;
133
- try {
134
- overlayElement = extractInterceptingElement(originalError.message);
135
- }
136
- catch (err) {
137
- // Ignoring this error
138
- }
139
- const description = (0, utils_1.overlayDescription)(overlayElement);
140
- reporter(`Attempting to auto-dismiss overlay: ${description}`);
141
- try {
142
- await runAgentOnOverlay(page, overlayElement);
143
- }
144
- catch (agentError) {
145
- reporter(`Error during overlay dismissal agent execution: ${agentError.toString()}`);
146
- throw originalError;
147
- }
148
- }
149
- exports.attemptToDismissOverlay = attemptToDismissOverlay;
@@ -5,4 +5,5 @@ export declare function overlayDescription(element: OverlayElement | undefined):
5
5
  export declare function isProductFruitsOverlay(element: OverlayElement | undefined): boolean | undefined;
6
6
  export declare function textContent(pageRef: Page, element: OverlayElement | undefined): Promise<string | undefined>;
7
7
  export declare function annotateForReport(testFn: TestFn, description: string): void;
8
+ export declare function traceThis(task: string): import("@empiricalrun/llm").TraceClient | undefined;
8
9
  //# 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":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+BAiDpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAMpE"}
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,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+BAiDpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAMpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,uDAgBrC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.annotateForReport = exports.textContent = exports.isProductFruitsOverlay = exports.overlayDescription = void 0;
3
+ exports.traceThis = exports.annotateForReport = exports.textContent = exports.isProductFruitsOverlay = exports.overlayDescription = void 0;
4
+ const llm_1 = require("@empiricalrun/llm");
4
5
  function overlayDescription(element) {
5
6
  if (element) {
6
7
  const { interceptor, parent } = element;
@@ -75,3 +76,21 @@ function annotateForReport(testFn, description) {
75
76
  });
76
77
  }
77
78
  exports.annotateForReport = annotateForReport;
79
+ function traceThis(task) {
80
+ const metadata = {
81
+ projectName: process.env.PROJECT_NAME ?? "",
82
+ testRunId: process.env.TEST_RUN_GITHUB_ACTION_ID ?? "",
83
+ };
84
+ const trace = (0, llm_1.createLangfuseTrace)({
85
+ name: "overlay_dismiss",
86
+ input: task,
87
+ tags: ["overlay_dismiss"],
88
+ metadata,
89
+ });
90
+ if (trace) {
91
+ const traceUrl = trace.getTraceUrl();
92
+ console.log(`Starting computer use agent: ${traceUrl}`);
93
+ }
94
+ return trace;
95
+ }
96
+ exports.traceThis = traceThis;
@@ -12,17 +12,20 @@ function patchClick(LocatorClass, testFn) {
12
12
  }
13
13
  const reporter = (description) => (0, utils_1.annotateForReport)(testFn, description);
14
14
  const originalClick = LocatorClass.prototype.click;
15
- //ref: github.com/microsoft/playwright/blob/69287f26bc514b740eac40160503d6fac8185d37/packages/playwright-core/src/client/locator.ts#L255
15
+ // ref: github.com/microsoft/playwright/blob/69287f26bc514b740eac40160503d6fac8185d37/packages/playwright-core/src/client/locator.ts#L255
16
16
  LocatorClass.prototype.click = async function (options) {
17
17
  const stepName = `locator.click(${(0, utils_2.description)(this)})`;
18
18
  return await testFn.step(stepName, async () => {
19
19
  let overlayAttemptsRemaining = 2;
20
20
  let originalError;
21
+ const overlayDismiss = new dismiss_overlays_1.OverlayDismissAgent(this._frame._page);
21
22
  while (overlayAttemptsRemaining >= 0) {
22
23
  overlayAttemptsRemaining--;
23
24
  try {
24
25
  let result = await originalClick.apply(this, [options]);
25
- return result; // If click is successful, we're done -- just return
26
+ // If click is successful, we can save overlay dismissal code to cache and return
27
+ await overlayDismiss.saveToCache();
28
+ return result;
26
29
  }
27
30
  catch (error) {
28
31
  originalError = error;
@@ -36,7 +39,7 @@ function patchClick(LocatorClass, testFn) {
36
39
  // This will cause our overlay dismissal to not run.
37
40
  throw originalError;
38
41
  }
39
- await (0, dismiss_overlays_1.attemptToDismissOverlay)(originalError, this._frame._page, reporter);
42
+ await overlayDismiss.run(originalError, reporter);
40
43
  // Dismissal attempt finished (implicitly successful if no error thrown)
41
44
  reporter(`Overlay dismissal attempted, retrying original action.`);
42
45
  }
@@ -16,12 +16,15 @@ function patchHover(LocatorClass, testFn) {
16
16
  return await testFn.step(stepName, async () => {
17
17
  let overlayAttemptsRemaining = 2;
18
18
  let originalError;
19
+ const overlayDismiss = new dismiss_overlays_1.OverlayDismissAgent(this._frame._page);
19
20
  while (overlayAttemptsRemaining >= 0) {
20
21
  overlayAttemptsRemaining--;
21
22
  try {
22
23
  let result = await originalHover.apply(this, [options]);
23
24
  await (0, utils_2.highlight)(this);
24
- return result; // If hover is successful, we're done -- just return
25
+ // If hover is successful, we can save overlay dismissal code to cache and return
26
+ await overlayDismiss.saveToCache();
27
+ return result;
25
28
  }
26
29
  catch (error) {
27
30
  originalError = error;
@@ -31,7 +34,7 @@ function patchHover(LocatorClass, testFn) {
31
34
  if (!(0, dismiss_overlays_1.isErrorSupported)(originalError.message)) {
32
35
  throw originalError;
33
36
  }
34
- await (0, dismiss_overlays_1.attemptToDismissOverlay)(originalError, this._frame._page, reporter);
37
+ await overlayDismiss.run(originalError, reporter);
35
38
  reporter(`Overlay dismissal attempted, retrying original action.`);
36
39
  }
37
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.25.23",
3
+ "version": "0.25.24",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"