@empiricalrun/test-gen 0.65.0 → 0.66.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 (66) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/agent/browsing/run.d.ts.map +1 -1
  3. package/dist/agent/browsing/run.js +1 -0
  4. package/dist/agent/chat/index.d.ts +1 -2
  5. package/dist/agent/chat/index.d.ts.map +1 -1
  6. package/dist/agent/chat/index.js +8 -4
  7. package/dist/agent/chat/{prompt.d.ts → prompt/index.d.ts} +2 -2
  8. package/dist/agent/chat/prompt/index.d.ts.map +1 -0
  9. package/dist/agent/chat/{prompt.js → prompt/index.js} +14 -63
  10. package/dist/agent/chat/prompt/pw-utils-docs.d.ts +2 -0
  11. package/dist/agent/chat/prompt/pw-utils-docs.d.ts.map +1 -0
  12. package/dist/agent/chat/prompt/pw-utils-docs.js +62 -0
  13. package/dist/agent/chat/{repo.d.ts → prompt/repo.d.ts} +1 -1
  14. package/dist/agent/chat/prompt/repo.d.ts.map +1 -0
  15. package/dist/agent/chat/{repo.js → prompt/repo.js} +1 -1
  16. package/dist/agent/cua/computer.js +1 -1
  17. package/dist/agent/cua/index.d.ts.map +1 -1
  18. package/dist/agent/cua/index.js +10 -5
  19. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts +7 -5
  20. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -1
  21. package/dist/agent/cua/pw-codegen/pw-pause/index.js +57 -29
  22. package/dist/agent/cua/pw-codegen/pw-pause/ipc.d.ts +3 -0
  23. package/dist/agent/cua/pw-codegen/pw-pause/ipc.d.ts.map +1 -0
  24. package/dist/agent/cua/pw-codegen/pw-pause/ipc.js +13 -0
  25. package/dist/agent/cua/pw-codegen/pw-pause/patch.d.ts +12 -0
  26. package/dist/agent/cua/pw-codegen/pw-pause/patch.d.ts.map +1 -0
  27. package/dist/agent/cua/pw-codegen/pw-pause/{utils.js → patch.js} +85 -12
  28. package/dist/artifacts/utils.d.ts.map +1 -1
  29. package/dist/artifacts/utils.js +14 -3
  30. package/dist/bin/index.js +1 -3
  31. package/dist/bin/utils/index.d.ts +0 -1
  32. package/dist/bin/utils/index.d.ts.map +1 -1
  33. package/dist/bin/utils/index.js +2 -0
  34. package/dist/tool-call-service/index.d.ts.map +1 -1
  35. package/dist/tool-call-service/index.js +2 -0
  36. package/dist/tool-call-service/utils.d.ts +1 -0
  37. package/dist/tool-call-service/utils.d.ts.map +1 -1
  38. package/dist/tool-call-service/utils.js +18 -11
  39. package/dist/tools/commit-and-create-pr.d.ts +0 -6
  40. package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
  41. package/dist/tools/commit-and-create-pr.js +20 -40
  42. package/dist/tools/str_replace_editor.d.ts +3 -2
  43. package/dist/tools/str_replace_editor.d.ts.map +1 -1
  44. package/dist/tools/str_replace_editor.js +44 -7
  45. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  46. package/dist/tools/test-gen-browser.js +8 -0
  47. package/dist/tools/upgrade-packages/index.d.ts +3 -0
  48. package/dist/tools/upgrade-packages/index.d.ts.map +1 -0
  49. package/dist/tools/upgrade-packages/index.js +124 -0
  50. package/dist/tools/upgrade-packages/utils.d.ts +13 -0
  51. package/dist/tools/upgrade-packages/utils.d.ts.map +1 -0
  52. package/dist/tools/upgrade-packages/utils.js +106 -0
  53. package/dist/tools/utils/index.d.ts +50 -2
  54. package/dist/tools/utils/index.d.ts.map +1 -1
  55. package/dist/tools/utils/index.js +87 -0
  56. package/dist/uploader/index.d.ts.map +1 -1
  57. package/dist/uploader/index.js +1 -0
  58. package/dist/utils/git.d.ts +12 -1
  59. package/dist/utils/git.d.ts.map +1 -1
  60. package/dist/utils/git.js +66 -1
  61. package/package.json +6 -6
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/dist/agent/chat/prompt.d.ts.map +0 -1
  64. package/dist/agent/chat/repo.d.ts.map +0 -1
  65. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts +0 -16
  66. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.66.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 1a6bb19: chore: upgrade playwright in devDependencies
8
+ - d3639c0: fix: update browsing agent artifact path for 1.53
9
+ - 881e856: fix: update system prompt to get PRs as output
10
+ - cfb157a: fix: follow-ups for playwright reporter upgrade
11
+ - 4062787: feat: update page.pause codegen to work with playwright 1.53
12
+ - Updated dependencies [cfb157a]
13
+ - Updated dependencies [a533ee5]
14
+ - Updated dependencies [4062787]
15
+ - @empiricalrun/test-run@0.10.3
16
+ - @empiricalrun/llm@0.18.2
17
+
18
+ ## 0.66.0
19
+
20
+ ### Minor Changes
21
+
22
+ - 576870a: feat: allow specifying package version in upgradePackages tool
23
+ - e94a2da: feat: update upgradePackages tool to include input schema for package selection
24
+ - dfb2007: feat: add upgradePackages tool
25
+
26
+ ### Patch Changes
27
+
28
+ - 33abadf: fix: text appendBranchNameToQueueUrl output is under 80 characters fixed
29
+ - 1300a80: fix: branch name with dot should deploy tool execute service
30
+ - 985d721: fix: git patch for new files
31
+ - 6a19421: feat: collect git patch artifacts from text editor tools
32
+ - 1a46013: feat: page.pause codegen works when tool execution is headed
33
+ - 805f35f: fix: handle error when fetching environment variables in chat agent
34
+ - 0a9ec78: feat: add o3 with reduced costs
35
+ - c430cc0: fix: use mouse wheel to execute scroll in cua
36
+ - Updated dependencies [9b873e3]
37
+ - Updated dependencies [41c266d]
38
+ - Updated dependencies [0a9ec78]
39
+ - Updated dependencies [8a83b29]
40
+ - @empiricalrun/test-run@0.10.2
41
+ - @empiricalrun/llm@0.18.1
42
+
3
43
  ## 0.65.0
4
44
 
5
45
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAclE,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAG5C,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,OAAO,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF,wBAAsB,8BAA8B,CAAC,EACnD,gBAAgB,EAChB,OAAO,EACP,YAAY,GACb,EAAE;IACD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlB;AAED,wBAAsB,gBAAgB,CAAC,EACrC,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,OAAO,EACP,yBAAyB,EACzB,YAAY,GACb,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACvC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC,CAkGD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAclE,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAG5C,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,OAAO,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF,wBAAsB,8BAA8B,CAAC,EACnD,gBAAgB,EAChB,OAAO,EACP,YAAY,GACb,EAAE;IACD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlB;AAED,wBAAsB,gBAAgB,CAAC,EACrC,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,OAAO,EACP,yBAAyB,EACzB,YAAY,GACb,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACvC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IACzB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC,CAmGD"}
@@ -51,6 +51,7 @@ async function runBrowsingAgent({ testCaseName, testCaseSuites, testFilePath, fi
51
51
  projects: [projectName],
52
52
  passthroughArgs: "--retries 0 --timeout 0",
53
53
  repoDir,
54
+ envOverrides: envOverrides,
54
55
  // @ts-ignore
55
56
  platform: "web",
56
57
  });
@@ -1,9 +1,8 @@
1
1
  import { SupportedChatModels } from "@empiricalrun/shared-types";
2
- export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, withRetry, }: {
2
+ export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }: {
3
3
  selectedModel: SupportedChatModels;
4
4
  useDiskForChatState: boolean;
5
5
  initialPromptContent: string | undefined;
6
- withRetry?: boolean;
7
6
  }): Promise<void>;
8
7
  export declare function runChatAgentForDashboard({ chatSessionId, selectedModel, }: {
9
8
  selectedModel: SupportedChatModels;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AA8DpC,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,SAAS,GACV,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,iBAwHA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBA6DA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAkEpC,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBA6HA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBA6DA"}
@@ -30,13 +30,17 @@ async function fetchEnvironmentVariables() {
30
30
  },
31
31
  });
32
32
  const data = await response.json();
33
+ if (!data.data) {
34
+ console.error("Failed to fetch environment variables:", data);
35
+ throw new Error("Failed to fetch environment variables");
36
+ }
33
37
  const envVars = data.data.environment_variables.reduce((acc, envVar) => {
34
38
  acc[envVar.name] = envVar.value;
35
39
  return acc;
36
40
  }, {});
37
41
  return envVars;
38
42
  }
39
- async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, withRetry, }) {
43
+ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }) {
40
44
  let chatState;
41
45
  if (useDiskForChatState) {
42
46
  chatState = (0, state_1.loadChatState)();
@@ -47,6 +51,7 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
47
51
  await (0, git_1.checkoutBranch)(branchName, process.cwd());
48
52
  let messagesLoadedFromDisk = chatState?.messages || [];
49
53
  let chatModel = (0, chat_1.createChatModel)(messagesLoadedFromDisk, selectedModel);
54
+ chatModel.validateEnvVarsForAuth();
50
55
  if (initialPromptContent && chatModel.messages.length === 0) {
51
56
  chatModel.pushUserMessage(initialPromptContent);
52
57
  chatModel.askUserForInput = false;
@@ -61,8 +66,8 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
61
66
  console.log(`${(0, picocolors_1.blue)(latest.role)}: ${latest.textMessage}`);
62
67
  }
63
68
  }
64
- // if withRetry set the chatState error null
65
- if (withRetry && chatState) {
69
+ if (chatState && chatState.error) {
70
+ // Reset error state as we are attempting a retry
66
71
  chatState.error = null;
67
72
  }
68
73
  const handleSigInt = () => {
@@ -114,7 +119,6 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
114
119
  }
115
120
  }
116
121
  else {
117
- // TODO: Should we pass a loader function? That would allow us to show a spinner
118
122
  if (!process.env.EMPIRICALRUN_API_KEY) {
119
123
  throw new Error("EMPIRICALRUN_API_KEY is not set");
120
124
  }
@@ -1,3 +1,3 @@
1
- import { FileInfo } from "../../types";
1
+ import { FileInfo } from "../../../types";
2
2
  export declare function buildSystemPrompt(fileInfo: FileInfo): Promise<string>;
3
- //# sourceMappingURL=prompt.d.ts.map
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,mBA0GzD"}
@@ -1,70 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildSystemPrompt = buildSystemPrompt;
4
+ const pw_utils_docs_1 = require("./pw-utils-docs");
4
5
  const repo_1 = require("./repo");
5
- const emailRecipe = `
6
- # Email automation
7
-
8
- ## Example usage
9
-
10
- ### Dynamic email
11
-
12
- This dynamically generates a random email address that can
13
- be used for the test (e.g. invite a new user).
14
-
15
- \`\`\`ts
16
- import { EmailClient } from "@empiricalrun/playwright-utils";
17
- import { expect } from "@playwright/test";
18
-
19
- const client = new EmailClient();
20
- const address = client.getAddress();
21
-
22
- // Input the \`address\` in the application
23
- // that sends the email.
24
-
25
- // Get email received on the \`address\`
26
- const email = await client.waitForEmail();
27
- expect(
28
- email.links.find((l) => l.text === "Join your team")
29
- ).toBeTruthy();
30
- \`\`\`
31
-
32
- ### Static email
33
-
34
- This uses a known (static) email that can be used to login
35
- into an application.
36
-
37
- This needs an email id (e.g. \`test-login-user\`). The email id
38
- is appended with the domain (managed internally) to get the full
39
- email address.
40
-
41
- \`\`\`ts
42
- import { EmailClient } from "@empiricalrun/playwright-utils";
43
-
44
- const emailId = \`test-login-user\`;
45
-
46
- const client = new EmailClient({ emailId });
47
- const address = client.getAddress(); // Returns full address with domain
48
-
49
- // Get email received on the \`address\`
50
- const email = await client.waitForEmail();
51
-
52
- // Get login OTP
53
- const loginCode = email.codes[0];
54
- \`\`\`
55
- `;
56
6
  async function buildSystemPrompt(fileInfo) {
57
- const repoContext = await (0, repo_1.getRepoInfoPrompt)(fileInfo);
58
- return `
59
- You are a helpful assistant that can answer questions and help with tasks related to writing
60
- and maintaining Playwright tests.
7
+ const preamble = `
8
+ You are a helpful assistant that can answer questions and help with tasks related to writing and maintaining Playwright tests.
9
+
10
+ You are working on a test code repository that contains Playwright tests and other related files. If you have modified
11
+ any files, your end output should be a pull request that will be reviewed and merged by a human.
61
12
 
62
- # Supported capabilities
13
+ # Your capabilities
63
14
 
64
15
  - Adding new Playwright tests or helper methods
65
16
  - Going through test reports and identifying app issues versus test issues
66
- - Modifying existing tests
67
- - Modifying repo configuration (e.g. in playwright.config.ts and other)
17
+ - Modifying existing tests to adapt to changes in the application
18
+ - Modifying repo configuration (e.g. in playwright.config.ts) and dependencies (e.g. in package.json)
68
19
 
69
20
  # Going through test reports
70
21
 
@@ -147,12 +98,12 @@ if (await saveButton.isVisible()) {
147
98
  }
148
99
  \`\`\`
149
100
 
150
- # Recipes
151
- You can refer to the following recipes to learn how to write tests for different scenarios.
101
+ `;
102
+ const repoContext = await (0, repo_1.getRepoInfoPrompt)(fileInfo);
103
+ return `${preamble}
152
104
 
153
- <email-automation>
154
- ${emailRecipe}
155
- </email-automation>
105
+ # Recipes
106
+ ${pw_utils_docs_1.playwrightUtilsDocs}
156
107
 
157
108
  # Repo context
158
109
  ${repoContext}
@@ -0,0 +1,2 @@
1
+ export declare const playwrightUtilsDocs = "\nYou can refer to the following recipes to learn how to write tests for different scenarios.\n\n<email-automation>\n\n# Email automation\n\n## Example usage\n\n### Dynamic email\n\nThis dynamically generates a random email address that can \nbe used for the test (e.g. invite a new user).\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\nimport { expect } from \"@playwright/test\";\n\nconst client = new EmailClient();\nconst address = client.getAddress();\n\n// Input the `address` in the application\n// that sends the email.\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\nexpect(\n email.links.find((l) => l.text === \"Join your team\")\n).toBeTruthy();\n```\n\n### Static email\n\nThis uses a known (static) email that can be used to login\ninto an application.\n\nThis needs an email id (e.g. `test-login-user`). The email id\nis appended with the domain (managed internally) to get the full\nemail address.\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\n\nconst emailId = `test-login-user`;\n\nconst client = new EmailClient({ emailId });\nconst address = client.getAddress(); // Returns full address with domain\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\n\n// Get login OTP\nconst loginCode = email.codes[0];\n```\n\n</email-automation>\n\n";
2
+ //# sourceMappingURL=pw-utils-docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pw-utils-docs.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/pw-utils-docs.ts"],"names":[],"mappings":"AAoDA,eAAO,MAAM,mBAAmB,i3CAO/B,CAAC"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.playwrightUtilsDocs = void 0;
4
+ const emailRecipe = `
5
+ # Email automation
6
+
7
+ ## Example usage
8
+
9
+ ### Dynamic email
10
+
11
+ This dynamically generates a random email address that can
12
+ be used for the test (e.g. invite a new user).
13
+
14
+ \`\`\`ts
15
+ import { EmailClient } from "@empiricalrun/playwright-utils";
16
+ import { expect } from "@playwright/test";
17
+
18
+ const client = new EmailClient();
19
+ const address = client.getAddress();
20
+
21
+ // Input the \`address\` in the application
22
+ // that sends the email.
23
+
24
+ // Get email received on the \`address\`
25
+ const email = await client.waitForEmail();
26
+ expect(
27
+ email.links.find((l) => l.text === "Join your team")
28
+ ).toBeTruthy();
29
+ \`\`\`
30
+
31
+ ### Static email
32
+
33
+ This uses a known (static) email that can be used to login
34
+ into an application.
35
+
36
+ This needs an email id (e.g. \`test-login-user\`). The email id
37
+ is appended with the domain (managed internally) to get the full
38
+ email address.
39
+
40
+ \`\`\`ts
41
+ import { EmailClient } from "@empiricalrun/playwright-utils";
42
+
43
+ const emailId = \`test-login-user\`;
44
+
45
+ const client = new EmailClient({ emailId });
46
+ const address = client.getAddress(); // Returns full address with domain
47
+
48
+ // Get email received on the \`address\`
49
+ const email = await client.waitForEmail();
50
+
51
+ // Get login OTP
52
+ const loginCode = email.codes[0];
53
+ \`\`\`
54
+ `;
55
+ exports.playwrightUtilsDocs = `
56
+ You can refer to the following recipes to learn how to write tests for different scenarios.
57
+
58
+ <email-automation>
59
+ ${emailRecipe}
60
+ </email-automation>
61
+
62
+ `;
@@ -1,3 +1,3 @@
1
- import { FileInfo } from "../../types";
1
+ import { FileInfo } from "../../../types";
2
2
  export declare function getRepoInfoPrompt(directory: FileInfo): Promise<string>;
3
3
  //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/repo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAwC1C,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,QAAQ,mBAyC1D"}
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getRepoInfoPrompt = getRepoInfoPrompt;
7
7
  const path_1 = __importDefault(require("path"));
8
- const repo_tree_1 = require("../../utils/repo-tree");
8
+ const repo_tree_1 = require("../../../utils/repo-tree");
9
9
  async function getAllMarkdownFiles(directory) {
10
10
  if (!directory.isDirectory) {
11
11
  return [];
@@ -148,7 +148,7 @@ async function executeModelAction(page, action, codegen) {
148
148
  scroll_y,
149
149
  });
150
150
  await page.mouse.move(x, y);
151
- await page.evaluate(`window.scrollBy(${scroll_x}, ${scroll_y})`);
151
+ await page.mouse.wheel(scroll_x, scroll_y);
152
152
  actionSummary = `Scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`;
153
153
  actionCode = await codegen.getCodeForLastAction();
154
154
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAStE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAwBlC,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAClC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CACJ,CAAC;AAEF,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,EACJ,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;CACd,CAAC,CAuND"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAStE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAgClC,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAClC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CACJ,CAAC;AAEF,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,EACJ,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;CACd,CAAC,CAuND"}
@@ -9,12 +9,17 @@ const openai_1 = __importDefault(require("openai"));
9
9
  const computer_1 = require("./computer");
10
10
  const model_1 = require("./model");
11
11
  const element_from_point_1 = require("./pw-codegen/element-from-point");
12
+ const pw_pause_1 = require("./pw-codegen/pw-pause");
12
13
  const MAX_ITERATIONS = 15;
13
- function getCodegen() {
14
+ async function getCodegenInstance() {
15
+ const repoDir = process.cwd();
16
+ const canUsePwPause = await (0, pw_pause_1.canUsePauseCodegen)(repoDir);
17
+ if (canUsePwPause) {
18
+ console.log("[getCodegen] using PlaywrightPauseCodegen");
19
+ return new pw_pause_1.PlaywrightPauseCodegen();
20
+ }
21
+ console.log("[getCodegen] using ElementFromPointCodegen");
14
22
  return new element_from_point_1.ElementFromPointCodegen();
15
- // TODO: Add support for page.pause approach
16
- // We can use PlaywrightPauseCodegen if playwright patch was successful,
17
- // IPC port is available and PW_CODEGEN_NO_INSPECTOR env var is set
18
23
  }
19
24
  function artifact(screenshot, name) {
20
25
  return {
@@ -24,7 +29,7 @@ function artifact(screenshot, name) {
24
29
  };
25
30
  }
26
31
  async function createTestUsingComputerUseAgent({ page, task, trace, }) {
27
- const codegen = getCodegen();
32
+ const codegen = await getCodegenInstance();
28
33
  await codegen.initialize(page);
29
34
  const screenshot = await (0, computer_1.getScreenshot)(page);
30
35
  const initialArtifact = artifact(screenshot, "Initial screen");
@@ -1,15 +1,17 @@
1
1
  import type { Page } from "playwright";
2
- export { revertToOriginalPwCode } from "./utils";
3
- export declare const PW_PAUSE_IPC_PORT = 3039;
4
- export declare function preparePlaywrightForCodegen(repoDir: string): Promise<void>;
5
- export declare class PlaywrightPauseCodegen {
2
+ import { BasePlaywrightCodegen } from "../types";
3
+ export { preparePlaywrightForCodegen, revertToOriginalPwCode } from "./patch";
4
+ export declare function canUsePauseCodegen(repoDir: string): Promise<boolean>;
5
+ export declare class PlaywrightPauseCodegen implements BasePlaywrightCodegen {
6
6
  private port;
7
7
  private page;
8
8
  private server;
9
+ private codeForLastAction;
9
10
  constructor();
11
+ private saveCode;
10
12
  initialize(page: Page): Promise<void>;
11
13
  startPlaywrightCodegen(page: Page): Promise<void>;
12
14
  recordAction(): Promise<void>;
13
- getCodegenResult(): Promise<string>;
15
+ getCodeForLastAction(): Promise<string>;
14
16
  }
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,MAAM,iBAoBhE;AAGD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;;IAMpD,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;IAsBjC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;CAI1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAIjD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAE9E,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,oBAqCvD;AAiBD,qBAAa,sBAAuB,YAAW,qBAAqB;IAClE,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;;YAMhC,QAAQ;IAOhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;IAsBjC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CAU9C"}
@@ -3,45 +3,67 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PlaywrightPauseCodegen = exports.PW_PAUSE_IPC_PORT = exports.revertToOriginalPwCode = void 0;
7
- exports.preparePlaywrightForCodegen = preparePlaywrightForCodegen;
6
+ exports.PlaywrightPauseCodegen = exports.revertToOriginalPwCode = exports.preparePlaywrightForCodegen = void 0;
7
+ exports.canUsePauseCodegen = canUsePauseCodegen;
8
8
  const express_1 = __importDefault(require("express"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
11
- const utils_1 = require("./utils");
12
- var utils_2 = require("./utils");
13
- Object.defineProperty(exports, "revertToOriginalPwCode", { enumerable: true, get: function () { return utils_2.revertToOriginalPwCode; } });
14
- exports.PW_PAUSE_IPC_PORT = 3039;
15
- async function preparePlaywrightForCodegen(repoDir) {
16
- const npmListOutput = await (0, utils_1.runNpmList)(repoDir);
17
- if (!npmListOutput) {
18
- return;
9
+ const ipc_1 = require("./ipc");
10
+ const patch_1 = require("./patch");
11
+ var patch_2 = require("./patch");
12
+ Object.defineProperty(exports, "preparePlaywrightForCodegen", { enumerable: true, get: function () { return patch_2.preparePlaywrightForCodegen; } });
13
+ Object.defineProperty(exports, "revertToOriginalPwCode", { enumerable: true, get: function () { return patch_2.revertToOriginalPwCode; } });
14
+ async function canUsePauseCodegen(repoDir) {
15
+ const hasPatchedPwCode = await (0, patch_1.hasPatchedRecorderApp)(repoDir);
16
+ if (!hasPatchedPwCode) {
17
+ // This codegen approach requires patching the playwright code to setup an
18
+ // IPC server <> client that passes generated code from Playwright to @empiricalrun/test-gen
19
+ console.log("Cannot use pause codegen because recorder app is not patched");
20
+ return false;
19
21
  }
20
- const playwrightCorePath = (0, utils_1.getPlaywrightCoreFromNpmList)(npmListOutput);
21
- const pathToRecorderApp = path_1.default.join(playwrightCorePath, "lib", "server", "recorder", "recorderApp.js");
22
- if (!fs_1.default.existsSync(pathToRecorderApp)) {
23
- const errMsg = `Cannot patch Playwright: ${pathToRecorderApp} does not exist`;
24
- throw new Error(errMsg);
22
+ if (!(await (0, ipc_1.isIPCPortAvailable)())) {
23
+ // The IPC port is hardcoded, and it should be available for IPC to work
24
+ console.log("Cannot use pause codegen because IPC port is not available");
25
+ return false;
25
26
  }
26
- await (0, utils_1.createFileBackup)(pathToRecorderApp);
27
- const port = exports.PW_PAUSE_IPC_PORT;
28
- await (0, utils_1.patchPwCode)(pathToRecorderApp, port);
27
+ const isRunningHeaded = process.env.RUN_PLAYWRIGHT_HEADED &&
28
+ process.env.RUN_PLAYWRIGHT_HEADED === "true";
29
+ if (!isRunningHeaded) {
30
+ // This codegen approach requires running Playwright in headed mode because
31
+ // it depends on the page.pause() API, which is no-op in headless mode
32
+ console.log("Cannot use pause codegen because RUN_PLAYWRIGHT_HEADED is not true");
33
+ return false;
34
+ }
35
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
36
+ if (!process.env.PW_CODEGEN_NO_INSPECTOR) {
37
+ // PW_CODEGEN_NO_INSPECTOR should be set to true, so that Playwright runs the
38
+ // EmptyRecorderApp() -- which is the entrypoint to our patched code
39
+ // Ref: https://github.com/microsoft/playwright/blob/c96558d0691c0377dbc0c772a1bd8120d546e6d7/packages/playwright-core/src/server/recorder/recorderApp.ts#L98
40
+ console.log("Cannot use pause codegen because PW_CODEGEN_NO_INSPECTOR is not set");
41
+ return false;
42
+ }
43
+ // Need all conditions to be met to use pause codegen
44
+ return true;
29
45
  }
30
- // TODO: Fix this to implement BasePlaywrightCodegen
31
46
  class PlaywrightPauseCodegen {
32
47
  port = 0;
33
48
  page;
34
49
  server;
50
+ codeForLastAction;
35
51
  constructor() {
36
- this.port = exports.PW_PAUSE_IPC_PORT;
52
+ this.port = ipc_1.PW_PAUSE_IPC_PORT;
53
+ }
54
+ async saveCode(code) {
55
+ const generatedCode = code.map((c) => c.actions.join("\n")).join("\n");
56
+ if (generatedCode) {
57
+ this.codeForLastAction = generatedCode;
58
+ }
37
59
  }
38
60
  async initialize(page) {
39
61
  // Start server to receive generated code from patch
40
62
  const app = (0, express_1.default)();
41
63
  app.use(express_1.default.json());
42
- app.post("/test", async (req, res) => {
43
- console.log("--- Received request in PlaywrightPatchCodegen ---");
44
- console.log(req.body);
64
+ app.post("/sources", async (req, res) => {
65
+ const { payload } = req.body;
66
+ await this.saveCode(JSON.parse(payload));
45
67
  return res.send({ success: true });
46
68
  });
47
69
  await new Promise((resolve) => {
@@ -74,11 +96,17 @@ class PlaywrightPauseCodegen {
74
96
  await page.pause();
75
97
  }
76
98
  async recordAction() {
77
- console.log("Record action is no-op");
99
+ // Record action is no-op
78
100
  }
79
- async getCodegenResult() {
80
- console.log("Get codegen result is no-op");
81
- return "";
101
+ async getCodeForLastAction() {
102
+ // Wait for the server to send the code
103
+ await new Promise((resolve) => setTimeout(resolve, 1000));
104
+ let codeToReturn = this.codeForLastAction;
105
+ this.codeForLastAction = undefined;
106
+ if (!codeToReturn) {
107
+ throw new Error("No code for last action");
108
+ }
109
+ return codeToReturn;
82
110
  }
83
111
  }
84
112
  exports.PlaywrightPauseCodegen = PlaywrightPauseCodegen;
@@ -0,0 +1,3 @@
1
+ export declare const PW_PAUSE_IPC_PORT = 3039;
2
+ export declare function isIPCPortAvailable(): Promise<boolean>;
3
+ //# sourceMappingURL=ipc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/ipc.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,wBAAsB,kBAAkB,qBAGvC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PW_PAUSE_IPC_PORT = void 0;
7
+ exports.isIPCPortAvailable = isIPCPortAvailable;
8
+ const detect_port_1 = __importDefault(require("detect-port"));
9
+ exports.PW_PAUSE_IPC_PORT = 3039;
10
+ async function isIPCPortAvailable() {
11
+ const portAvailable = await (0, detect_port_1.default)(exports.PW_PAUSE_IPC_PORT);
12
+ return portAvailable === exports.PW_PAUSE_IPC_PORT;
13
+ }
@@ -0,0 +1,12 @@
1
+ export declare function preparePlaywrightForCodegen(repoDir: string): Promise<void>;
2
+ export declare function getPathToRecorderApp(pwCorePath: string): Promise<string>;
3
+ export declare function runNpmList(repoDir: string): Promise<string | undefined>;
4
+ export declare function getPlaywrightCoreFromNpmList(output: string): {
5
+ path: string;
6
+ version: "1.47" | "1.53";
7
+ };
8
+ export declare function hasPatchedRecorderApp(repoDir: string): Promise<boolean | undefined>;
9
+ export declare function revertToOriginalPwCode(repoDir: string): Promise<void>;
10
+ export declare function createFileBackup(pathToFile: string): Promise<void>;
11
+ export declare function patchPwCode(pwVersion: "1.47" | "1.53", pathToRecorderApp: string, port: number): Promise<void>;
12
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/patch.ts"],"names":[],"mappings":"AAmBA,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,MAAM,iBAehE;AAED,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,mBAgB5D;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,+BAiB/C;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CA8BA;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,gCAkB1D;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,iBAoB3D;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,iBAGxD;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,MAAM,iBA2Bb"}