@empiricalrun/test-gen 0.66.0 → 0.66.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.
- package/CHANGELOG.md +26 -0
- package/dist/agent/browsing/run.js +4 -4
- package/dist/agent/browsing/utils.d.ts +1 -0
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +5 -4
- package/dist/agent/chat/index.d.ts +1 -0
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +2 -0
- package/dist/agent/chat/{prompt.d.ts → prompt/index.d.ts} +2 -2
- package/dist/agent/chat/prompt/index.d.ts.map +1 -0
- package/dist/agent/chat/{prompt.js → prompt/index.js} +17 -64
- package/dist/agent/chat/prompt/pw-utils-docs.d.ts +2 -0
- package/dist/agent/chat/prompt/pw-utils-docs.d.ts.map +1 -0
- package/dist/agent/chat/prompt/pw-utils-docs.js +62 -0
- package/dist/agent/chat/{repo.d.ts → prompt/repo.d.ts} +1 -1
- package/dist/agent/chat/prompt/repo.d.ts.map +1 -0
- package/dist/agent/chat/{repo.js → prompt/repo.js} +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.d.ts +14 -0
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.js +62 -0
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/index.js +19 -15
- package/dist/agent/cua/pw-codegen/pw-pause/patch.d.ts +7 -4
- package/dist/agent/cua/pw-codegen/pw-pause/patch.d.ts.map +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/patch.js +51 -24
- package/dist/agent/cua/pw-codegen/pw-pause/types.d.ts +14 -0
- package/dist/agent/cua/pw-codegen/pw-pause/types.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/types.js +2 -0
- package/dist/artifacts/utils.d.ts +1 -1
- package/dist/artifacts/utils.d.ts.map +1 -1
- package/dist/artifacts/utils.js +19 -8
- package/dist/bin/index.js +7 -1
- package/dist/bin/utils/index.d.ts +2 -1
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +34 -5
- package/dist/browser-injected-scripts/annotate-elements.js +1 -4
- package/dist/file/client.d.ts +1 -0
- package/dist/file/client.d.ts.map +1 -1
- package/dist/file/client.js +3 -0
- package/dist/file/server.d.ts +2 -0
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/recorder/display.d.ts +2 -0
- package/dist/recorder/display.d.ts.map +1 -0
- package/dist/recorder/display.js +50 -0
- package/dist/recorder/index.d.ts +4 -0
- package/dist/recorder/index.d.ts.map +1 -0
- package/dist/recorder/index.js +108 -0
- package/dist/recorder/request.d.ts +6 -0
- package/dist/recorder/request.d.ts.map +1 -0
- package/dist/recorder/request.js +55 -0
- package/dist/recorder/temp-files.d.ts +3 -0
- package/dist/recorder/temp-files.d.ts.map +1 -0
- package/dist/recorder/temp-files.js +39 -0
- package/dist/recorder/upload.d.ts +2 -0
- package/dist/recorder/upload.d.ts.map +1 -0
- package/dist/recorder/upload.js +85 -0
- package/dist/recorder/validation.d.ts +2 -0
- package/dist/recorder/validation.d.ts.map +1 -0
- package/dist/recorder/validation.js +24 -0
- package/dist/uploader/index.d.ts.map +1 -1
- package/dist/uploader/index.js +1 -0
- package/package.json +6 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/agent/chat/prompt.d.ts.map +0 -1
- package/dist/agent/chat/repo.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.66.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 45dfd06: feat: show version comparison in banner
|
|
8
|
+
- 7c66a47: feat: first working version of --use-recorder
|
|
9
|
+
- 5bc45e1: feat: create requests from recorder cli
|
|
10
|
+
- d0569de: fix: ensure browser agent is not stuck on page.pause
|
|
11
|
+
- fd719f1: feat: upload recorder video and attach to request
|
|
12
|
+
- a65a4d2: fix: path and ui issues
|
|
13
|
+
|
|
14
|
+
## 0.66.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 1a6bb19: chore: upgrade playwright in devDependencies
|
|
19
|
+
- d3639c0: fix: update browsing agent artifact path for 1.53
|
|
20
|
+
- 881e856: fix: update system prompt to get PRs as output
|
|
21
|
+
- cfb157a: fix: follow-ups for playwright reporter upgrade
|
|
22
|
+
- 4062787: feat: update page.pause codegen to work with playwright 1.53
|
|
23
|
+
- Updated dependencies [cfb157a]
|
|
24
|
+
- Updated dependencies [a533ee5]
|
|
25
|
+
- Updated dependencies [4062787]
|
|
26
|
+
- @empiricalrun/test-run@0.10.3
|
|
27
|
+
- @empiricalrun/llm@0.18.2
|
|
28
|
+
|
|
3
29
|
## 0.66.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
@@ -83,10 +83,10 @@ async function runBrowsingAgent({ testCaseName, testCaseSuites, testFilePath, fi
|
|
|
83
83
|
if (error) {
|
|
84
84
|
// Clean up the file if there is any error
|
|
85
85
|
try {
|
|
86
|
-
const fileContent = fs_1.default.readFileSync(
|
|
87
|
-
const updatedContent = (0, web_1.replaceCreateTestWithNewCode)(
|
|
88
|
-
fs_1.default.writeFileSync(
|
|
89
|
-
await (0, web_1.lintErrors)(
|
|
86
|
+
const fileContent = fs_1.default.readFileSync(absFilePathToUpdate, "utf-8");
|
|
87
|
+
const updatedContent = (0, web_1.replaceCreateTestWithNewCode)(absFilePathToUpdate, fileContent, "");
|
|
88
|
+
fs_1.default.writeFileSync(absFilePathToUpdate, updatedContent, "utf-8");
|
|
89
|
+
await (0, web_1.lintErrors)(absFilePathToUpdate);
|
|
90
90
|
}
|
|
91
91
|
catch (e) {
|
|
92
92
|
console.error(`[generateTestsUsingMasterAgent] Failed to remove extra scripts from files post test gen error:`, e);
|
|
@@ -4,6 +4,7 @@ import { Page } from "playwright";
|
|
|
4
4
|
import { PlaywrightTestConfig } from "playwright/test";
|
|
5
5
|
export declare function isRegExp(obj: any): obj is RegExp;
|
|
6
6
|
export declare function prepareBrowsingAgentTask(steps: string[]): string;
|
|
7
|
+
export declare function addImportForMethod(testFilePath: string, methodName: string): Promise<void>;
|
|
7
8
|
export declare function replaceTodoWithCreateTest(testFilePath: string, repoDir: string): Promise<void>;
|
|
8
9
|
export declare function markTestAsOnly({ testCaseName, testCaseSuites, specPath, }: {
|
|
9
10
|
testCaseName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAED,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,iBAgBnB;AAgED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,iBAwBhB;AAED,wBAAsB,cAAc,CAAC,EACnC,YAAY,EACZ,cAAc,EACd,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAoBA;AAED,wBAAsB,yBAAyB,CAAC,EAC9C,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDlB;AAyBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBA8HxD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,CA2B/B;AAWD,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,EACtC,gBAAgB,GAAE,MAAM,EAAU,GACjC,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isRegExp = isRegExp;
|
|
7
7
|
exports.prepareBrowsingAgentTask = prepareBrowsingAgentTask;
|
|
8
|
+
exports.addImportForMethod = addImportForMethod;
|
|
8
9
|
exports.replaceTodoWithCreateTest = replaceTodoWithCreateTest;
|
|
9
10
|
exports.markTestAsOnly = markTestAsOnly;
|
|
10
11
|
exports.prepareFileForMasterAgent = prepareFileForMasterAgent;
|
|
@@ -33,14 +34,14 @@ function prepareBrowsingAgentTask(steps) {
|
|
|
33
34
|
const task = `${sanitizedSteps.join("\n")}\n`;
|
|
34
35
|
return task;
|
|
35
36
|
}
|
|
36
|
-
async function
|
|
37
|
+
async function addImportForMethod(testFilePath, methodName) {
|
|
37
38
|
// Instead of using "@empiricalrun/test-gen", we use the local dist file
|
|
38
39
|
// This is to avoid assuming that the test-gen package is installed in the project
|
|
39
40
|
const importSource = path_1.default.join(__dirname, "../../../dist/index.js");
|
|
40
41
|
if (!fs_1.default.existsSync(importSource)) {
|
|
41
42
|
throw new Error(`createTest import source not found at ${importSource}`);
|
|
42
43
|
}
|
|
43
|
-
fs_1.default.writeFileSync(testFilePath, (0, web_1.addNewImport)(fs_1.default.readFileSync(testFilePath, "utf-8"), [
|
|
44
|
+
fs_1.default.writeFileSync(testFilePath, (0, web_1.addNewImport)(fs_1.default.readFileSync(testFilePath, "utf-8"), [methodName], importSource));
|
|
44
45
|
}
|
|
45
46
|
async function prepareFileForUpdateScenario({ testCase, specPath, trace, }) {
|
|
46
47
|
const { name, suites } = testCase;
|
|
@@ -77,7 +78,7 @@ async function prepareFileForUpdateScenario({ testCase, specPath, trace, }) {
|
|
|
77
78
|
},
|
|
78
79
|
});
|
|
79
80
|
await (0, web_1.appendScopeToCreateTest)(createTestFilePath, scopeVariables);
|
|
80
|
-
await
|
|
81
|
+
await addImportForMethod(createTestFilePath, "createTest");
|
|
81
82
|
const { pomPrompt, nonSpecFilePrompt } = await (0, context_1.contextForGeneration)(createTestFilePath);
|
|
82
83
|
await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
|
|
83
84
|
trace,
|
|
@@ -106,7 +107,7 @@ async function replaceTodoWithCreateTest(testFilePath, repoDir) {
|
|
|
106
107
|
const [, pageVarName] = todoMatch;
|
|
107
108
|
const pageVariable = pageVarName || "page"; // Default to "page" if not specified
|
|
108
109
|
fs_1.default.writeFileSync(absoluteTestFilePath, fileContent.replace(todoRegex, (_, __, todoText) => `await createTest("${todoText.replace(/"/g, '\\"')}", ${pageVariable});`));
|
|
109
|
-
await
|
|
110
|
+
await addImportForMethod(absoluteTestFilePath, "createTest");
|
|
110
111
|
}
|
|
111
112
|
async function markTestAsOnly({ testCaseName, testCaseSuites, specPath, }) {
|
|
112
113
|
const testFileContent = fs_1.default.readFileSync(specPath, "utf-8");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SupportedChatModels } from "@empiricalrun/shared-types";
|
|
2
|
+
export declare function fetchEnvironmentVariables(): Promise<Record<string, string>>;
|
|
2
3
|
export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }: {
|
|
3
4
|
selectedModel: SupportedChatModels;
|
|
4
5
|
useDiskForChatState: boolean;
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAwCpC,wBAAsB,yBAAyB,IAAI,OAAO,CACxD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACvB,CAwBA;AAED,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"}
|
package/dist/agent/chat/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchEnvironmentVariables = fetchEnvironmentVariables;
|
|
3
4
|
exports.runChatAgentForCLI = runChatAgentForCLI;
|
|
4
5
|
exports.runChatAgentForDashboard = runChatAgentForDashboard;
|
|
5
6
|
const llm_1 = require("@empiricalrun/llm");
|
|
@@ -51,6 +52,7 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
|
|
|
51
52
|
await (0, git_1.checkoutBranch)(branchName, process.cwd());
|
|
52
53
|
let messagesLoadedFromDisk = chatState?.messages || [];
|
|
53
54
|
let chatModel = (0, chat_1.createChatModel)(messagesLoadedFromDisk, selectedModel);
|
|
55
|
+
chatModel.validateEnvVarsForAuth();
|
|
54
56
|
if (initialPromptContent && chatModel.messages.length === 0) {
|
|
55
57
|
chatModel.pushUserMessage(initialPromptContent);
|
|
56
58
|
chatModel.askUserForInput = false;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { FileInfo } from "
|
|
1
|
+
import { FileInfo } from "../../../types";
|
|
2
2
|
export declare function buildSystemPrompt(fileInfo: FileInfo): Promise<string>;
|
|
3
|
-
//# sourceMappingURL=
|
|
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,mBA4GzD"}
|
|
@@ -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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
#
|
|
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
|
|
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
|
|
|
@@ -116,7 +67,9 @@ ONLY when explicitly asked for by the user.
|
|
|
116
67
|
|
|
117
68
|
1. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
|
|
118
69
|
2. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
|
|
119
|
-
3. Trust Playwright's ability to auto-wait while taking actions on elements.
|
|
70
|
+
3. Trust Playwright's ability to auto-wait while taking actions on elements.
|
|
71
|
+
- Example 1: Do not add checks on locator.isVisible() before clicking on it: Playwright already waits for visibility on locator.click()
|
|
72
|
+
- Example 2: Do not add page.waitForLoadState after a page.goto: Playwright already waits for page "load" event in page.goto()
|
|
120
73
|
4. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
|
|
121
74
|
5. Try/catch blocks are a code smell for tests: you should not use them.
|
|
122
75
|
6. Do not use then() or catch() syntax in a test. Use async/await only
|
|
@@ -147,12 +100,12 @@ if (await saveButton.isVisible()) {
|
|
|
147
100
|
}
|
|
148
101
|
\`\`\`
|
|
149
102
|
|
|
150
|
-
|
|
151
|
-
|
|
103
|
+
`;
|
|
104
|
+
const repoContext = await (0, repo_1.getRepoInfoPrompt)(fileInfo);
|
|
105
|
+
return `${preamble}
|
|
152
106
|
|
|
153
|
-
|
|
154
|
-
${
|
|
155
|
-
</email-automation>
|
|
107
|
+
# Recipes
|
|
108
|
+
${pw_utils_docs_1.playwrightUtilsDocs}
|
|
156
109
|
|
|
157
110
|
# Repo context
|
|
158
111
|
${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
|
+
`;
|
|
@@ -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("
|
|
8
|
+
const repo_tree_1 = require("../../../utils/repo-tree");
|
|
9
9
|
async function getAllMarkdownFiles(directory) {
|
|
10
10
|
if (!directory.isDirectory) {
|
|
11
11
|
return [];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
import { SourcesPayload } from "./types";
|
|
3
|
+
export declare class PlaywrightPauseCodegenForRecorder {
|
|
4
|
+
private sourcesCallback;
|
|
5
|
+
private port;
|
|
6
|
+
private page;
|
|
7
|
+
private server;
|
|
8
|
+
private codeForLastAction;
|
|
9
|
+
constructor(sourcesCallback: (code: SourcesPayload[]) => Promise<void>);
|
|
10
|
+
private saveCode;
|
|
11
|
+
initialize(page: Page): Promise<void>;
|
|
12
|
+
startPlaywrightCodegen(page: Page): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=for-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"for-recorder.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/for-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,iCAAiC;IAO1C,OAAO,CAAC,eAAe;IANzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;gBAGpC,eAAe,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;YAKtD,QAAQ;IAWhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;CAiBxC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
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.PlaywrightPauseCodegenForRecorder = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const ipc_1 = require("./ipc");
|
|
9
|
+
class PlaywrightPauseCodegenForRecorder {
|
|
10
|
+
sourcesCallback;
|
|
11
|
+
port = 0;
|
|
12
|
+
page;
|
|
13
|
+
server;
|
|
14
|
+
codeForLastAction;
|
|
15
|
+
constructor(sourcesCallback) {
|
|
16
|
+
this.sourcesCallback = sourcesCallback;
|
|
17
|
+
this.port = ipc_1.PW_PAUSE_IPC_PORT;
|
|
18
|
+
}
|
|
19
|
+
async saveCode(code) {
|
|
20
|
+
const generatedCode = code.map((c) => c.actions.join("\n")).join("\n");
|
|
21
|
+
if (generatedCode) {
|
|
22
|
+
console.log(`[PlaywrightPauseCodegen] Received code from Playwright: ${generatedCode}`);
|
|
23
|
+
this.codeForLastAction = generatedCode;
|
|
24
|
+
await this.sourcesCallback(code);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async initialize(page) {
|
|
28
|
+
// Start server to receive generated code from patch
|
|
29
|
+
const app = (0, express_1.default)();
|
|
30
|
+
app.use(express_1.default.json());
|
|
31
|
+
app.post("/sources", async (req, res) => {
|
|
32
|
+
const { payload } = req.body;
|
|
33
|
+
await this.saveCode(JSON.parse(payload));
|
|
34
|
+
return res.send({ success: true });
|
|
35
|
+
});
|
|
36
|
+
await new Promise((resolve) => {
|
|
37
|
+
this.server = app.listen(this.port, () => resolve());
|
|
38
|
+
});
|
|
39
|
+
console.log(`Server started on port ${this.port}`);
|
|
40
|
+
// Start page.pause experience in the page
|
|
41
|
+
this.page = page;
|
|
42
|
+
await this.startPlaywrightCodegen(page);
|
|
43
|
+
}
|
|
44
|
+
async startPlaywrightCodegen(page) {
|
|
45
|
+
// Similar to the same name method in the main codegen
|
|
46
|
+
const timerPromise = new Promise((resolve) => {
|
|
47
|
+
setTimeout(resolve, 1000);
|
|
48
|
+
});
|
|
49
|
+
const pausePromise = page.pause();
|
|
50
|
+
await timerPromise;
|
|
51
|
+
const evaluatePromise = page.evaluate(() => {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
54
|
+
const glassPane = document.querySelector("x-pw-glass");
|
|
55
|
+
if (glassPane) {
|
|
56
|
+
glassPane.remove();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
await Promise.all([pausePromise, evaluatePromise]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.PlaywrightPauseCodegenForRecorder = PlaywrightPauseCodegenForRecorder;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAKjD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAE9E,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,oBAuCvD;AAED,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;IAUhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;IAyBjC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CAU9C"}
|
|
@@ -54,6 +54,7 @@ class PlaywrightPauseCodegen {
|
|
|
54
54
|
async saveCode(code) {
|
|
55
55
|
const generatedCode = code.map((c) => c.actions.join("\n")).join("\n");
|
|
56
56
|
if (generatedCode) {
|
|
57
|
+
console.log(`[PlaywrightPauseCodegen] Received code from Playwright: ${generatedCode}`);
|
|
57
58
|
this.codeForLastAction = generatedCode;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -78,22 +79,25 @@ class PlaywrightPauseCodegen {
|
|
|
78
79
|
// TODO: Glass pane needs to be removed on every page load
|
|
79
80
|
// We use bindings that Playwright exposes to the page
|
|
80
81
|
// Ref: https://github.com/microsoft/playwright/blob/e1c8e0f6b33923c95cc4b9416aefa6977b1d3c55/packages/playwright-core/src/server/recorder.ts#L191
|
|
81
|
-
|
|
82
|
-
setTimeout(
|
|
83
|
-
// First, we start recording
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
86
|
-
// Then, we will resume the effect of pause()
|
|
87
|
-
// @ts-ignore
|
|
88
|
-
console.log(window["__pw_resume"]());
|
|
89
|
-
// Then, we remove highlights that Playwright shows on the screen
|
|
90
|
-
const glassPane = document.querySelector("x-pw-glass");
|
|
91
|
-
if (glassPane) {
|
|
92
|
-
glassPane.remove();
|
|
93
|
-
}
|
|
94
|
-
}, 1000);
|
|
82
|
+
const timerPromise = new Promise((resolve) => {
|
|
83
|
+
setTimeout(resolve, 1000);
|
|
95
84
|
});
|
|
96
|
-
|
|
85
|
+
const pausePromise = page.pause();
|
|
86
|
+
await timerPromise;
|
|
87
|
+
const evaluatePromise = page.evaluate(() => {
|
|
88
|
+
// First, we start recording
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
91
|
+
// Then, we will resume the effect of pause()
|
|
92
|
+
// @ts-ignore
|
|
93
|
+
console.log(window["__pw_resume"]());
|
|
94
|
+
// Then, we remove highlights that Playwright shows on the screen
|
|
95
|
+
const glassPane = document.querySelector("x-pw-glass");
|
|
96
|
+
if (glassPane) {
|
|
97
|
+
glassPane.remove();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
await Promise.all([pausePromise, evaluatePromise]);
|
|
97
101
|
}
|
|
98
102
|
async recordAction() {
|
|
99
103
|
// Record action is no-op
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export declare function preparePlaywrightForCodegen(repoDir: string): Promise<void>;
|
|
2
|
-
export declare function getPathToRecorderApp(
|
|
2
|
+
export declare function getPathToRecorderApp(pwCorePath: string): Promise<string>;
|
|
3
3
|
export declare function runNpmList(repoDir: string): Promise<string | undefined>;
|
|
4
|
-
export declare function getPlaywrightCoreFromNpmList(output: string):
|
|
5
|
-
|
|
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>;
|
|
6
9
|
export declare function revertToOriginalPwCode(repoDir: string): Promise<void>;
|
|
7
10
|
export declare function createFileBackup(pathToFile: string): Promise<void>;
|
|
8
|
-
export declare function patchPwCode(pathToRecorderApp: string, port: number): Promise<void>;
|
|
11
|
+
export declare function patchPwCode(pwVersion: "1.47" | "1.53", pathToRecorderApp: string, port: number): Promise<void>;
|
|
9
12
|
//# sourceMappingURL=patch.d.ts.map
|
|
@@ -1 +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,
|
|
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"}
|
|
@@ -28,29 +28,30 @@ const ipc_1 = require("./ipc");
|
|
|
28
28
|
const CUSTOM_PATCH_COMMENT = `// Custom patch code set by @empiricalrun/test-gen`;
|
|
29
29
|
async function preparePlaywrightForCodegen(repoDir) {
|
|
30
30
|
try {
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
if (!fs_1.default.existsSync(pathToRecorderApp)) {
|
|
36
|
-
const errMsg = `Cannot patch Playwright: ${pathToRecorderApp} does not exist`;
|
|
37
|
-
throw new Error(errMsg);
|
|
31
|
+
const npmListOutput = await runNpmList(repoDir);
|
|
32
|
+
if (!npmListOutput) {
|
|
33
|
+
return;
|
|
38
34
|
}
|
|
35
|
+
const pwCore = getPlaywrightCoreFromNpmList(npmListOutput);
|
|
36
|
+
const { path: pwCorePath, version: pwVersion } = pwCore;
|
|
37
|
+
const pathToRecorderApp = await getPathToRecorderApp(pwCorePath);
|
|
39
38
|
await createFileBackup(pathToRecorderApp);
|
|
40
|
-
await patchPwCode(pathToRecorderApp, ipc_1.PW_PAUSE_IPC_PORT);
|
|
39
|
+
await patchPwCode(pwVersion, pathToRecorderApp, ipc_1.PW_PAUSE_IPC_PORT);
|
|
41
40
|
}
|
|
42
41
|
catch (error) {
|
|
43
42
|
console.error("Error patching Playwright", error);
|
|
44
43
|
throw error;
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
async function getPathToRecorderApp(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
async function getPathToRecorderApp(pwCorePath) {
|
|
47
|
+
// Both supported versions have the same path structure
|
|
48
|
+
const pathToRecorderApp = path_1.default.join(pwCorePath, "lib", "server", "recorder", "recorderApp.js");
|
|
49
|
+
if (!pathToRecorderApp) {
|
|
50
|
+
throw new Error("Cannot find path to recorder app");
|
|
51
|
+
}
|
|
52
|
+
if (!fs_1.default.existsSync(pathToRecorderApp)) {
|
|
53
|
+
throw new Error(`${pathToRecorderApp} does not exist`);
|
|
51
54
|
}
|
|
52
|
-
const playwrightCorePath = getPlaywrightCoreFromNpmList(npmListOutput);
|
|
53
|
-
const pathToRecorderApp = path_1.default.join(playwrightCorePath, "lib", "server", "recorder", "recorderApp.js");
|
|
54
55
|
return pathToRecorderApp;
|
|
55
56
|
}
|
|
56
57
|
async function runNpmList(repoDir) {
|
|
@@ -78,20 +79,37 @@ function getPlaywrightCoreFromNpmList(output) {
|
|
|
78
79
|
if (!playwrightTest) {
|
|
79
80
|
throw new Error("Could not find @playwright/test in npm dependencies");
|
|
80
81
|
}
|
|
81
|
-
const playwrightTestVersion = playwrightTest.version;
|
|
82
|
-
if (playwrightTestVersion !== "1.47.1") {
|
|
83
|
-
throw new Error("Unsupported playwright-test version: ${playwrightTestVersion}");
|
|
84
|
-
}
|
|
85
82
|
const playwrightDeps = playwrightTest.dependencies["playwright"];
|
|
86
83
|
const playwrightCoreDep = playwrightDeps.dependencies["playwright-core"];
|
|
87
84
|
if (!playwrightCoreDep) {
|
|
88
85
|
throw new Error("Could not find playwright-core in @playwright/test dependencies");
|
|
89
86
|
}
|
|
90
|
-
|
|
87
|
+
function extractMinorVersion(version) {
|
|
88
|
+
const minorVersion = version.split(".").slice(0, 2).join(".");
|
|
89
|
+
if (minorVersion === "1.47") {
|
|
90
|
+
return "1.47";
|
|
91
|
+
}
|
|
92
|
+
else if (minorVersion === "1.53") {
|
|
93
|
+
return "1.53";
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
throw new Error(`Unsupported Playwright version: ${minorVersion}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
path: playwrightCoreDep.path,
|
|
101
|
+
version: extractMinorVersion(playwrightCoreDep.version),
|
|
102
|
+
};
|
|
91
103
|
}
|
|
92
104
|
async function hasPatchedRecorderApp(repoDir) {
|
|
93
105
|
try {
|
|
94
|
-
const
|
|
106
|
+
const npmListOutput = await runNpmList(repoDir);
|
|
107
|
+
if (!npmListOutput) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const pwCore = getPlaywrightCoreFromNpmList(npmListOutput);
|
|
111
|
+
const { path: pwCorePath } = pwCore;
|
|
112
|
+
const pathToRecorderApp = await getPathToRecorderApp(pwCorePath);
|
|
95
113
|
if (!pathToRecorderApp) {
|
|
96
114
|
return false;
|
|
97
115
|
}
|
|
@@ -109,8 +127,8 @@ async function revertToOriginalPwCode(repoDir) {
|
|
|
109
127
|
if (!npmListOutput) {
|
|
110
128
|
return;
|
|
111
129
|
}
|
|
112
|
-
const
|
|
113
|
-
const pathToRecorderApp = path_1.default.join(
|
|
130
|
+
const { path: pwCorePath } = getPlaywrightCoreFromNpmList(npmListOutput);
|
|
131
|
+
const pathToRecorderApp = path_1.default.join(pwCorePath, "lib", "server", "recorder", "recorderApp.js");
|
|
114
132
|
const backupPath = `${pathToRecorderApp}.backup`;
|
|
115
133
|
fs_1.default.copyFileSync(backupPath, pathToRecorderApp);
|
|
116
134
|
fs_1.default.unlinkSync(backupPath);
|
|
@@ -123,9 +141,18 @@ async function createFileBackup(pathToFile) {
|
|
|
123
141
|
const backupPath = `${pathToFile}.backup`;
|
|
124
142
|
fs_1.default.copyFileSync(pathToFile, backupPath);
|
|
125
143
|
}
|
|
126
|
-
async function patchPwCode(pathToRecorderApp, port) {
|
|
144
|
+
async function patchPwCode(pwVersion, pathToRecorderApp, port) {
|
|
127
145
|
const currentFileContents = fs_1.default.readFileSync(pathToRecorderApp, "utf-8");
|
|
128
|
-
|
|
146
|
+
let original;
|
|
147
|
+
if (pwVersion === "1.47") {
|
|
148
|
+
original = `async setSources(sources) {}`;
|
|
149
|
+
}
|
|
150
|
+
else if (pwVersion === "1.53") {
|
|
151
|
+
original = `async setSources(sources, primaryPageURL) {\n }`;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
throw new Error(`Unsupported Playwright version: ${pwVersion}`);
|
|
155
|
+
}
|
|
129
156
|
const replacement = `async setSources(sources) {
|
|
130
157
|
${CUSTOM_PATCH_COMMENT}
|
|
131
158
|
const payload = JSON.stringify(sources.filter(source => source.isRecorded).filter(source => source.id === "javascript"));
|