@empiricalrun/test-gen 0.51.5 → 0.52.0
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 +30 -0
- package/dist/agent/browsing/run.d.ts.map +1 -1
- package/dist/agent/chat/index.d.ts +5 -0
- package/dist/agent/chat/index.d.ts.map +1 -0
- package/dist/agent/chat/index.js +129 -0
- package/dist/agent/chat/prompt.d.ts +2 -0
- package/dist/agent/chat/prompt.d.ts.map +1 -0
- package/dist/agent/chat/prompt.js +74 -0
- package/dist/agent/chat/repo.d.ts +2 -0
- package/dist/agent/chat/repo.d.ts.map +1 -0
- package/dist/agent/chat/repo.js +64 -0
- package/dist/agent/cua/index.d.ts.map +1 -1
- package/dist/agent/cua/index.js +10 -35
- package/dist/agent/cua/model.d.ts +8 -0
- package/dist/agent/cua/model.d.ts.map +1 -0
- package/dist/agent/cua/model.js +35 -0
- package/dist/bin/index.js +3 -2
- package/dist/bin/logger/index.js +3 -3
- package/dist/bin/utils/index.d.ts +1 -1
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.d.ts +5 -0
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +13 -1
- package/dist/reporter/index.d.ts +1 -1
- package/dist/reporter/index.d.ts.map +1 -1
- package/dist/reporter/index.js +8 -8
- package/dist/reporter/lib.d.ts +31 -0
- package/dist/reporter/lib.d.ts.map +1 -0
- package/dist/reporter/lib.js +72 -0
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +7 -0
- package/dist/uploader/index.d.ts.map +1 -1
- package/dist/uploader/index.js +3 -3
- package/dist/uploader/utils.d.ts +8 -0
- package/dist/uploader/utils.d.ts.map +1 -0
- package/dist/uploader/utils.js +35 -0
- package/dist/utils/repo-tree.d.ts.map +1 -1
- package/dist/utils/repo-tree.js +2 -0
- package/package.json +4 -3
- package/dist/agent/chat.d.ts +0 -5
- package/dist/agent/chat.d.ts.map +0 -1
- package/dist/agent/chat.js +0 -188
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.52.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a399a57: feat: added Gemini support to chat agent
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 1b8d273: fix: clean up backup files
|
|
12
|
+
- Updated dependencies [a399a57]
|
|
13
|
+
- Updated dependencies [1b8d273]
|
|
14
|
+
- Updated dependencies [99b0826]
|
|
15
|
+
- @empiricalrun/llm@0.12.0
|
|
16
|
+
|
|
17
|
+
## 0.51.6
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- dc17737: fix: repo context dir not found
|
|
22
|
+
- b058de5: feat: add app knowledge to chat agent system prompt
|
|
23
|
+
- dbe2ace: fix: remove Anthropic type from chat agent
|
|
24
|
+
- ce7fece: fix: improvements to cua usage in test-gen-browser tool call
|
|
25
|
+
- b8b4eff: fix: crash when last-chat is not found
|
|
26
|
+
- af84555: chore: remove reporter dependency from test-gen package
|
|
27
|
+
- 2766be8: feat: introduce chat model interface to support multiple llms
|
|
28
|
+
- Updated dependencies [dbe2ace]
|
|
29
|
+
- Updated dependencies [b8b4eff]
|
|
30
|
+
- Updated dependencies [2766be8]
|
|
31
|
+
- @empiricalrun/llm@0.11.5
|
|
32
|
+
|
|
3
33
|
## 0.51.5
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,OAAO,GACR,EAAE,iBAAiB
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,OAAO,GACR,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAgFD"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function chatAgent({ selectedModel, useDiskForChatState, }: {
|
|
2
|
+
selectedModel?: "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-exp-03-25";
|
|
3
|
+
useDiskForChatState?: boolean;
|
|
4
|
+
}): Promise<string>;
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AA4DA,wBAAsB,SAAS,CAAC,EAC9B,aAA4C,EAC5C,mBAA2B,GAC5B,EAAE;IACD,aAAa,CAAC,EACV,4BAA4B,GAC5B,4BAA4B,GAC5B,0BAA0B,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,mBA2FA"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chatAgent = void 0;
|
|
4
|
+
const chat_1 = require("@empiricalrun/llm/chat");
|
|
5
|
+
const picocolors_1 = require("picocolors");
|
|
6
|
+
const web_1 = require("../../bin/utils/platform/web");
|
|
7
|
+
const human_in_the_loop_1 = require("../../human-in-the-loop");
|
|
8
|
+
const diagnosis_fetcher_1 = require("../../tools/diagnosis-fetcher");
|
|
9
|
+
const grep_1 = require("../../tools/grep");
|
|
10
|
+
const test_gen_browser_1 = require("../../tools/test-gen-browser");
|
|
11
|
+
const test_run_1 = require("../../tools/test-run");
|
|
12
|
+
const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
|
|
13
|
+
const zod_schema_1 = require("../../tools/zod-schema");
|
|
14
|
+
const prompt_1 = require("./prompt");
|
|
15
|
+
const tools = [
|
|
16
|
+
test_run_1.runTestTool,
|
|
17
|
+
test_gen_browser_1.generateTestWithBrowserAgent,
|
|
18
|
+
diagnosis_fetcher_1.diagnosisTool,
|
|
19
|
+
grep_1.grepTool,
|
|
20
|
+
test_run_fetcher_1.testRunTool,
|
|
21
|
+
];
|
|
22
|
+
const toolExecutors = {
|
|
23
|
+
...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
|
|
24
|
+
str_replace_editor: (input) => (0, chat_1.strReplaceEditorTool)(input, web_1.validateTypescript),
|
|
25
|
+
};
|
|
26
|
+
function createChatModel(useDiskForChatState, selectedModel) {
|
|
27
|
+
if (selectedModel.startsWith("claude")) {
|
|
28
|
+
return new chat_1.ClaudeChatModel(useDiskForChatState);
|
|
29
|
+
}
|
|
30
|
+
if (selectedModel.startsWith("gemini")) {
|
|
31
|
+
return new chat_1.GeminiChatModel();
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Unsupported model: ${selectedModel}`);
|
|
34
|
+
}
|
|
35
|
+
function getModelName(model) {
|
|
36
|
+
if (model.startsWith("claude"))
|
|
37
|
+
return "Claude";
|
|
38
|
+
if (model.startsWith("gemini"))
|
|
39
|
+
return "Gemini";
|
|
40
|
+
return "AI";
|
|
41
|
+
}
|
|
42
|
+
function concludeAgent(usageSummary) {
|
|
43
|
+
console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + usageSummary)}`);
|
|
44
|
+
(0, chat_1.cleanupBackupFiles)(process.cwd());
|
|
45
|
+
}
|
|
46
|
+
async function chatAgent({ selectedModel = "claude-3-7-sonnet-20250219", useDiskForChatState = false, }) {
|
|
47
|
+
let chatModel = createChatModel(useDiskForChatState, selectedModel);
|
|
48
|
+
let userPrompt = undefined;
|
|
49
|
+
const handleSigInt = () => {
|
|
50
|
+
concludeAgent(chatModel.getUsageSummary());
|
|
51
|
+
process.exit(0);
|
|
52
|
+
};
|
|
53
|
+
process.once("SIGINT", handleSigInt);
|
|
54
|
+
process.once("SIGTERM", handleSigInt);
|
|
55
|
+
const ora = (await import("ora")).default;
|
|
56
|
+
if (chatModel.askUserForInput) {
|
|
57
|
+
// Show last message to the user for context when we loaded from disk
|
|
58
|
+
const latest = chatModel.getHumanReadableLatestMessage();
|
|
59
|
+
if (latest) {
|
|
60
|
+
console.log(`${latest.role}: ${latest.textMessage}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const systemPrompt = await (0, prompt_1.buildSystemPrompt)();
|
|
64
|
+
while (!userPrompt?.toLowerCase().includes("stop")) {
|
|
65
|
+
if (chatModel.askUserForInput) {
|
|
66
|
+
try {
|
|
67
|
+
userPrompt = await human_in_the_loop_1.humanLoop.getFeedback({
|
|
68
|
+
message: "User:",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
// https://github.com/SBoudrias/Inquirer.js/issues/1502#issuecomment-2275991680
|
|
73
|
+
if (e instanceof Error && e.name === "ExitPromptError") {
|
|
74
|
+
concludeAgent(chatModel.getUsageSummary());
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
throw e;
|
|
78
|
+
}
|
|
79
|
+
chatModel.pushUserMessage(userPrompt);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const toolUse = chatModel.getPendingToolCall();
|
|
83
|
+
if (toolUse) {
|
|
84
|
+
const spinner = ora(`Executing tool ${toolUse.name} with args: ${JSON.stringify(toolUse.input)}`).start();
|
|
85
|
+
const toolExecutor = toolExecutors[toolUse.name];
|
|
86
|
+
if (!toolExecutor) {
|
|
87
|
+
throw new Error(`Tool ${toolUse.name} not found`);
|
|
88
|
+
}
|
|
89
|
+
const toolResult = await toolExecutor(toolUse.input);
|
|
90
|
+
if (toolResult.isError) {
|
|
91
|
+
spinner.fail(`Tool ${toolUse.name} failed with error: ${toolResult.result}`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
spinner.succeed(`Tool ${toolUse.name} completed`);
|
|
95
|
+
}
|
|
96
|
+
chatModel.pushMessage({
|
|
97
|
+
role: "user",
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: "tool_result",
|
|
101
|
+
tool_use_id: toolUse.id,
|
|
102
|
+
content: toolResult.result,
|
|
103
|
+
is_error: toolResult.isError,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const spinner = ora(`${getModelName(selectedModel)} is working...`).start();
|
|
110
|
+
const response = await chatModel.getLLMResponse({
|
|
111
|
+
systemPrompt,
|
|
112
|
+
tools: tools.map((tool) => (0, zod_schema_1.zodToOpenAITool)(tool.schema)),
|
|
113
|
+
selectedModel,
|
|
114
|
+
});
|
|
115
|
+
spinner.stop();
|
|
116
|
+
if (!response) {
|
|
117
|
+
throw new Error("No response from LLM");
|
|
118
|
+
}
|
|
119
|
+
chatModel.pushMessage(response);
|
|
120
|
+
const latest = chatModel.getHumanReadableLatestMessage();
|
|
121
|
+
if (latest) {
|
|
122
|
+
console.log(`${latest.role}: ${latest.textMessage}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const usageSummary = chatModel.getUsageSummary();
|
|
126
|
+
concludeAgent(usageSummary);
|
|
127
|
+
return usageSummary;
|
|
128
|
+
}
|
|
129
|
+
exports.chatAgent = chatAgent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAEA,wBAAsB,iBAAiB,oBAoEtC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSystemPrompt = void 0;
|
|
4
|
+
const repo_1 = require("./repo");
|
|
5
|
+
async function buildSystemPrompt() {
|
|
6
|
+
const repoContext = await (0, repo_1.getRepoContext)();
|
|
7
|
+
return `
|
|
8
|
+
You are a helpful assistant that can answer questions and help with tasks related to writing
|
|
9
|
+
and maintaining Playwright tests.
|
|
10
|
+
|
|
11
|
+
# Supported capabilities
|
|
12
|
+
|
|
13
|
+
- Adding new Playwright tests or helper methods
|
|
14
|
+
- Going through test reports and identifying app issues versus test issues
|
|
15
|
+
- Modifying existing tests
|
|
16
|
+
- Modifying repo configuration (e.g. in playwright.config.ts and other)
|
|
17
|
+
|
|
18
|
+
# Going through test reports
|
|
19
|
+
|
|
20
|
+
- App issues: app issues caught by test failures, like UI issues, API endpoint issues, etc. These are issues that
|
|
21
|
+
will be reported to an app developer to investigate and fix.
|
|
22
|
+
- Test issues: Playwright tests can become outdated when app code changes. These are issues that need to be
|
|
23
|
+
fixed with modifications to the test code, and it is your job to do that.
|
|
24
|
+
|
|
25
|
+
# Tools
|
|
26
|
+
|
|
27
|
+
You are given a set of tools to help you fulfill the user's request. Read their descriptions to
|
|
28
|
+
understand what each tool does.
|
|
29
|
+
|
|
30
|
+
For example, if the user asks you to run a test, you could use the runTest tool.
|
|
31
|
+
Once the test is run, you will receive the results in the form of a JSON object.
|
|
32
|
+
Summarize the results in a few sentences.
|
|
33
|
+
|
|
34
|
+
If the user provides a diagnosis URL, you can use the fetchDiagnosisDetails tool
|
|
35
|
+
to get more information about the test case and its results.
|
|
36
|
+
|
|
37
|
+
If the user provides a test run URL, you can use the fetchTestRunDetails tool
|
|
38
|
+
to get detailed information about a specific test run.
|
|
39
|
+
|
|
40
|
+
Or if the user asks you to modify a test, you could use the generateTestWithBrowserAgent tool. If you suspect
|
|
41
|
+
that a UI selector needs to be updated, using the browser agent is a good idea.
|
|
42
|
+
|
|
43
|
+
Before using generateTestWithBrowserAgent, you need to prepare the test code for the browser agent.
|
|
44
|
+
You can do this by using the str_replace_editor tool to add a TODO comment to the test code. This
|
|
45
|
+
comment should explain to the browser agent what to do.
|
|
46
|
+
|
|
47
|
+
For example, if the expected modification is to click on a login button, you could add the following comment.
|
|
48
|
+
|
|
49
|
+
// TODO(agent): Click on the login button
|
|
50
|
+
|
|
51
|
+
The position of the comment is important: the browser agent will look for this comment and replace it with
|
|
52
|
+
the actual code to click on the login button. If you are fixing a failing test, your comment should be
|
|
53
|
+
around the failing line of code, so that it can be replaced/modified.
|
|
54
|
+
|
|
55
|
+
# Rules for fixing Playwright tests
|
|
56
|
+
|
|
57
|
+
You must follow these rules while adding new tests or modifying existing tests. There can be exceptions to these rules, but
|
|
58
|
+
ONLY when explicitly asked for by the user.
|
|
59
|
+
|
|
60
|
+
1. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
|
|
61
|
+
2. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
|
|
62
|
+
3. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
|
|
63
|
+
4. 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)
|
|
64
|
+
|
|
65
|
+
# Proactiveness
|
|
66
|
+
|
|
67
|
+
You are allowed to be proactive, but ONLY for read-only actions, like searching for content, reading files, fetching data from tools, and
|
|
68
|
+
running Playwright tests. For any read-write actions (e.g. modifying any file), you should share your plan and get the user's approval before proceeding.
|
|
69
|
+
|
|
70
|
+
# Repo context
|
|
71
|
+
${repoContext}
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
74
|
+
exports.buildSystemPrompt = buildSystemPrompt;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/repo.ts"],"names":[],"mappings":"AAmCA,wBAAsB,cAAc,oBAwBnC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
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.getRepoContext = void 0;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const repo_tree_1 = require("../../utils/repo-tree");
|
|
10
|
+
async function getAllMarkdownFiles() {
|
|
11
|
+
const dir = path_1.default.join(process.cwd(), ".empiricalrun");
|
|
12
|
+
if (!fs_extra_1.default.existsSync(dir)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const files = await fs_extra_1.default.readdir(dir);
|
|
16
|
+
return files
|
|
17
|
+
.filter((file) => file.endsWith(".md"))
|
|
18
|
+
.map((file) => {
|
|
19
|
+
return {
|
|
20
|
+
name: file,
|
|
21
|
+
content: fs_extra_1.default.readFileSync(path_1.default.join(dir, file), "utf8"),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function knowledgeContext() {
|
|
26
|
+
const mdFiles = await getAllMarkdownFiles();
|
|
27
|
+
const knowledge = mdFiles.map((file) => {
|
|
28
|
+
return `
|
|
29
|
+
<knowledge_file>
|
|
30
|
+
<file_name>${file.name}</file_name>
|
|
31
|
+
<file_content>
|
|
32
|
+
${file.content}
|
|
33
|
+
</file_content>
|
|
34
|
+
</knowledge_file>
|
|
35
|
+
`;
|
|
36
|
+
});
|
|
37
|
+
return knowledge.join("\n");
|
|
38
|
+
}
|
|
39
|
+
async function getRepoContext() {
|
|
40
|
+
let REPO_CONTEXT_PROMPT = `
|
|
41
|
+
You are running as a CLI tool inside the directory of the repo that has Playwright tests.
|
|
42
|
+
|
|
43
|
+
Here is the repo directory structure:
|
|
44
|
+
|
|
45
|
+
${(0, repo_tree_1.generateAsciiTree)(process.cwd())}
|
|
46
|
+
|
|
47
|
+
While specifying paths to files, use relative paths from the current working directory. For example:
|
|
48
|
+
- Correct path: "tests/lesson.spec.ts"
|
|
49
|
+
- Incorrect path: "/repo/tests/lesson.spec.ts" or "${path_1.default.basename(process.cwd())}/tests/lesson.spec.ts"
|
|
50
|
+
`;
|
|
51
|
+
const knowledge = await knowledgeContext();
|
|
52
|
+
if (knowledge.length > 0) {
|
|
53
|
+
REPO_CONTEXT_PROMPT += `
|
|
54
|
+
## Repo-specific knowledge
|
|
55
|
+
|
|
56
|
+
You can use the following knowledge with your tasks.
|
|
57
|
+
<knowledge>
|
|
58
|
+
${knowledge}
|
|
59
|
+
</knowledge>
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
return REPO_CONTEXT_PROMPT;
|
|
63
|
+
}
|
|
64
|
+
exports.getRepoContext = getRepoContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMlC,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,iBAoBtD;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,GACL,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC,CAkFD"}
|
package/dist/agent/cua/index.js
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.createTestUsingComputerUseAgent = exports.startPlaywrightCodegen = void 0;
|
|
7
|
-
const openai_1 = __importDefault(require("openai"));
|
|
8
4
|
const utils_1 = require("../browsing/utils");
|
|
9
5
|
const computer_1 = require("./computer");
|
|
10
|
-
const
|
|
11
|
-
Don't ask the user for confirmations - just execute the actions.
|
|
12
|
-
|
|
13
|
-
For example, if the user message says "Click on Submit button", then
|
|
14
|
-
you click on the submit button -- even if it looks like a scary action.`;
|
|
6
|
+
const model_1 = require("./model");
|
|
15
7
|
async function startPlaywrightCodegen(page) {
|
|
16
8
|
// TODO: Use this method to offload code generation to Playwright
|
|
17
9
|
// Unclear how to retrieve source code that is generated
|
|
@@ -44,18 +36,7 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
44
36
|
const viewport = page.viewportSize();
|
|
45
37
|
let screenWidth = viewport?.width || 1280;
|
|
46
38
|
let screenHeight = viewport?.height || 720;
|
|
47
|
-
|
|
48
|
-
let response = await openai.responses.create({
|
|
49
|
-
model: "computer-use-preview",
|
|
50
|
-
tools: [
|
|
51
|
-
{
|
|
52
|
-
type: "computer-preview",
|
|
53
|
-
display_width: screenWidth,
|
|
54
|
-
display_height: screenHeight,
|
|
55
|
-
environment: "browser",
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
instructions: INSTRUCTIONS,
|
|
39
|
+
let response = await (0, model_1.callComputerUseModel)({
|
|
59
40
|
input: [
|
|
60
41
|
{
|
|
61
42
|
role: "user",
|
|
@@ -72,7 +53,8 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
72
53
|
],
|
|
73
54
|
},
|
|
74
55
|
],
|
|
75
|
-
|
|
56
|
+
screenWidth,
|
|
57
|
+
screenHeight,
|
|
76
58
|
});
|
|
77
59
|
// eslint-disable-next-line no-constant-condition
|
|
78
60
|
while (true) {
|
|
@@ -92,6 +74,7 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
92
74
|
const computerCall = computerCalls[0];
|
|
93
75
|
const lastCallId = computerCall.call_id;
|
|
94
76
|
const action = computerCall.action;
|
|
77
|
+
const pendingSafetyChecks = computerCall.pending_safety_checks;
|
|
95
78
|
// Execute the action (function defined in step 3)
|
|
96
79
|
const actionCode = await (0, computer_1.handleModelAction)(page, action);
|
|
97
80
|
generatedCode += actionCode;
|
|
@@ -99,18 +82,8 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
99
82
|
// Take a screenshot after the action (function defined in step 4)
|
|
100
83
|
const screenshotBytes = await (0, computer_1.getScreenshot)(page);
|
|
101
84
|
// Send the screenshot back as a computer_call_output
|
|
102
|
-
response = await
|
|
103
|
-
|
|
104
|
-
previous_response_id: response.id,
|
|
105
|
-
tools: [
|
|
106
|
-
{
|
|
107
|
-
type: "computer-preview",
|
|
108
|
-
display_width: screenWidth,
|
|
109
|
-
display_height: screenHeight,
|
|
110
|
-
environment: "browser",
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
instructions: INSTRUCTIONS,
|
|
85
|
+
response = await (0, model_1.callComputerUseModel)({
|
|
86
|
+
previousResponseId: response.id,
|
|
114
87
|
input: [
|
|
115
88
|
{
|
|
116
89
|
call_id: lastCallId,
|
|
@@ -119,9 +92,11 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
119
92
|
type: "computer_screenshot",
|
|
120
93
|
image_url: `data:image/png;base64,${screenshotBytes}`,
|
|
121
94
|
},
|
|
95
|
+
acknowledged_safety_checks: pendingSafetyChecks,
|
|
122
96
|
},
|
|
123
97
|
],
|
|
124
|
-
|
|
98
|
+
screenWidth,
|
|
99
|
+
screenHeight,
|
|
125
100
|
});
|
|
126
101
|
}
|
|
127
102
|
return {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Response, ResponseInput } from "openai/resources/responses/responses.mjs";
|
|
2
|
+
export declare function callComputerUseModel({ input, previousResponseId, screenWidth, screenHeight, }: {
|
|
3
|
+
input: ResponseInput;
|
|
4
|
+
previousResponseId?: string;
|
|
5
|
+
screenWidth: number;
|
|
6
|
+
screenHeight: number;
|
|
7
|
+
}): Promise<Response>;
|
|
8
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,aAAa,EACd,MAAM,0CAA0C,CAAC;AAQlD,wBAAsB,oBAAoB,CAAC,EACzC,KAAK,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,GACb,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBpB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
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.callComputerUseModel = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
const INSTRUCTIONS = `You will be asked to execute some actions in a browser context.
|
|
9
|
+
Don't ask the user for confirmations - just execute the actions.
|
|
10
|
+
|
|
11
|
+
For example, if the user message says "Click on Submit button", then
|
|
12
|
+
you click on the submit button -- even if it looks like a scary action.`;
|
|
13
|
+
async function callComputerUseModel({ input, previousResponseId, screenWidth, screenHeight, }) {
|
|
14
|
+
const openai = new openai_1.default();
|
|
15
|
+
return await openai.responses.create({
|
|
16
|
+
model: "computer-use-preview",
|
|
17
|
+
previous_response_id: previousResponseId,
|
|
18
|
+
tools: [
|
|
19
|
+
{
|
|
20
|
+
type: "computer-preview",
|
|
21
|
+
display_width: screenWidth,
|
|
22
|
+
display_height: screenHeight,
|
|
23
|
+
environment: "browser",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
reasoning: {
|
|
27
|
+
effort: "medium",
|
|
28
|
+
generate_summary: "concise",
|
|
29
|
+
},
|
|
30
|
+
instructions: INSTRUCTIONS,
|
|
31
|
+
input,
|
|
32
|
+
truncation: "auto",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
exports.callComputerUseModel = callComputerUseModel;
|
package/dist/bin/index.js
CHANGED
|
@@ -41,12 +41,13 @@ async function runChatAgent(modelInput, useDiskForChatState) {
|
|
|
41
41
|
"3-7": "claude-3-7-sonnet-20250219",
|
|
42
42
|
"claude-3-5": "claude-3-5-sonnet-20241022",
|
|
43
43
|
"3-5": "claude-3-5-sonnet-20241022",
|
|
44
|
+
"gemini-2.5-pro-exp-03-25": "gemini-2.5-pro-exp-03-25",
|
|
44
45
|
};
|
|
45
46
|
if (modelInput && !MODEL_MAPPING[modelInput]) {
|
|
46
47
|
throw new Error(`Invalid chat model: ${modelInput}`);
|
|
47
48
|
}
|
|
48
49
|
return await (0, chat_1.chatAgent)({
|
|
49
|
-
|
|
50
|
+
selectedModel: modelInput ? MODEL_MAPPING[modelInput] : undefined,
|
|
50
51
|
useDiskForChatState,
|
|
51
52
|
});
|
|
52
53
|
}
|
|
@@ -186,7 +187,7 @@ async function runAgentsWorkflow(testGenConfig, testGenToken) {
|
|
|
186
187
|
.option("--suites <suites>", "Comma separated list of describe blocks")
|
|
187
188
|
.option("--use-chat", "Use chat agent (and not the workflow)")
|
|
188
189
|
.option("--use-disk-for-chat-state", "Save and load chat state from disk")
|
|
189
|
-
.option("--chat-model <model>", "Chat model to use (claude-3-7-sonnet-20250219 or claude-3-5-sonnet-20241022)")
|
|
190
|
+
.option("--chat-model <model>", "Chat model to use (claude-3-7-sonnet-20250219 or claude-3-5-sonnet-20241022 or gemini-2.5-pro-exp-03-25)")
|
|
190
191
|
.parse(process.argv);
|
|
191
192
|
const options = program.opts();
|
|
192
193
|
const completedOptions = await (0, utils_2.validateAndCompleteCliOptions)(options);
|
package/dist/bin/logger/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.waitForLogsToFlush = exports.CustomLogger = void 0;
|
|
4
|
-
const reporter_1 = require("@empiricalrun/reporter");
|
|
5
4
|
const picocolors_1 = require("picocolors");
|
|
6
|
-
const
|
|
5
|
+
const reporter_1 = require("../../reporter");
|
|
6
|
+
const lib_1 = require("../../reporter/lib");
|
|
7
7
|
let queuedReporterMessages = [];
|
|
8
8
|
class CustomLogger {
|
|
9
9
|
useReporter = false;
|
|
@@ -13,7 +13,7 @@ class CustomLogger {
|
|
|
13
13
|
logToReporter(message) {
|
|
14
14
|
if (this.useReporter) {
|
|
15
15
|
(() => {
|
|
16
|
-
const promise = (0,
|
|
16
|
+
const promise = (0, reporter_1.getReporter)()?.report(new lib_1.MessageBuilder({ type: "message", message: message }));
|
|
17
17
|
if (promise) {
|
|
18
18
|
queuedReporterMessages.push(promise);
|
|
19
19
|
}
|
|
@@ -6,7 +6,7 @@ export interface CliOptions {
|
|
|
6
6
|
suites?: string;
|
|
7
7
|
useChat?: boolean;
|
|
8
8
|
useDiskForChatState?: boolean;
|
|
9
|
-
chatModel?: "claude-3-7" | "3-7" | "claude-3-5" | "3-5" | "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022";
|
|
9
|
+
chatModel?: "claude-3-7" | "3-7" | "claude-3-5" | "3-5" | "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-exp-03-25";
|
|
10
10
|
}
|
|
11
11
|
export declare function validateAndCompleteCliOptions(options: CliOptions): Promise<CliOptions>;
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EACN,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,KAAK,GACL,4BAA4B,GAC5B,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EACN,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,KAAK,GACL,4BAA4B,GAC5B,4BAA4B,GAC5B,0BAA0B,CAAC;CAChC;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
|
|
@@ -18,6 +18,11 @@ export declare function getTypescriptTestBlock({ scenarioName, suites, content,
|
|
|
18
18
|
testNode: Node | undefined;
|
|
19
19
|
testAlias: string;
|
|
20
20
|
};
|
|
21
|
+
export declare function hasTestBlock({ testName, testSuites, filePath, }: {
|
|
22
|
+
testName: string;
|
|
23
|
+
testSuites: string[];
|
|
24
|
+
filePath: string;
|
|
25
|
+
}): boolean;
|
|
21
26
|
export declare function hasTopLevelDescribeConfigureWithSerialMode(filePath: string): Promise<boolean>;
|
|
22
27
|
/**
|
|
23
28
|
* Function to find the first 'describe' block configured with 'serial: true'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAK3D,OAAO,EAGL,IAAI,EAEJ,UAAU,EAEX,MAAM,UAAU,CAAC;AAIlB,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,MAAM,EACN,OAAO,GACR,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG;IACF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,CA2CA;AAwBD,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,mCAWjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAShD;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,iBAgBrE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,UAE5E;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,iBAMpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,UAcpD;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,+BAoBvE;AA+CD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UAoCtB;AAED,eAAO,MAAM,6BAA6B;qBAKvB,MAAM;iBACV,MAAM;YACX,MAAM,EAAE;YA2DjB,CAAC;AAEF,eAAO,MAAM,iCAAiC,YACnC,MAAM,aACJ,MAAM,EAAE,gBACL,MAAM,sBAyBrB,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,EAC1C,YAAY,EACZ,QAAQ,EACR,MAAM,GACP,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,iBAgDA;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EAAE,iBAsBzB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;CACpB,WAYA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,UAOA;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B5E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAK3D,OAAO,EAGL,IAAI,EAEJ,UAAU,EAEX,MAAM,UAAU,CAAC;AAIlB,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,MAAM,EACN,OAAO,GACR,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG;IACF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,CA2CA;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,UAAU,EACV,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,WAUA;AAwBD,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,mCAWjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAShD;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,iBAgBrE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,UAE5E;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,iBAMpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,UAcpD;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,+BAoBvE;AA+CD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UAoCtB;AAED,eAAO,MAAM,6BAA6B;qBAKvB,MAAM;iBACV,MAAM;YACX,MAAM,EAAE;YA2DjB,CAAC;AAEF,eAAO,MAAM,iCAAiC,YACnC,MAAM,aACJ,MAAM,EAAE,gBACL,MAAM,sBAyBrB,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,EAC1C,YAAY,EACZ,QAAQ,EACR,MAAM,GACP,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,iBAgDA;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EAAE,iBAsBzB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;CACpB,WAYA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,UAOA;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B5E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD"}
|
|
@@ -3,7 +3,7 @@ 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.isSyntaxValid = exports.getVariableDeclarationsFromCode = exports.buildTestNamePrompt = exports.isTestPresent = exports.appendScopeToCreateTest = exports.addUserContextFixture = exports.importAllExportsStmtFromFilePaths = exports.injectCodeSnippetBySuiteChain = exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.getTypescriptTestBlock = exports.getTestModuleAliasFromSourceFile = void 0;
|
|
6
|
+
exports.isSyntaxValid = exports.getVariableDeclarationsFromCode = exports.buildTestNamePrompt = exports.isTestPresent = exports.appendScopeToCreateTest = exports.addUserContextFixture = exports.importAllExportsStmtFromFilePaths = exports.injectCodeSnippetBySuiteChain = exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.hasTestBlock = exports.getTypescriptTestBlock = exports.getTestModuleAliasFromSourceFile = void 0;
|
|
7
7
|
const parser_1 = require("@babel/parser");
|
|
8
8
|
const eslint_1 = require("eslint");
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
@@ -72,6 +72,18 @@ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
exports.getTypescriptTestBlock = getTypescriptTestBlock;
|
|
75
|
+
function hasTestBlock({ testName, testSuites, filePath, }) {
|
|
76
|
+
if (!fs_extra_1.default.existsSync(filePath)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const { testBlock } = getTypescriptTestBlock({
|
|
80
|
+
scenarioName: testName,
|
|
81
|
+
content: fs_extra_1.default.readFileSync(filePath, "utf-8"),
|
|
82
|
+
suites: testSuites,
|
|
83
|
+
});
|
|
84
|
+
return Boolean(testBlock);
|
|
85
|
+
}
|
|
86
|
+
exports.hasTestBlock = hasTestBlock;
|
|
75
87
|
// get the names of parent describe blocks
|
|
76
88
|
function getParentDescribeNames(node) {
|
|
77
89
|
const names = [];
|
package/dist/reporter/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjD,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAKF,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAUlD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAAS;;IAKlB,eAAe,CAAC,EACpB,eAAe,EACf,QAAQ,GACT,EAAE;QACD,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB;IAgDK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWxD"}
|
package/dist/reporter/index.js
CHANGED
|
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.TestGenUpdatesReporter = exports.setReporterConfig = exports.getReporter = void 0;
|
|
7
7
|
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
8
|
-
const reporter_1 = require("@empiricalrun/reporter");
|
|
9
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
9
|
const path_1 = __importDefault(require("path"));
|
|
11
10
|
const logger_1 = require("../bin/logger");
|
|
12
11
|
const uploader_1 = require("../uploader");
|
|
12
|
+
const lib_1 = require("./lib");
|
|
13
13
|
let reporterInstance = undefined;
|
|
14
14
|
let reporterConfig = undefined;
|
|
15
15
|
function getReporter() {
|
|
@@ -19,7 +19,7 @@ function getReporter() {
|
|
|
19
19
|
}
|
|
20
20
|
// initialise once config is set
|
|
21
21
|
if (!reporterInstance && reporterConfig) {
|
|
22
|
-
reporterInstance = new
|
|
22
|
+
reporterInstance = new lib_1.Reporter(reporterConfig);
|
|
23
23
|
}
|
|
24
24
|
return reporterInstance;
|
|
25
25
|
}
|
|
@@ -55,7 +55,7 @@ class TestGenUpdatesReporter {
|
|
|
55
55
|
await Promise.allSettled([
|
|
56
56
|
...(videoUrls.length
|
|
57
57
|
? [
|
|
58
|
-
reporter?.report(new
|
|
58
|
+
reporter?.report(new lib_1.MessageBuilder({
|
|
59
59
|
type: "video",
|
|
60
60
|
message: JSON.stringify({
|
|
61
61
|
type: "video",
|
|
@@ -66,7 +66,7 @@ class TestGenUpdatesReporter {
|
|
|
66
66
|
: []),
|
|
67
67
|
...(traceFiles.length
|
|
68
68
|
? [
|
|
69
|
-
reporter?.report(new
|
|
69
|
+
reporter?.report(new lib_1.MessageBuilder({
|
|
70
70
|
type: "pw-trace",
|
|
71
71
|
message: JSON.stringify({
|
|
72
72
|
type: "trace",
|
|
@@ -107,7 +107,7 @@ class TestGenUpdatesReporter {
|
|
|
107
107
|
const filePath = Object.keys(files)[0];
|
|
108
108
|
const relativeFilePath = filePath.replace(path_1.default.join(this.repoDir, "gen-assets"), "");
|
|
109
109
|
const url = `${uploader_1.UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
|
|
110
|
-
await getReporter()?.report(new
|
|
110
|
+
await getReporter()?.report(new lib_1.MessageBuilder({
|
|
111
111
|
type: "current-snapshot",
|
|
112
112
|
message: JSON.stringify({ type: "current-view", url }),
|
|
113
113
|
}));
|
|
@@ -122,7 +122,7 @@ class TestGenUpdatesReporter {
|
|
|
122
122
|
async sendMessage(message) {
|
|
123
123
|
const reporter = getReporter();
|
|
124
124
|
if (reporter) {
|
|
125
|
-
await reporter.report(new
|
|
125
|
+
await reporter.report(new lib_1.MessageBuilder({
|
|
126
126
|
type: "message",
|
|
127
127
|
message,
|
|
128
128
|
}));
|
|
@@ -131,7 +131,7 @@ class TestGenUpdatesReporter {
|
|
|
131
131
|
async sendLogUrl(message) {
|
|
132
132
|
const reporter = getReporter();
|
|
133
133
|
if (reporter) {
|
|
134
|
-
await reporter.report(new
|
|
134
|
+
await reporter.report(new lib_1.MessageBuilder({
|
|
135
135
|
type: "log-url",
|
|
136
136
|
message,
|
|
137
137
|
}));
|
|
@@ -140,7 +140,7 @@ class TestGenUpdatesReporter {
|
|
|
140
140
|
async sendAgentTraceUrl(message) {
|
|
141
141
|
const reporter = getReporter();
|
|
142
142
|
if (reporter) {
|
|
143
|
-
await reporter.report(new
|
|
143
|
+
await reporter.report(new lib_1.MessageBuilder({
|
|
144
144
|
type: "agent-trace",
|
|
145
145
|
message,
|
|
146
146
|
}));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type DashboardMessagePayload = {
|
|
2
|
+
message: string;
|
|
3
|
+
messageType: "message" | "agent-trace" | "current-snapshot" | "video" | "pw-trace" | "log-url" | "commit-id";
|
|
4
|
+
pull_request?: string;
|
|
5
|
+
testCaseName?: string;
|
|
6
|
+
testCaseId?: number;
|
|
7
|
+
projectRepoName?: string;
|
|
8
|
+
testSessionId?: number;
|
|
9
|
+
generationId?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare class MessageBuilder {
|
|
12
|
+
private props;
|
|
13
|
+
constructor(props: {
|
|
14
|
+
message: string;
|
|
15
|
+
type: DashboardMessagePayload["messageType"];
|
|
16
|
+
});
|
|
17
|
+
buildMessage(dashboardOptions?: {
|
|
18
|
+
override?: Partial<DashboardMessagePayload>;
|
|
19
|
+
}): DashboardMessagePayload;
|
|
20
|
+
}
|
|
21
|
+
export declare class Reporter {
|
|
22
|
+
private config;
|
|
23
|
+
constructor(config: {
|
|
24
|
+
testSessionId: number;
|
|
25
|
+
generationId: number;
|
|
26
|
+
});
|
|
27
|
+
report(messageBuilder: MessageBuilder): Promise<void>;
|
|
28
|
+
private sendMessageToDashboard;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/reporter/lib.ts"],"names":[],"mappings":"AAMA,KAAK,uBAAuB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EACP,SAAS,GACT,aAAa,GACb,kBAAkB,GAClB,OAAO,GACP,UAAU,GACV,SAAS,GACT,WAAW,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,cAAc;IAEvB,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;KAC9C;IAGH,YAAY,CAAC,gBAAgB,CAAC,EAAE;QAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;KAC7C,GAAG,uBAAuB;CAO5B;AAED,qBAAa,QAAQ;IAEjB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;KACtB;IAGG,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;YAc7C,sBAAsB;CAmCrC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
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.Reporter = exports.MessageBuilder = void 0;
|
|
7
|
+
const async_retry_1 = __importDefault(require("async-retry"));
|
|
8
|
+
const DASHBOARD_DOMAIN = process.env.DASHBOARD_DOMAIN ||
|
|
9
|
+
(process.env.CI === "true" ? "https://dash.empirical.run" : "");
|
|
10
|
+
class MessageBuilder {
|
|
11
|
+
props;
|
|
12
|
+
constructor(props) {
|
|
13
|
+
this.props = props;
|
|
14
|
+
}
|
|
15
|
+
buildMessage(dashboardOptions) {
|
|
16
|
+
return {
|
|
17
|
+
...dashboardOptions?.override,
|
|
18
|
+
message: this.props.message,
|
|
19
|
+
messageType: this.props.type,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.MessageBuilder = MessageBuilder;
|
|
24
|
+
class Reporter {
|
|
25
|
+
config;
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
}
|
|
29
|
+
async report(messageBuilder) {
|
|
30
|
+
const message = messageBuilder.buildMessage({
|
|
31
|
+
override: {
|
|
32
|
+
testSessionId: this.config.testSessionId,
|
|
33
|
+
generationId: this.config.generationId,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (!message) {
|
|
37
|
+
console.info("No message found. Skipping sending message to dashboard");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await this.sendMessageToDashboard(message);
|
|
41
|
+
}
|
|
42
|
+
async sendMessageToDashboard(message) {
|
|
43
|
+
if (!DASHBOARD_DOMAIN) {
|
|
44
|
+
console.warn("No dashboard domain found. Skipping send message to dashboard");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await (0, async_retry_1.default)(async () => {
|
|
49
|
+
const body = JSON.stringify(message);
|
|
50
|
+
await fetch(`${DASHBOARD_DOMAIN}/api/github/updates`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
// TODO: fix this with authentication of github updates api
|
|
55
|
+
Authorization: "weQPMWKT",
|
|
56
|
+
},
|
|
57
|
+
body,
|
|
58
|
+
});
|
|
59
|
+
}, {
|
|
60
|
+
retries: 3,
|
|
61
|
+
minTimeout: 1000,
|
|
62
|
+
maxTimeout: 60000,
|
|
63
|
+
factor: 3,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error(`Error sending message to Dashboard: ${e.code}`);
|
|
68
|
+
console.error(e.message);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.Reporter = Reporter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAmDpC,eAAO,MAAM,4BAA4B,EAAE,IAuE1C,CAAC"}
|
|
@@ -4,6 +4,7 @@ exports.generateTestWithBrowserAgent = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const run_1 = require("../agent/browsing/run");
|
|
6
6
|
const utils_1 = require("../agent/browsing/utils");
|
|
7
|
+
const web_1 = require("../bin/utils/platform/web");
|
|
7
8
|
const scenarios_1 = require("../bin/utils/scenarios");
|
|
8
9
|
const git_1 = require("../utils/git");
|
|
9
10
|
const BrowserAgentSchema = zod_1.z.object({
|
|
@@ -65,6 +66,12 @@ exports.generateTestWithBrowserAgent = {
|
|
|
65
66
|
result: `Invalid project name: ${project}. Valid project names are: ${validProjectNames.join(", ")}`,
|
|
66
67
|
};
|
|
67
68
|
}
|
|
69
|
+
if (!(0, web_1.hasTestBlock)({ testName, testSuites, filePath: fileName })) {
|
|
70
|
+
return {
|
|
71
|
+
isError: true,
|
|
72
|
+
result: `Test block not found for test name: "${testName}" in file: "${fileName}" with describe blocks: "${testSuites.join(", ")}"`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
68
75
|
try {
|
|
69
76
|
await (0, utils_1.replaceTodoWithCreateTest)({
|
|
70
77
|
testCaseName: testName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,UAKlB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAEtE;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,eAAe,EACf,QAAQ,EACR,OAAO,GACR,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CAgED;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
|
package/dist/uploader/index.js
CHANGED
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.checkIfResultsUploadAllowed = exports.getUploadPathForRun = exports.uploadTestResultsUsingPrjRepo = exports.getRelativeUploadPath = exports.getFullUploadPath = exports.UPLOAD_DOMAIN = exports.UPLOAD_BUCKET = void 0;
|
|
7
7
|
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
8
|
-
const reporter_1 = require("@empiricalrun/reporter");
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
10
|
// json summary of test results
|
|
11
11
|
// originally we used to upload test results directory for this
|
|
12
12
|
// but now we have our test results (per testcase artifacts) inside playwright-report/data
|
|
@@ -55,8 +55,8 @@ async function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, repoDi
|
|
|
55
55
|
const fileNames = Object.keys(files);
|
|
56
56
|
console.log("Uploaded files", fileNames.map((f) => getFullUploadPath(repoDir, f, uploadDir)));
|
|
57
57
|
const defaultLocation = path_1.default.join(repoDir, "playwright-report", "summary.json");
|
|
58
|
-
const results = (0,
|
|
59
|
-
const flatTestsList = (0,
|
|
58
|
+
const results = (0, utils_1.parseJsonReport)(defaultLocation);
|
|
59
|
+
const flatTestsList = (0, utils_1.getFlattenedTestList)(results.suites);
|
|
60
60
|
const testAttachmentPaths = [];
|
|
61
61
|
for (const test of flatTestsList) {
|
|
62
62
|
if (test.title === testName) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSONReportSpec, JSONReportSuite } from "@playwright/test/reporter";
|
|
2
|
+
export declare function parseJsonReport<T>(srcFile: string): T;
|
|
3
|
+
export type flattenedSpecT = JSONReportSpec & {
|
|
4
|
+
nesting: string[];
|
|
5
|
+
suitesString: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const getFlattenedTestList: (suites: JSONReportSuite[]) => flattenedSpecT[];
|
|
8
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/uploader/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAChB,MAAM,2BAA2B,CAAC;AAGnC,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAErD;AAMD,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AACF,eAAO,MAAM,oBAAoB,WACvB,eAAe,EAAE,KACxB,cAAc,EA4BhB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
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.getFlattenedTestList = exports.parseJsonReport = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
function parseJsonReport(srcFile) {
|
|
9
|
+
return JSON.parse(fs_1.default.readFileSync(srcFile, { encoding: "utf-8" }));
|
|
10
|
+
}
|
|
11
|
+
exports.parseJsonReport = parseJsonReport;
|
|
12
|
+
const getFlattenedTestList = (suites) => {
|
|
13
|
+
let flattenedSpecs = [];
|
|
14
|
+
const traverseSuites = (suite, nesting = []) => {
|
|
15
|
+
nesting = [...nesting, suite.title];
|
|
16
|
+
flattenedSpecs.push(...suite.specs.map((spec) => {
|
|
17
|
+
const finalNesting = [...nesting, spec.title];
|
|
18
|
+
const suites = nesting.slice(1);
|
|
19
|
+
const suitesString = suites.length ? suites.join("~~") : "~~";
|
|
20
|
+
return {
|
|
21
|
+
...spec,
|
|
22
|
+
nesting: finalNesting,
|
|
23
|
+
suitesString,
|
|
24
|
+
};
|
|
25
|
+
}));
|
|
26
|
+
// Traverse nested suites recursively
|
|
27
|
+
if (suite.suites && suite.suites.length > 0) {
|
|
28
|
+
suite.suites.forEach((suite) => traverseSuites(suite, nesting));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
// Start the traversal from the top-level suites
|
|
32
|
+
suites.forEach((suite) => traverseSuites(suite));
|
|
33
|
+
return flattenedSpecs;
|
|
34
|
+
};
|
|
35
|
+
exports.getFlattenedTestList = getFlattenedTestList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-tree.d.ts","sourceRoot":"","sources":["../../src/utils/repo-tree.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"repo-tree.d.ts","sourceRoot":"","sources":["../../src/utils/repo-tree.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,qBAU3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,UAsE9D"}
|
package/dist/utils/repo-tree.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/test-gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@actions/core": "^1.10.1",
|
|
49
49
|
"@babel/parser": "^7.26.3",
|
|
50
50
|
"@types/sanitize-html": "^2.11.0",
|
|
51
|
+
"async-retry": "^1.3.3",
|
|
51
52
|
"commander": "^12.1.0",
|
|
52
53
|
"detect-port": "^1.6.1",
|
|
53
54
|
"dotenv": "^16.4.5",
|
|
@@ -74,13 +75,13 @@
|
|
|
74
75
|
"tsx": "^4.16.2",
|
|
75
76
|
"typescript": "^5.3.3",
|
|
76
77
|
"zod": "^3.23.8",
|
|
77
|
-
"@empiricalrun/llm": "^0.
|
|
78
|
+
"@empiricalrun/llm": "^0.12.0",
|
|
78
79
|
"@empiricalrun/r2-uploader": "^0.3.8",
|
|
79
|
-
"@empiricalrun/reporter": "^0.23.2",
|
|
80
80
|
"@empiricalrun/test-run": "^0.7.6"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@playwright/test": "1.47.1",
|
|
84
|
+
"@types/async-retry": "^1.4.8",
|
|
84
85
|
"@types/detect-port": "^1.3.5",
|
|
85
86
|
"@types/express": "^4.17.21",
|
|
86
87
|
"@types/fs-extra": "^11.0.4",
|
package/dist/agent/chat.d.ts
DELETED
package/dist/agent/chat.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/agent/chat.ts"],"names":[],"mappings":"AA+FA,wBAAsB,SAAS,CAAC,EAC9B,SAAwC,EACxC,mBAAmB,GACpB,EAAE;IACD,SAAS,CAAC,EAAE,4BAA4B,GAAG,4BAA4B,CAAC;IACxE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,mBAkHA"}
|
package/dist/agent/chat.js
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
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.chatAgent = void 0;
|
|
7
|
-
const chat_1 = require("@empiricalrun/llm/chat");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const picocolors_1 = require("picocolors");
|
|
10
|
-
const web_1 = require("../bin/utils/platform/web");
|
|
11
|
-
const human_in_the_loop_1 = require("../human-in-the-loop");
|
|
12
|
-
const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
|
|
13
|
-
const grep_1 = require("../tools/grep");
|
|
14
|
-
const test_gen_browser_1 = require("../tools/test-gen-browser");
|
|
15
|
-
const test_run_1 = require("../tools/test-run");
|
|
16
|
-
const test_run_fetcher_1 = require("../tools/test-run-fetcher");
|
|
17
|
-
const zod_schema_1 = require("../tools/zod-schema");
|
|
18
|
-
const repo_tree_1 = require("../utils/repo-tree");
|
|
19
|
-
const systemPrompt = `
|
|
20
|
-
You are a helpful assistant that can answer questions and help with tasks.
|
|
21
|
-
You are given a set of tools to use to fulfill the user's request. Read their descriptions to
|
|
22
|
-
understand what each tool does.
|
|
23
|
-
|
|
24
|
-
# Tools
|
|
25
|
-
|
|
26
|
-
For example, if the user asks you to run a test, you could use the runTest tool.
|
|
27
|
-
Once the test is run, you will receive the results in the form of a JSON object.
|
|
28
|
-
Summarize the results in a few sentences.
|
|
29
|
-
|
|
30
|
-
If the user provides a diagnosis URL, you can use the fetchDiagnosisDetails tool
|
|
31
|
-
to get more information about the test case and its results.
|
|
32
|
-
|
|
33
|
-
If the user provides a test run URL, you can use the fetchTestRunDetails tool
|
|
34
|
-
to get detailed information about a specific test run.
|
|
35
|
-
|
|
36
|
-
Or if the user asks you to modify a test, you could use the generateTestWithBrowserAgent tool. If you suspect
|
|
37
|
-
that a UI selector needs to be updated, using the browser agent is a good idea.
|
|
38
|
-
|
|
39
|
-
Before using generateTestWithBrowserAgent, you need to prepare the test code for the browser agent.
|
|
40
|
-
You can do this by using the str_replace_editor tool to add a TODO comment to the test code. This
|
|
41
|
-
comment should explain to the browser agent what to do.
|
|
42
|
-
|
|
43
|
-
For example, if the expected modification is to click on a login button, you could add the following comment.
|
|
44
|
-
|
|
45
|
-
// TODO(agent): Click on the login button
|
|
46
|
-
|
|
47
|
-
The position of the comment is important: the browser agent will look for this comment and replace it with
|
|
48
|
-
the actual code to click on the login button. If you are fixing a failing test, your comment should be
|
|
49
|
-
around the failing line of code, so that it can be replaced/modified.
|
|
50
|
-
|
|
51
|
-
# Repo context
|
|
52
|
-
|
|
53
|
-
You are running as a CLI tool inside the directory of the repo where this test file is located. Here is
|
|
54
|
-
the repo directory structure:
|
|
55
|
-
|
|
56
|
-
${(0, repo_tree_1.generateAsciiTree)(process.cwd())}
|
|
57
|
-
|
|
58
|
-
While specifying paths to files, use relative paths from the current working directory. For example:
|
|
59
|
-
- Correct path: "tests/lesson.spec.ts"
|
|
60
|
-
- Incorrect path: "/repo/tests/lesson.spec.ts" or "${path_1.default.basename(process.cwd())}/tests/lesson.spec.ts"
|
|
61
|
-
|
|
62
|
-
# Rules for fixing Playwright tests
|
|
63
|
-
|
|
64
|
-
You must follow these rules while adding new tests or modifying existing tests. There can be exceptions to these rules, but
|
|
65
|
-
ONLY when explicitly asked for by the user.
|
|
66
|
-
|
|
67
|
-
1. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
|
|
68
|
-
2. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
|
|
69
|
-
3. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
|
|
70
|
-
4. 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)
|
|
71
|
-
|
|
72
|
-
# Proactiveness
|
|
73
|
-
|
|
74
|
-
You are allowed to be proactive, but only when the user asks you to do something. You should strive to
|
|
75
|
-
strike a balance between:
|
|
76
|
-
1. Doing the right thing when asked, including taking actions and follow-up actions
|
|
77
|
-
2. Not surprising the user with actions you take without asking. It is okay to ask the user for confirmation before taking actions.
|
|
78
|
-
`;
|
|
79
|
-
const tools = [
|
|
80
|
-
test_run_1.runTestTool,
|
|
81
|
-
test_gen_browser_1.generateTestWithBrowserAgent,
|
|
82
|
-
diagnosis_fetcher_1.diagnosisTool,
|
|
83
|
-
grep_1.grepTool,
|
|
84
|
-
test_run_fetcher_1.testRunTool,
|
|
85
|
-
];
|
|
86
|
-
const toolExecutors = {
|
|
87
|
-
...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
|
|
88
|
-
str_replace_editor: (input) => (0, chat_1.strReplaceEditorTool)(input, web_1.validateTypescript),
|
|
89
|
-
};
|
|
90
|
-
async function chatAgent({ chatModel = "claude-3-7-sonnet-20250219", useDiskForChatState, }) {
|
|
91
|
-
const ora = (await import("ora")).default;
|
|
92
|
-
let userPrompt = undefined;
|
|
93
|
-
let chatState = useDiskForChatState ? chat_1.ChatState.load() : new chat_1.ChatState(false);
|
|
94
|
-
const handleSigInt = () => {
|
|
95
|
-
console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
|
|
96
|
-
process.exit(0);
|
|
97
|
-
};
|
|
98
|
-
process.once("SIGINT", handleSigInt);
|
|
99
|
-
process.once("SIGTERM", handleSigInt);
|
|
100
|
-
if (chatState.askUserForInput) {
|
|
101
|
-
// Show last message to the user for context when we loaded from disk
|
|
102
|
-
const messages = chatState.messages;
|
|
103
|
-
const lastMessage = messages[messages.length - 1];
|
|
104
|
-
if (lastMessage && Array.isArray(lastMessage.content)) {
|
|
105
|
-
const textContent = lastMessage.content.find((b) => b.type === "text");
|
|
106
|
-
if (textContent) {
|
|
107
|
-
const role = lastMessage.role.charAt(0).toUpperCase() + lastMessage.role.slice(1);
|
|
108
|
-
console.log(`${role}: ${textContent.text}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
while (!userPrompt?.toLowerCase().includes("stop")) {
|
|
113
|
-
chatState.saveToDisk();
|
|
114
|
-
if (chatState.askUserForInput) {
|
|
115
|
-
try {
|
|
116
|
-
userPrompt = await human_in_the_loop_1.humanLoop.getFeedback({
|
|
117
|
-
message: "User:",
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
catch (e) {
|
|
121
|
-
// https://github.com/SBoudrias/Inquirer.js/issues/1502#issuecomment-2275991680
|
|
122
|
-
if (e instanceof Error && e.name === "ExitPromptError") {
|
|
123
|
-
console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
|
|
124
|
-
process.exit(0);
|
|
125
|
-
}
|
|
126
|
-
throw e;
|
|
127
|
-
}
|
|
128
|
-
chatState.pushMessage({
|
|
129
|
-
role: "user",
|
|
130
|
-
content: [
|
|
131
|
-
{
|
|
132
|
-
type: "text",
|
|
133
|
-
text: userPrompt,
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
});
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const toolUse = chatState.getPendingToolCall();
|
|
140
|
-
if (toolUse) {
|
|
141
|
-
const spinner = ora(`Executing tool ${toolUse.name} with args: ${JSON.stringify(toolUse.input)}`).start();
|
|
142
|
-
const toolExecutor = toolExecutors[toolUse.name];
|
|
143
|
-
if (!toolExecutor) {
|
|
144
|
-
throw new Error(`Tool ${toolUse.name} not found`);
|
|
145
|
-
}
|
|
146
|
-
const toolResult = await toolExecutor(toolUse.input);
|
|
147
|
-
if (toolResult.isError) {
|
|
148
|
-
spinner.fail(`Tool ${toolUse.name} failed with error: ${toolResult.result}`);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
spinner.succeed(`Tool ${toolUse.name} completed`);
|
|
152
|
-
}
|
|
153
|
-
chatState.pushMessage({
|
|
154
|
-
role: "user",
|
|
155
|
-
content: [
|
|
156
|
-
{
|
|
157
|
-
type: "tool_result",
|
|
158
|
-
tool_use_id: toolUse.id,
|
|
159
|
-
content: toolResult.result,
|
|
160
|
-
is_error: toolResult.isError,
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
});
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
const spinner = ora("Claude is working...").start();
|
|
167
|
-
const response = await (0, chat_1.createClaudeMessage)({
|
|
168
|
-
systemPrompt,
|
|
169
|
-
messages: chatState.getMessagesForCreateCompletion(),
|
|
170
|
-
tools: tools.map((tool) => (0, chat_1.convertOpenAISchemaToAnthropic)((0, zod_schema_1.zodToOpenAITool)(tool.schema))),
|
|
171
|
-
model: chatModel,
|
|
172
|
-
withStrReplaceEditor: true,
|
|
173
|
-
});
|
|
174
|
-
spinner.stop();
|
|
175
|
-
if (!response) {
|
|
176
|
-
throw new Error("No response from LLM");
|
|
177
|
-
}
|
|
178
|
-
chatState.pushMessage(response);
|
|
179
|
-
const textBlock = response.content.find((b) => b.type === "text");
|
|
180
|
-
if (textBlock) {
|
|
181
|
-
console.log("Assistant:", textBlock.text);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
const usageSummary = chatState.getUsageSummary();
|
|
185
|
-
console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
|
|
186
|
-
return usageSummary;
|
|
187
|
-
}
|
|
188
|
-
exports.chatAgent = chatAgent;
|