@empiricalrun/test-gen 0.27.9 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/actions/fill.d.ts.map +1 -1
- package/dist/actions/fill.js +18 -3
- package/dist/actions/index.d.ts +3 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +20 -10
- package/dist/actions/text-content.d.ts +4 -0
- package/dist/actions/text-content.d.ts.map +1 -0
- package/dist/actions/text-content.js +53 -0
- package/dist/agent/browsing/index.d.ts.map +1 -1
- package/dist/agent/browsing/index.js +2 -0
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +25 -54
- package/dist/agent/codegen/create-test-block.d.ts +9 -0
- package/dist/agent/codegen/create-test-block.d.ts.map +1 -0
- package/dist/agent/codegen/create-test-block.js +63 -0
- package/dist/agent/codegen/fix-ts-errors.d.ts +2 -3
- package/dist/agent/codegen/fix-ts-errors.d.ts.map +1 -1
- package/dist/agent/codegen/fix-ts-errors.js +12 -16
- package/dist/agent/codegen/run.d.ts.map +1 -1
- package/dist/agent/codegen/run.js +4 -2
- package/dist/agent/codegen/update-flow.d.ts +8 -0
- package/dist/agent/codegen/update-flow.d.ts.map +1 -1
- package/dist/agent/codegen/update-flow.js +139 -58
- package/dist/bin/index.js +3 -0
- package/dist/bin/utils/index.d.ts +1 -1
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +2 -2
- package/dist/bin/utils/platform/web/index.d.ts +5 -1
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +55 -7
- package/dist/bin/utils/scenarios/index.d.ts +1 -1
- package/dist/bin/utils/scenarios/index.d.ts.map +1 -1
- package/dist/bin/utils/scenarios/index.js +3 -3
- package/dist/reporter/index.d.ts +1 -0
- package/dist/reporter/index.d.ts.map +1 -1
- package/dist/test-build/index.d.ts +10 -0
- package/dist/test-build/index.d.ts.map +1 -0
- package/dist/test-build/index.js +30 -0
- package/dist/types/index.d.ts +7 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.28.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a6fe36d: fix: incomplete llm response at the time of fixing ts errors
|
|
8
|
+
|
|
9
|
+
## 0.28.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- a3761d0: feat: support test block extraction via suites param
|
|
14
|
+
- 7ab94df: feat: add support for downloading latest build during test gen workflow
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [cc12707]
|
|
19
|
+
- @empiricalrun/reporter@0.18.2
|
|
20
|
+
|
|
21
|
+
## 0.27.10
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 66aa7b6: fix: master agent gives up on blank page and coding agent hallucination
|
|
26
|
+
- Updated dependencies [aadad32]
|
|
27
|
+
- Updated dependencies [bac164e]
|
|
28
|
+
- @empiricalrun/reporter@0.18.1
|
|
29
|
+
- @empiricalrun/r2-uploader@0.3.0
|
|
30
|
+
|
|
3
31
|
## 0.27.9
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/actions/fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,2BAA2B,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/actions/fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,2BAA2B,uBAAuB,CAAC;AAGhE,eAAO,MAAM,mBAAmB,EAAE,yBAgEjC,CAAC"}
|
package/dist/actions/fill.js
CHANGED
|
@@ -4,12 +4,16 @@ exports.fillActionGenerator = exports.PLAYWRIGHT_FILL_ACTION_NAME = void 0;
|
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
6
|
exports.PLAYWRIGHT_FILL_ACTION_NAME = "fill_input_element";
|
|
7
|
-
const
|
|
7
|
+
const NO_STATE_VARIABLE = "NA";
|
|
8
|
+
const fillActionGenerator = (page, options) => {
|
|
9
|
+
const stateVariableNames = Object.keys(options?.stateVariables || {});
|
|
10
|
+
stateVariableNames.push(NO_STATE_VARIABLE);
|
|
8
11
|
return {
|
|
9
12
|
execute: async (args) => {
|
|
10
13
|
const css = args.css_selector;
|
|
11
14
|
const locator = await (0, utils_1.getPlaywrightLocatorUsingCssSelector)(css, page);
|
|
12
|
-
const
|
|
15
|
+
const textToFill = options?.stateVariables[args.variable_name] || args.text;
|
|
16
|
+
const exec = new Function("page", `return page.${locator}.fill("${textToFill}", { timeout: 3000 })`);
|
|
13
17
|
await exec(page);
|
|
14
18
|
return {
|
|
15
19
|
locator,
|
|
@@ -32,6 +36,17 @@ const fillActionGenerator = (page) => {
|
|
|
32
36
|
type: "string",
|
|
33
37
|
description: "The text to fill the input element with",
|
|
34
38
|
},
|
|
39
|
+
variable_name: {
|
|
40
|
+
type: "string",
|
|
41
|
+
enum: stateVariableNames,
|
|
42
|
+
description: `variable name whose value needs to be filled in the input element.
|
|
43
|
+
The variable name and values are:
|
|
44
|
+
|
|
45
|
+
${JSON.stringify(stateVariableNames, null, 2)}
|
|
46
|
+
|
|
47
|
+
- Choose the variable name based on the value above.
|
|
48
|
+
- Set variable_name as 'NA' if there is no variable to be used.`,
|
|
49
|
+
},
|
|
35
50
|
css_selector: {
|
|
36
51
|
type: "string",
|
|
37
52
|
description: "CSS selector to identify the element uniquely.When creating CSS selectors, ensure they are unique to the page and specific enough to select only one element.",
|
|
@@ -41,7 +56,7 @@ const fillActionGenerator = (page) => {
|
|
|
41
56
|
description: constants_1.DEFAULT_ACTION_REASON_PROMPT,
|
|
42
57
|
},
|
|
43
58
|
},
|
|
44
|
-
required: ["text", "css_selector", "reason"],
|
|
59
|
+
required: ["variable_name", "text", "css_selector", "reason"],
|
|
45
60
|
},
|
|
46
61
|
},
|
|
47
62
|
},
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Page } from "playwright";
|
|
2
2
|
import { ActionSchema } from "../types";
|
|
3
3
|
export declare class PlaywrightActions {
|
|
4
|
-
private
|
|
4
|
+
private page;
|
|
5
|
+
private actionGenerators;
|
|
5
6
|
private recordedActions;
|
|
7
|
+
private stateVariables;
|
|
6
8
|
constructor(page: Page);
|
|
7
9
|
executeAction(name: string | undefined, args: Record<string, any>): Promise<void>;
|
|
8
10
|
getActionSchemas(): ActionSchema[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIlC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIlC,OAAO,EAAE,YAAY,EAA6B,MAAM,UAAU,CAAC;AASnE,qBAAa,iBAAiB;IAMhB,OAAO,CAAC,IAAI;IAJxB,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,cAAc,CAA2B;gBAE7B,IAAI,EAAE,IAAI;IAaxB,aAAa,CAAC,IAAI,oBAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAyBhE,gBAAgB,IAAI,YAAY,EAAE;IAMlC,YAAY;IAIZ,gBAAgB,CAAC,KAAK,EAAE,MAAM;IAO9B,UAAU;CAUX"}
|
package/dist/actions/index.js
CHANGED
|
@@ -9,22 +9,30 @@ const done_1 = require("./done");
|
|
|
9
9
|
const fill_1 = require("./fill");
|
|
10
10
|
const goto_1 = require("./goto");
|
|
11
11
|
const reload_page_1 = require("./reload-page");
|
|
12
|
+
const text_content_1 = require("./text-content");
|
|
12
13
|
class PlaywrightActions {
|
|
13
|
-
|
|
14
|
+
page;
|
|
15
|
+
// private actions: Action[];
|
|
16
|
+
actionGenerators;
|
|
14
17
|
recordedActions;
|
|
18
|
+
stateVariables = {};
|
|
15
19
|
constructor(page) {
|
|
16
|
-
this.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
this.page = page;
|
|
21
|
+
this.actionGenerators = [
|
|
22
|
+
fill_1.fillActionGenerator,
|
|
23
|
+
goto_1.gotoActionGenerator,
|
|
24
|
+
click_1.clickActionGenerator,
|
|
25
|
+
done_1.doneActionGenerator,
|
|
26
|
+
assert_1.assertTextVisibilityActionGenerator,
|
|
27
|
+
reload_page_1.reloadActionGenerator,
|
|
28
|
+
text_content_1.textContentActionGenerator,
|
|
23
29
|
];
|
|
24
30
|
this.recordedActions = [];
|
|
25
31
|
}
|
|
26
32
|
async executeAction(name = "", args) {
|
|
27
|
-
const [action] = this.
|
|
33
|
+
const [action] = this.actionGenerators
|
|
34
|
+
.map((a) => a(this.page, { stateVariables: this.stateVariables }))
|
|
35
|
+
.filter((a) => a.name === name);
|
|
28
36
|
if (!action) {
|
|
29
37
|
throw Error(`No action registered for action: ${name}`);
|
|
30
38
|
}
|
|
@@ -47,7 +55,9 @@ class PlaywrightActions {
|
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
57
|
getActionSchemas() {
|
|
50
|
-
return this.
|
|
58
|
+
return this.actionGenerators
|
|
59
|
+
.map((a) => a(this.page, { stateVariables: this.stateVariables }))
|
|
60
|
+
.map((a) => a.schema);
|
|
51
61
|
}
|
|
52
62
|
generateCode() {
|
|
53
63
|
return this.recordedActions.map((a) => a.code).join("\n");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-content.d.ts","sourceRoot":"","sources":["../../src/actions/text-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAE1D,eAAO,MAAM,0BAA0B,EAAE,yBAkDxC,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textContentActionGenerator = exports.PLAYWRIGHT_GOTO_ACTION_NAME = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
exports.PLAYWRIGHT_GOTO_ACTION_NAME = "text_content";
|
|
7
|
+
const textContentActionGenerator = (page, options) => {
|
|
8
|
+
return {
|
|
9
|
+
execute: async (args) => {
|
|
10
|
+
const css = args.css_selector;
|
|
11
|
+
const locator = await (0, utils_1.getPlaywrightLocatorUsingCssSelector)(css, page);
|
|
12
|
+
const exec = new Function("page", `return page.${locator}.textContent()`);
|
|
13
|
+
const value = (await exec(page));
|
|
14
|
+
if (options) {
|
|
15
|
+
options.stateVariables[args.variable_name] = value;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
locator,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
template: (args, options) => {
|
|
22
|
+
const templ = `const ${args.variable_name} = await ${(0, utils_1.getPageVarName)()}.${options.locator}.textContent();`;
|
|
23
|
+
return templ;
|
|
24
|
+
},
|
|
25
|
+
name: exports.PLAYWRIGHT_GOTO_ACTION_NAME,
|
|
26
|
+
schema: {
|
|
27
|
+
type: "function",
|
|
28
|
+
function: {
|
|
29
|
+
name: exports.PLAYWRIGHT_GOTO_ACTION_NAME,
|
|
30
|
+
description: "extract text content of the element",
|
|
31
|
+
parameters: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
variable_name: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "name of the variable to store the text content. The variable name should describe what the text content is about.",
|
|
37
|
+
},
|
|
38
|
+
css_selector: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "CSS selector to identify the element uniquely.When creating CSS selectors, ensure they are unique to the page and specific enough to select only one element.",
|
|
41
|
+
},
|
|
42
|
+
reason: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: constants_1.DEFAULT_ACTION_REASON_PROMPT,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: ["variable_name", "css_selector", "reason"],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
exports.textContentActionGenerator = textContentActionGenerator;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC1D,YAAY,CAAC,EAAE;QACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC1D,YAAY,CAAC,EAAE;QACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,mBAuO9B"}
|
|
@@ -36,6 +36,8 @@ async function browsingAgentUsingMasterAgent(task, page, options) {
|
|
|
36
36
|
provider: options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
37
37
|
defaultModel: options.model || constants_1.DEFAULT_MODEL,
|
|
38
38
|
providerApiKey: constants_1.MODEL_API_KEYS[options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
39
|
+
// we will be using google model for larger context window, in such cases 1 million tokens is not enough
|
|
40
|
+
maxTokens: options.modelProvider === "google" ? 3000000 : 1000000,
|
|
39
41
|
});
|
|
40
42
|
const actions = new actions_1.PlaywrightActions(page);
|
|
41
43
|
const tools = actions.getActionSchemas();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAWvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAWvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAK5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAqDD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAyBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBAiBxD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,QA6BjD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAM1E;AAWD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,CAAC,CA8CjB;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,YAAiB,EACjB,IAAS,EACT,eAAoB,EACpB,gBAAqB,EACrB,UAAyC,GAC1C,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,8EASA"}
|
|
@@ -12,6 +12,7 @@ const api_1 = __importDefault(require("tsx/cjs/api"));
|
|
|
12
12
|
const logger_1 = require("../../bin/logger");
|
|
13
13
|
const context_1 = require("../../bin/utils/context");
|
|
14
14
|
const web_1 = require("../../bin/utils/platform/web");
|
|
15
|
+
const create_test_block_1 = require("../codegen/create-test-block");
|
|
15
16
|
const fix_ts_errors_1 = require("../codegen/fix-ts-errors");
|
|
16
17
|
const update_flow_1 = require("../codegen/update-flow");
|
|
17
18
|
function isRegExp(obj) {
|
|
@@ -31,25 +32,16 @@ exports.prepareBrowsingAgentTask = prepareBrowsingAgentTask;
|
|
|
31
32
|
*/
|
|
32
33
|
async function prepareFileForUpdateScenario(genConfig) {
|
|
33
34
|
const { specPath, testCase } = genConfig;
|
|
34
|
-
const { name
|
|
35
|
+
const { name } = testCase;
|
|
35
36
|
// update the test case with appropriate location for createTest
|
|
36
37
|
// TODO: reduce the payload for this LLM call. Only provide test file and page files which are used in the test block
|
|
37
38
|
// this will help with faster response
|
|
38
|
-
const [suggestion] = await (0, update_flow_1.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- This method will help you execute given task.
|
|
45
|
-
- Given the task you need to decide the right file path and code block to place this method.
|
|
46
|
-
- The task should contain hints as in where to place this method.
|
|
47
|
-
- If there is no hint provided in the task, you can assume the method needs to be placed at the end of the test block.
|
|
48
|
-
- YOU NEED TO MANDATORILY USE "createTest" method to execute the task.
|
|
49
|
-
- Once the methods is placed in the right location with a task and playwright page provided, assume the task is done.
|
|
50
|
-
- You need to respond with file path, code block and updated code block where this method can be placed in order to accomplish the task`,
|
|
51
|
-
],
|
|
52
|
-
}, specPath, genConfig.options, false, false);
|
|
39
|
+
const [suggestion] = await (0, update_flow_1.appendCreateTestBlock)({
|
|
40
|
+
testCase,
|
|
41
|
+
file: specPath,
|
|
42
|
+
validateTypes: false,
|
|
43
|
+
options: genConfig.options,
|
|
44
|
+
});
|
|
53
45
|
const createTestFilePath = suggestion?.updatedFiles[0] || "";
|
|
54
46
|
console.log("appending to existing test block");
|
|
55
47
|
console.log("updated test file path", createTestFilePath);
|
|
@@ -60,30 +52,17 @@ async function prepareFileForUpdateScenario(genConfig) {
|
|
|
60
52
|
testCode: codePrompt,
|
|
61
53
|
pomCode: pomPrompt,
|
|
62
54
|
testCase: testCase,
|
|
63
|
-
options: genConfig.options,
|
|
64
55
|
});
|
|
65
56
|
const testFileContent = await fs_extra_1.default.readFile(specPath, "utf-8");
|
|
66
|
-
const { testBlock, testNode } = (0, web_1.getTypescriptTestBlock)(
|
|
57
|
+
const { testBlock, testNode } = (0, web_1.getTypescriptTestBlock)({
|
|
58
|
+
scenarioName: name,
|
|
59
|
+
content: testFileContent,
|
|
60
|
+
});
|
|
67
61
|
const parentDescribe = (0, web_1.findFirstSerialDescribeBlock)(testNode);
|
|
68
62
|
// add test.only / describe.only to the spec file so that only that block is executed
|
|
69
63
|
const updatedTestFileContent = newContentsWithTestOnly(testFileContent, testBlock, testBlock, parentDescribe?.getText() || "");
|
|
70
64
|
await fs_extra_1.default.writeFile(specPath, updatedTestFileContent);
|
|
71
65
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Function to prepare test file for new scenarios for master agent to run
|
|
74
|
-
* @param {TestGenConfig} genConfig
|
|
75
|
-
*/
|
|
76
|
-
async function prepareFileForNewScenario(genConfig) {
|
|
77
|
-
const { specPath, testCase } = genConfig;
|
|
78
|
-
const { name, steps } = testCase;
|
|
79
|
-
console.log("creating new test block");
|
|
80
|
-
const mergedSteps = prepareBrowsingAgentTask(steps);
|
|
81
|
-
// TODO: this assumes that test code repo has `page` as the main entrypoint fixture
|
|
82
|
-
const testGenCodeBlock = createTestBlockForCreateTest(name, mergedSteps);
|
|
83
|
-
const existingContents = await fs_extra_1.default.readFile(specPath, "utf-8");
|
|
84
|
-
const newContents = `${existingContents}\n\n${testGenCodeBlock}`;
|
|
85
|
-
await fs_extra_1.default.writeFile(specPath, (0, web_1.addNewImport)(newContents, ["createTest"], "@empiricalrun/test-gen"));
|
|
86
|
-
}
|
|
87
66
|
/**
|
|
88
67
|
* Function to prepare test file for master agent to run
|
|
89
68
|
* @param {TestGenConfig} genConfig
|
|
@@ -99,16 +78,21 @@ async function prepareFileForMasterAgent(genConfig) {
|
|
|
99
78
|
const fileContentWithImports = (0, web_1.addNewImport)("", ["test", "expect"], (0, web_1.getFixtureImportPath)(specPath));
|
|
100
79
|
await fs_extra_1.default.writeFile(specPath, fileContentWithImports, "utf-8");
|
|
101
80
|
}
|
|
102
|
-
let createTestFilePath = specPath;
|
|
103
81
|
const existingContents = await fs_extra_1.default.readFile(specPath, "utf-8");
|
|
104
|
-
const { testBlock } = (0, web_1.getTypescriptTestBlock)(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
await
|
|
82
|
+
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
83
|
+
scenarioName: name,
|
|
84
|
+
content: existingContents,
|
|
85
|
+
});
|
|
86
|
+
if (!testBlock) {
|
|
87
|
+
const newTestBlock = await (0, create_test_block_1.createEmptyTestCaseBlock)({
|
|
88
|
+
testCase: genConfig.testCase,
|
|
89
|
+
file: specPath,
|
|
90
|
+
options: genConfig.options,
|
|
91
|
+
});
|
|
92
|
+
await fs_extra_1.default.writeFile(specPath, `${existingContents} \n\n ${newTestBlock}`, "utf-8");
|
|
110
93
|
}
|
|
111
|
-
|
|
94
|
+
await prepareFileForUpdateScenario(genConfig);
|
|
95
|
+
return specPath;
|
|
112
96
|
}
|
|
113
97
|
exports.prepareFileForMasterAgent = prepareFileForMasterAgent;
|
|
114
98
|
function newContentsWithTestOnly(existingContents, originalTestBlock, updatedTestBlock, parentDescribeBlock) {
|
|
@@ -123,19 +107,6 @@ function newContentsWithTestOnly(existingContents, originalTestBlock, updatedTes
|
|
|
123
107
|
return existingContents.replace(parentDescribeBlock, describeMarkedAsOnly);
|
|
124
108
|
}
|
|
125
109
|
}
|
|
126
|
-
function createTestBlockForCreateTest(name, steps) {
|
|
127
|
-
return `
|
|
128
|
-
test.only("${name}", async ({page}) => {
|
|
129
|
-
${createTestGenBlock(steps)}
|
|
130
|
-
});
|
|
131
|
-
`;
|
|
132
|
-
}
|
|
133
|
-
function createTestGenBlock(steps) {
|
|
134
|
-
return `
|
|
135
|
-
await createTest(\`
|
|
136
|
-
${steps}\`, page);
|
|
137
|
-
`;
|
|
138
|
-
}
|
|
139
110
|
async function injectPwLocatorGenerator(page) {
|
|
140
111
|
const scriptResps = await Promise.all([
|
|
141
112
|
"https://assets-test.empirical.run/pw-selector.js",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
+
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
3
|
+
export declare function createEmptyTestCaseBlock({ testCase, file, options, trace, }: {
|
|
4
|
+
testCase: TestCase;
|
|
5
|
+
file: string;
|
|
6
|
+
options?: TestGenConfigOptions;
|
|
7
|
+
trace?: TraceClient;
|
|
8
|
+
}): Promise<string | undefined>;
|
|
9
|
+
//# sourceMappingURL=create-test-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-test-block.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/create-test-block.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,wBAAsB,wBAAwB,CAAC,EAC7C,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,+BAsDA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEmptyTestCaseBlock = void 0;
|
|
4
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
5
|
+
const logger_1 = require("../../bin/logger");
|
|
6
|
+
const context_1 = require("../../bin/utils/context");
|
|
7
|
+
const web_1 = require("../../bin/utils/platform/web");
|
|
8
|
+
const constants_1 = require("../../constants");
|
|
9
|
+
const session_1 = require("../../session");
|
|
10
|
+
async function createEmptyTestCaseBlock({ testCase, file, options, trace, }) {
|
|
11
|
+
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
12
|
+
logger.log("Creating new test block");
|
|
13
|
+
const context = await (0, context_1.contextForGeneration)(file);
|
|
14
|
+
// TODO: move this to a common place
|
|
15
|
+
const session = (0, session_1.getSessionDetails)();
|
|
16
|
+
trace =
|
|
17
|
+
trace ||
|
|
18
|
+
llm_1.langfuseInstance.trace({
|
|
19
|
+
name: "update-test",
|
|
20
|
+
id: crypto.randomUUID(),
|
|
21
|
+
release: session.version,
|
|
22
|
+
tags: [
|
|
23
|
+
options?.metadata.projectName || "",
|
|
24
|
+
options?.metadata.environment || "",
|
|
25
|
+
].filter((s) => !!s),
|
|
26
|
+
});
|
|
27
|
+
const promptSpan = trace?.span({
|
|
28
|
+
name: "build-create-empty-test-case-prompt",
|
|
29
|
+
});
|
|
30
|
+
const prompt = await (0, llm_1.getPrompt)("create-empty-test-block", {
|
|
31
|
+
testFiles: context.codePrompt,
|
|
32
|
+
pageFiles: context.pomPrompt,
|
|
33
|
+
scenarioName: testCase.name,
|
|
34
|
+
scenario: testCase.steps.join("\n"),
|
|
35
|
+
scenarioFile: file,
|
|
36
|
+
});
|
|
37
|
+
promptSpan?.end({ output: { prompt } });
|
|
38
|
+
const llm = new llm_1.LLM({
|
|
39
|
+
trace,
|
|
40
|
+
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
41
|
+
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
42
|
+
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
43
|
+
});
|
|
44
|
+
const firstShotMessage = await llm.createChatCompletion({
|
|
45
|
+
messages: prompt,
|
|
46
|
+
modelParameters: {
|
|
47
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
48
|
+
...options?.modelParameters,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const markdownRemoverSpan = trace?.span({
|
|
52
|
+
name: "remove-markdown-span",
|
|
53
|
+
});
|
|
54
|
+
let response = firstShotMessage?.content || "";
|
|
55
|
+
markdownRemoverSpan?.end({ output: { response } });
|
|
56
|
+
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
57
|
+
scenarioName: testCase.name,
|
|
58
|
+
content: response,
|
|
59
|
+
});
|
|
60
|
+
console.log(`trace: ${trace?.getTraceUrl()}`);
|
|
61
|
+
return testBlock;
|
|
62
|
+
}
|
|
63
|
+
exports.createEmptyTestCaseBlock = createEmptyTestCaseBlock;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { TraceClient } from "@empiricalrun/llm";
|
|
2
2
|
import { CustomLogger } from "../../bin/logger";
|
|
3
|
-
import { TestCase
|
|
4
|
-
export declare function validateAndFixTypescriptErrors({ trace, logger, file, testCode, pomCode, testCase,
|
|
3
|
+
import { TestCase } from "../../types";
|
|
4
|
+
export declare function validateAndFixTypescriptErrors({ trace, logger, file, testCode, pomCode, testCase, }: {
|
|
5
5
|
trace?: TraceClient;
|
|
6
6
|
logger?: CustomLogger;
|
|
7
7
|
file: string;
|
|
8
8
|
testCode: string;
|
|
9
9
|
pomCode: string;
|
|
10
10
|
testCase: TestCase;
|
|
11
|
-
options?: TestGenConfigOptions;
|
|
12
11
|
}): Promise<void>;
|
|
13
12
|
//# sourceMappingURL=fix-ts-errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix-ts-errors.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/fix-ts-errors.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"fix-ts-errors.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/fix-ts-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAK3D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,wBAAsB,8BAA8B,CAAC,EACnD,KAAK,EACL,MAA2B,EAC3B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAQ,GACT,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;CACpB,iBA8DA"}
|
|
@@ -5,11 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.validateAndFixTypescriptErrors = void 0;
|
|
7
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const generative_ai_1 = require("@google/generative-ai");
|
|
8
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const remove_markdown_1 = __importDefault(require("remove-markdown"));
|
|
9
11
|
const logger_1 = require("../../bin/logger");
|
|
10
12
|
const web_1 = require("../../bin/utils/platform/web");
|
|
11
|
-
|
|
12
|
-
async function validateAndFixTypescriptErrors({ trace, logger = new logger_1.CustomLogger(), file, testCode, pomCode, testCase, options, }) {
|
|
13
|
+
async function validateAndFixTypescriptErrors({ trace, logger = new logger_1.CustomLogger(), file, testCode, pomCode, testCase, }) {
|
|
13
14
|
const validateTypesSpan = trace?.span({ name: "detect-type-errors-in-file" });
|
|
14
15
|
logger.log("Validating types...");
|
|
15
16
|
let errors = (0, web_1.validateTypescript)(file);
|
|
@@ -43,20 +44,15 @@ async function validateAndFixTypescriptErrors({ trace, logger = new logger_1.Cus
|
|
|
43
44
|
scenaioName: testCase.name,
|
|
44
45
|
});
|
|
45
46
|
promptSpan?.end({ output: { instruction } });
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
56
|
-
...options?.modelParameters,
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
const response = message?.content || "";
|
|
47
|
+
const genAI = new generative_ai_1.GoogleGenerativeAI(process.env.GOOGLE_API_KEY);
|
|
48
|
+
const model = genAI.getGenerativeModel({ model: "gemini-1.5-pro-latest" });
|
|
49
|
+
const prompt = instruction.map((p) => p.content);
|
|
50
|
+
const llmOutputTrace = trace?.span({ name: "llm-output" });
|
|
51
|
+
const message = await model.generateContent(prompt);
|
|
52
|
+
llmOutputTrace?.end({ output: { message: message.response.text() } });
|
|
53
|
+
const removeMarkdownSpan = trace?.span({ name: "remove-markdown" });
|
|
54
|
+
let response = (0, remove_markdown_1.default)(message.response.text() || "");
|
|
55
|
+
removeMarkdownSpan?.end({ output: { response } });
|
|
60
56
|
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
61
57
|
await fs_extra_1.default.writeFile(file, response, "utf-8");
|
|
62
58
|
readWriteFileSpan?.end({ output: { response } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CA8FrB"}
|
|
@@ -22,7 +22,10 @@ async function generateTest(testCase, file, options) {
|
|
|
22
22
|
}
|
|
23
23
|
const context = await (0, context_1.contextForGeneration)(file);
|
|
24
24
|
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
25
|
-
const { testBlock } = (0, web_1.getTypescriptTestBlock)(
|
|
25
|
+
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
26
|
+
scenarioName: testCase?.name,
|
|
27
|
+
content: testFileContent,
|
|
28
|
+
});
|
|
26
29
|
const isUpdate = !!testBlock;
|
|
27
30
|
if (isUpdate) {
|
|
28
31
|
return await (0, update_flow_1.updateTest)(testCase, file, options);
|
|
@@ -87,7 +90,6 @@ async function generateTest(testCase, file, options) {
|
|
|
87
90
|
testCode: codePrompt,
|
|
88
91
|
pomCode: pomPrompt,
|
|
89
92
|
testCase: testCase,
|
|
90
|
-
options,
|
|
91
93
|
});
|
|
92
94
|
trace.event({ name: "format-file" });
|
|
93
95
|
await (0, web_1.formatCode)(file);
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
1
2
|
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
2
3
|
type UpdatedTestCase = TestCase & {
|
|
3
4
|
updatedFiles: string[];
|
|
4
5
|
};
|
|
5
6
|
export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions | undefined, logging?: boolean, validate?: boolean): Promise<UpdatedTestCase[]>;
|
|
7
|
+
export declare function appendCreateTestBlock({ testCase, file, options, trace, validateTypes, }: {
|
|
8
|
+
testCase: TestCase;
|
|
9
|
+
file: string;
|
|
10
|
+
options?: TestGenConfigOptions;
|
|
11
|
+
trace?: TraceClient;
|
|
12
|
+
validateTypes?: boolean;
|
|
13
|
+
}): Promise<UpdatedTestCase[]>;
|
|
6
14
|
export {};
|
|
7
15
|
//# sourceMappingURL=update-flow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,KAAK,eAAe,GAAG,QAAQ,GAAG;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AA6GF,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,GAAE,OAAc,EACvB,QAAQ,GAAE,OAAc,GACvB,OAAO,CAAC,eAAe,EAAE,CAAC,CA4E5B;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,KAAK,EACL,aAAoB,GACrB,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgE7B"}
|
|
@@ -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.updateTest = void 0;
|
|
6
|
+
exports.appendCreateTestBlock = exports.updateTest = void 0;
|
|
7
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
8
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
@@ -15,64 +15,15 @@ const constants_1 = require("../../constants");
|
|
|
15
15
|
const session_1 = require("../../session");
|
|
16
16
|
const fix_ts_errors_1 = require("./fix-ts-errors");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
|
-
async function
|
|
19
|
-
const logger = new logger_1.CustomLogger({ useReporter: logging });
|
|
20
|
-
const context = await (0, context_1.contextForGeneration)(file);
|
|
21
|
-
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
22
|
-
const generatedTestCases = [];
|
|
23
|
-
logger.logEmptyLine();
|
|
24
|
-
const session = (0, session_1.getSessionDetails)();
|
|
25
|
-
const trace = llm_1.langfuseInstance.trace({
|
|
26
|
-
name: "update-test",
|
|
27
|
-
id: crypto_1.default.randomUUID(),
|
|
28
|
-
release: session.version,
|
|
29
|
-
tags: [
|
|
30
|
-
options?.metadata.projectName || "",
|
|
31
|
-
options?.metadata.environment || "",
|
|
32
|
-
].filter((s) => !!s),
|
|
33
|
-
});
|
|
34
|
-
trace.event({
|
|
35
|
-
name: "collate-files-as-text",
|
|
36
|
-
output: {
|
|
37
|
-
codePrompt,
|
|
38
|
-
pomPrompt,
|
|
39
|
-
testFileContent,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
trace.update({ input: { testCase } });
|
|
43
|
-
const promptSpan = trace.span({
|
|
44
|
-
name: "update-scenario-prompt",
|
|
45
|
-
});
|
|
46
|
-
const promptName = "update-scenario";
|
|
47
|
-
const instruction = await (0, llm_1.getPrompt)(promptName, {
|
|
48
|
-
testFiles: codePrompt,
|
|
49
|
-
pageFiles: pomPrompt,
|
|
50
|
-
scenarioName: testCase.name,
|
|
51
|
-
scenarioSteps: testCase.steps.join("\n"),
|
|
52
|
-
scenarioFile: file,
|
|
53
|
-
}, 14);
|
|
54
|
-
promptSpan.end({ output: { instruction } });
|
|
55
|
-
const llm = new llm_1.LLM({
|
|
56
|
-
trace,
|
|
57
|
-
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
58
|
-
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
59
|
-
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
60
|
-
});
|
|
61
|
-
const firstShotMessage = await llm.createChatCompletion({
|
|
62
|
-
messages: instruction,
|
|
63
|
-
modelParameters: {
|
|
64
|
-
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
65
|
-
...options?.modelParameters,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
let response = firstShotMessage?.content || "";
|
|
69
|
-
logger.success("Test generated successfully!");
|
|
70
|
-
const fileChanges = (0, utils_1.extractTestUpdates)(response);
|
|
18
|
+
async function applyFileChanges({ validateTypes = true, trace, testCase, fileChanges, logger, pomPrompt, codePrompt, }) {
|
|
71
19
|
await Promise.allSettled(fileChanges.map(async (fileChange) => {
|
|
72
20
|
if (!fileChange.filePath) {
|
|
73
21
|
return;
|
|
74
22
|
}
|
|
75
|
-
const { testBlock: testBlockUpdate } = (0, web_1.getTypescriptTestBlock)(
|
|
23
|
+
const { testBlock: testBlockUpdate } = (0, web_1.getTypescriptTestBlock)({
|
|
24
|
+
scenarioName: testCase?.name || "",
|
|
25
|
+
content: fileChange.newCode || "",
|
|
26
|
+
});
|
|
76
27
|
if (testBlockUpdate) {
|
|
77
28
|
// assuming the test case getting updated
|
|
78
29
|
// maintaining the previous accuracy of the test case update
|
|
@@ -80,7 +31,10 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
80
31
|
let contents = await fs_extra_1.default.readFile(fileChange.filePath, "utf-8");
|
|
81
32
|
const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(fileChange.newCode, testCase?.name);
|
|
82
33
|
let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
|
|
83
|
-
const { testBlock } = (0, web_1.getTypescriptTestBlock)(
|
|
34
|
+
const { testBlock } = (0, web_1.getTypescriptTestBlock)({
|
|
35
|
+
scenarioName: testCase?.name,
|
|
36
|
+
content: contents,
|
|
37
|
+
});
|
|
84
38
|
contents = contents.replace(testBlock, `\n\n${strippedContent}`);
|
|
85
39
|
updatedContent = prependContent + contents;
|
|
86
40
|
await fs_extra_1.default.writeFile(fileChange.filePath, updatedContent, "utf-8");
|
|
@@ -113,7 +67,7 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
113
67
|
readWriteFileSpan.end({ output: { contents } });
|
|
114
68
|
}
|
|
115
69
|
// format and validate file change
|
|
116
|
-
if (
|
|
70
|
+
if (validateTypes) {
|
|
117
71
|
await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
|
|
118
72
|
trace,
|
|
119
73
|
logger,
|
|
@@ -121,13 +75,75 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
121
75
|
testCode: codePrompt,
|
|
122
76
|
pomCode: pomPrompt,
|
|
123
77
|
testCase: testCase,
|
|
124
|
-
options,
|
|
125
78
|
});
|
|
126
79
|
}
|
|
127
80
|
trace.event({ name: "format-file" });
|
|
128
81
|
await (0, web_1.formatCode)(fileChange.filePath);
|
|
129
82
|
logger.success(`${fileChange.filePath} file formatted successfully!`);
|
|
130
83
|
}));
|
|
84
|
+
}
|
|
85
|
+
async function updateTest(testCase, file, options, logging = true, validate = true) {
|
|
86
|
+
const logger = new logger_1.CustomLogger({ useReporter: logging });
|
|
87
|
+
const context = await (0, context_1.contextForGeneration)(file);
|
|
88
|
+
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
89
|
+
const generatedTestCases = [];
|
|
90
|
+
logger.logEmptyLine();
|
|
91
|
+
const session = (0, session_1.getSessionDetails)();
|
|
92
|
+
const trace = llm_1.langfuseInstance.trace({
|
|
93
|
+
name: "update-test",
|
|
94
|
+
id: crypto_1.default.randomUUID(),
|
|
95
|
+
release: session.version,
|
|
96
|
+
tags: [
|
|
97
|
+
options?.metadata.projectName || "",
|
|
98
|
+
options?.metadata.environment || "",
|
|
99
|
+
].filter((s) => !!s),
|
|
100
|
+
});
|
|
101
|
+
trace.event({
|
|
102
|
+
name: "collate-files-as-text",
|
|
103
|
+
output: {
|
|
104
|
+
codePrompt,
|
|
105
|
+
pomPrompt,
|
|
106
|
+
testFileContent,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
trace.update({ input: { testCase } });
|
|
110
|
+
const promptSpan = trace.span({
|
|
111
|
+
name: "update-scenario-prompt",
|
|
112
|
+
});
|
|
113
|
+
const promptName = "update-scenario";
|
|
114
|
+
const instruction = await (0, llm_1.getPrompt)(promptName, {
|
|
115
|
+
testFiles: codePrompt,
|
|
116
|
+
pageFiles: pomPrompt,
|
|
117
|
+
scenarioName: testCase.name,
|
|
118
|
+
scenarioSteps: testCase.steps.join("\n"),
|
|
119
|
+
scenarioFile: file,
|
|
120
|
+
}, 14);
|
|
121
|
+
promptSpan.end({ output: { instruction } });
|
|
122
|
+
const llm = new llm_1.LLM({
|
|
123
|
+
trace,
|
|
124
|
+
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
125
|
+
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
126
|
+
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
127
|
+
});
|
|
128
|
+
const firstShotMessage = await llm.createChatCompletion({
|
|
129
|
+
messages: instruction,
|
|
130
|
+
modelParameters: {
|
|
131
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
132
|
+
...options?.modelParameters,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
let response = firstShotMessage?.content || "";
|
|
136
|
+
logger.success("Test generated successfully!");
|
|
137
|
+
const fileChanges = (0, utils_1.extractTestUpdates)(response);
|
|
138
|
+
await applyFileChanges({
|
|
139
|
+
validateTypes: validate,
|
|
140
|
+
trace,
|
|
141
|
+
testCase,
|
|
142
|
+
fileChanges,
|
|
143
|
+
logger,
|
|
144
|
+
pomPrompt: pomPrompt,
|
|
145
|
+
codePrompt: codePrompt,
|
|
146
|
+
});
|
|
131
147
|
logger.log(`Trace: ${trace.getTraceUrl()}`);
|
|
132
148
|
generatedTestCases.push({
|
|
133
149
|
...testCase,
|
|
@@ -138,3 +154,68 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
138
154
|
return generatedTestCases;
|
|
139
155
|
}
|
|
140
156
|
exports.updateTest = updateTest;
|
|
157
|
+
async function appendCreateTestBlock({ testCase, file, options, trace, validateTypes = true, }) {
|
|
158
|
+
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
159
|
+
logger.log("Appending create test block");
|
|
160
|
+
const context = await (0, context_1.contextForGeneration)(file);
|
|
161
|
+
const { codePrompt, pomPrompt } = context;
|
|
162
|
+
const generatedTestCases = [];
|
|
163
|
+
// TODO: move this to a common place
|
|
164
|
+
const session = (0, session_1.getSessionDetails)();
|
|
165
|
+
trace =
|
|
166
|
+
trace ||
|
|
167
|
+
llm_1.langfuseInstance.trace({
|
|
168
|
+
name: "append-create-test-block",
|
|
169
|
+
id: crypto_1.default.randomUUID(),
|
|
170
|
+
release: session.version,
|
|
171
|
+
tags: [
|
|
172
|
+
options?.metadata.projectName || "",
|
|
173
|
+
options?.metadata.environment || "",
|
|
174
|
+
].filter((s) => !!s),
|
|
175
|
+
});
|
|
176
|
+
const promptName = "append-create-test-block";
|
|
177
|
+
const promptSpan = trace.span({
|
|
178
|
+
name: "append-create-test-block-prompt",
|
|
179
|
+
});
|
|
180
|
+
const instruction = await (0, llm_1.getPrompt)(promptName, {
|
|
181
|
+
testFiles: codePrompt,
|
|
182
|
+
pageFiles: pomPrompt,
|
|
183
|
+
scenarioName: testCase.name,
|
|
184
|
+
scenarioSteps: testCase.steps.join("\n"),
|
|
185
|
+
scenarioFile: file,
|
|
186
|
+
});
|
|
187
|
+
promptSpan.end({ output: { instruction } });
|
|
188
|
+
const llm = new llm_1.LLM({
|
|
189
|
+
trace,
|
|
190
|
+
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
191
|
+
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
192
|
+
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
193
|
+
});
|
|
194
|
+
const firstShotMessage = await llm.createChatCompletion({
|
|
195
|
+
messages: instruction,
|
|
196
|
+
modelParameters: {
|
|
197
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
198
|
+
...options?.modelParameters,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
let response = firstShotMessage?.content || "";
|
|
202
|
+
const fileChanges = (0, utils_1.extractTestUpdates)(response);
|
|
203
|
+
await applyFileChanges({
|
|
204
|
+
trace,
|
|
205
|
+
testCase,
|
|
206
|
+
fileChanges,
|
|
207
|
+
logger,
|
|
208
|
+
pomPrompt: pomPrompt,
|
|
209
|
+
codePrompt: codePrompt,
|
|
210
|
+
validateTypes,
|
|
211
|
+
});
|
|
212
|
+
logger.log(`Trace: ${trace.getTraceUrl()}`);
|
|
213
|
+
generatedTestCases.push({
|
|
214
|
+
...testCase,
|
|
215
|
+
updatedFiles: fileChanges.map((f) => f.filePath),
|
|
216
|
+
});
|
|
217
|
+
trace.update({ input: { testCase }, output: { response } });
|
|
218
|
+
await (0, llm_1.flushAllTraces)();
|
|
219
|
+
return generatedTestCases;
|
|
220
|
+
}
|
|
221
|
+
exports.appendCreateTestBlock = appendCreateTestBlock;
|
package/dist/bin/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const run_2 = require("../agent/codegen/run");
|
|
|
12
12
|
const reporter_1 = require("../reporter");
|
|
13
13
|
const ci_1 = require("../reporter/ci");
|
|
14
14
|
const session_1 = require("../session");
|
|
15
|
+
const test_build_1 = require("../test-build");
|
|
15
16
|
const logger_1 = require("./logger");
|
|
16
17
|
const utils_2 = require("./utils");
|
|
17
18
|
dotenv_1.default.config({
|
|
@@ -62,6 +63,8 @@ async function runAgent(testGenConfig) {
|
|
|
62
63
|
generationId: testGenConfig.options?.metadata.generationId,
|
|
63
64
|
});
|
|
64
65
|
try {
|
|
66
|
+
// download the build if it exists
|
|
67
|
+
await (0, test_build_1.downloadBuild)(testGenConfig.build || {});
|
|
65
68
|
await runAgent(testGenConfig);
|
|
66
69
|
}
|
|
67
70
|
catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TestGenConfig } from "../../types";
|
|
2
|
-
export declare function parseCliArgs(
|
|
2
|
+
export declare function parseCliArgs(testGenToken?: string): Promise<{
|
|
3
3
|
testGenConfig: TestGenConfig;
|
|
4
4
|
}>;
|
|
5
5
|
export declare function getTestConfigCliArg(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,wBAAsB,YAAY,CAChC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,wBAAsB,YAAY,CAChC,YAAY,GAAE,MAA8B;;GAM7C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
|
package/dist/bin/utils/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTestConfigCliArg = exports.parseCliArgs = void 0;
|
|
4
4
|
const scenarios_1 = require("./scenarios");
|
|
5
|
-
async function parseCliArgs(
|
|
6
|
-
const testGenConfig = await (0, scenarios_1.loadTestConfigs)(
|
|
5
|
+
async function parseCliArgs(testGenToken = getTestConfigCliArg()) {
|
|
6
|
+
const testGenConfig = await (0, scenarios_1.loadTestConfigs)(testGenToken);
|
|
7
7
|
return {
|
|
8
8
|
testGenConfig,
|
|
9
9
|
};
|
|
@@ -6,7 +6,11 @@ import { Node } from "ts-morph";
|
|
|
6
6
|
* @param {string} content
|
|
7
7
|
* @return { testBlock: string; parentDescribe: string; } testBlock - the test block content, testNode - the test function node
|
|
8
8
|
*/
|
|
9
|
-
export declare function getTypescriptTestBlock(scenarioName
|
|
9
|
+
export declare function getTypescriptTestBlock({ scenarioName, suites, content, }: {
|
|
10
|
+
scenarioName: string;
|
|
11
|
+
suites?: string[];
|
|
12
|
+
content: string;
|
|
13
|
+
}): {
|
|
10
14
|
testBlock: string | undefined;
|
|
11
15
|
testNode: Node | undefined;
|
|
12
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,IAAI,EAAuB,MAAM,UAAU,CAAC;AAGxE;;;;;;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;CAC5B,CAuCA;AAwBD;;;;;;;;;;;;;;;;;;;;;;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,mCAUjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAShD;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAQhD;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,+BAmBvE;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UA0CtB"}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.getTypescriptTestBlock = void 0;
|
|
7
7
|
const eslint_1 = require("eslint");
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
|
|
9
10
|
const prettier_1 = __importDefault(require("prettier"));
|
|
10
11
|
const ts_morph_1 = require("ts-morph");
|
|
11
12
|
const typescript_1 = __importDefault(require("typescript"));
|
|
@@ -16,18 +17,62 @@ const typescript_1 = __importDefault(require("typescript"));
|
|
|
16
17
|
* @param {string} content
|
|
17
18
|
* @return { testBlock: string; parentDescribe: string; } testBlock - the test block content, testNode - the test function node
|
|
18
19
|
*/
|
|
19
|
-
function getTypescriptTestBlock(scenarioName, content) {
|
|
20
|
+
function getTypescriptTestBlock({ scenarioName, suites, content, }) {
|
|
20
21
|
const project = new ts_morph_1.Project();
|
|
21
22
|
const sourceFile = project.createSourceFile("test.ts", content);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// Get all test function nodes that match the scenario name
|
|
24
|
+
const matchingTestFunctionNodes = sourceFile
|
|
25
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)
|
|
26
|
+
.filter((node) => {
|
|
27
|
+
return Boolean(node.getExpression().getText() === "test" &&
|
|
28
|
+
node.getArguments()[0]?.getText().includes(scenarioName));
|
|
29
|
+
});
|
|
30
|
+
if (!suites?.length) {
|
|
31
|
+
const firstNode = matchingTestFunctionNodes?.[0];
|
|
32
|
+
return {
|
|
33
|
+
testBlock: firstNode?.getText(),
|
|
34
|
+
testNode: firstNode,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Iterate over each test function node and check if the suites match
|
|
38
|
+
for (const testNode of matchingTestFunctionNodes) {
|
|
39
|
+
const parentDescribes = getParentDescribeNames(testNode);
|
|
40
|
+
if ((0, lodash_isequal_1.default)(parentDescribes, suites)) {
|
|
41
|
+
// Found the matching test block
|
|
42
|
+
return {
|
|
43
|
+
testBlock: testNode.getText(),
|
|
44
|
+
testNode,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// No matching test block found
|
|
25
49
|
return {
|
|
26
|
-
testBlock:
|
|
27
|
-
testNode:
|
|
50
|
+
testBlock: undefined,
|
|
51
|
+
testNode: undefined,
|
|
28
52
|
};
|
|
29
53
|
}
|
|
30
54
|
exports.getTypescriptTestBlock = getTypescriptTestBlock;
|
|
55
|
+
// get the names of parent describe blocks
|
|
56
|
+
function getParentDescribeNames(node) {
|
|
57
|
+
const names = [];
|
|
58
|
+
let current = node.getParent();
|
|
59
|
+
while (current) {
|
|
60
|
+
if (ts_morph_1.Node.isCallExpression(current)) {
|
|
61
|
+
const expr = current.getExpression();
|
|
62
|
+
if (expr.getText() === "test.describe") {
|
|
63
|
+
const describeBlockArguments = current.getArguments();
|
|
64
|
+
if (describeBlockArguments.length > 0) {
|
|
65
|
+
const describeBlockName = describeBlockArguments[0];
|
|
66
|
+
if (ts_morph_1.Node.isStringLiteral(describeBlockName)) {
|
|
67
|
+
names.push(describeBlockName.getLiteralText());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
current = current.getParent();
|
|
73
|
+
}
|
|
74
|
+
return names.reverse(); // Reverse to get from outermost to innermost
|
|
75
|
+
}
|
|
31
76
|
/**
|
|
32
77
|
* Function to find the first 'describe' block configured with 'serial: true'
|
|
33
78
|
*
|
|
@@ -124,7 +169,10 @@ exports.validateTypescript = validateTypescript;
|
|
|
124
169
|
async function stripAndPrependImports(content, testName) {
|
|
125
170
|
const importRegexp = /import\s+\{[^}]*\}\s+from\s+["'][^"']+["'];?/g;
|
|
126
171
|
const imports = content.match(importRegexp);
|
|
127
|
-
const { testBlock: strippedContent } = getTypescriptTestBlock(
|
|
172
|
+
const { testBlock: strippedContent } = getTypescriptTestBlock({
|
|
173
|
+
scenarioName: testName,
|
|
174
|
+
content,
|
|
175
|
+
});
|
|
128
176
|
const prependContent = (imports?.join("\n") || "") + "\n\n";
|
|
129
177
|
return [prependContent, strippedContent];
|
|
130
178
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { TestGenConfig } from "../../../types";
|
|
2
|
-
declare function loadTestConfigs(
|
|
2
|
+
declare function loadTestConfigs(testGenToken: string): Promise<TestGenConfig>;
|
|
3
3
|
export { loadTestConfigs };
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bin/utils/scenarios/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bin/utils/scenarios/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,aAAa,EAAwB,MAAM,gBAAgB,CAAC;AAU5E,iBAAe,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAc3E;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadTestConfigs = void 0;
|
|
4
|
-
async function loadTestConfigs(
|
|
5
|
-
|
|
6
|
-
const str = decodeURIComponent(atob(scenariosPath));
|
|
4
|
+
async function loadTestConfigs(testGenToken) {
|
|
5
|
+
const str = decodeURIComponent(atob(testGenToken));
|
|
7
6
|
const config = JSON.parse(str);
|
|
8
7
|
const specPath = `./tests/${config.group || "index"}.spec.ts`;
|
|
9
8
|
return {
|
|
@@ -13,6 +12,7 @@ async function loadTestConfigs(scenariosPath) {
|
|
|
13
12
|
steps: config.steps.filter((s) => !!s),
|
|
14
13
|
group: config.group,
|
|
15
14
|
},
|
|
15
|
+
build: config.build,
|
|
16
16
|
options: config.options,
|
|
17
17
|
};
|
|
18
18
|
}
|
package/dist/reporter/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAa5E,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAKF,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAUlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,qBAAa,sBAAsB;;IAE3B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1C,eAAe,CAAC,EACpB,eAAe,EACf,QAAQ,GACT,EAAE;QACD,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB;IA4CK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlD"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":";AACA,OAAO,EAA4B,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAa5E,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAKF,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAUlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,qBAAa,sBAAsB;;IAE3B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1C,eAAe,CAAC,EACpB,eAAe,EACf,QAAQ,GACT,EAAE;QACD,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB;IA4CK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Build } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* method to download the build from the URL provided in the build object
|
|
4
|
+
* this is only used in cases like chrome extension as of yet.
|
|
5
|
+
* @export
|
|
6
|
+
* @param {Build} build
|
|
7
|
+
* @return {*} {Promise<void>}
|
|
8
|
+
*/
|
|
9
|
+
export declare function downloadBuild(build: Build): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-build/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAOjC;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAY/D"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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.downloadBuild = void 0;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const logger_1 = require("../bin/logger");
|
|
9
|
+
const exec_1 = require("../utils/exec");
|
|
10
|
+
/**
|
|
11
|
+
* method to download the build from the URL provided in the build object
|
|
12
|
+
* this is only used in cases like chrome extension as of yet.
|
|
13
|
+
* @export
|
|
14
|
+
* @param {Build} build
|
|
15
|
+
* @return {*} {Promise<void>}
|
|
16
|
+
*/
|
|
17
|
+
async function downloadBuild(build) {
|
|
18
|
+
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
19
|
+
const packageJSONPath = "package.json";
|
|
20
|
+
const packageJsonStr = await fs_extra_1.default.readFile(packageJSONPath, "utf-8");
|
|
21
|
+
const packageJSONData = JSON.parse(packageJsonStr);
|
|
22
|
+
const buildDownloadScript = packageJSONData.scripts["download"];
|
|
23
|
+
if (buildDownloadScript && build?.url) {
|
|
24
|
+
logger.log(`Downloading build from ${build.url}`);
|
|
25
|
+
await (0, exec_1.cmd)(`npm run download ${build.url}`.split(" "), {
|
|
26
|
+
env: { ...Object(process.env) },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.downloadBuild = downloadBuild;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -19,9 +19,13 @@ export type TestGenConfigOptions = {
|
|
|
19
19
|
environment: "development" | "production";
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
+
export type Build = {
|
|
23
|
+
url?: string;
|
|
24
|
+
};
|
|
22
25
|
export type TestGenConfig = {
|
|
23
26
|
specPath: string;
|
|
24
27
|
testCase: TestCase;
|
|
28
|
+
build?: Build;
|
|
25
29
|
options?: TestGenConfigOptions;
|
|
26
30
|
};
|
|
27
31
|
export type TestCase = {
|
|
@@ -29,7 +33,9 @@ export type TestCase = {
|
|
|
29
33
|
steps: string[];
|
|
30
34
|
group: string;
|
|
31
35
|
};
|
|
32
|
-
export type PlaywrightActionGenerator = (page: Page
|
|
36
|
+
export type PlaywrightActionGenerator = (page: Page, options?: {
|
|
37
|
+
stateVariables: Record<string, any>;
|
|
38
|
+
}) => Action;
|
|
33
39
|
export type ActionSchema = OpenAI.Chat.Completions.ChatCompletionTool;
|
|
34
40
|
export type Action = {
|
|
35
41
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,aAAa,EAAE,WAAW,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,EAAE;QACR,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC;KAC3C,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,aAAa,EAAE,WAAW,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,EAAE;QACR,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC;KAC3C,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,CACtC,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE;IACR,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACrC,KACE,MAAM,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC;AAEtE,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC5E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;CAC/E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/test-gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"@actions/core": "^1.10.1",
|
|
19
19
|
"@aws-sdk/client-s3": "^3.614.0",
|
|
20
20
|
"@aws-sdk/s3-request-presigner": "^3.614.0",
|
|
21
|
+
"@google/generative-ai": "^0.15.0",
|
|
21
22
|
"@playwright/test": "^1.44.1",
|
|
22
23
|
"@types/sanitize-html": "^2.11.0",
|
|
23
24
|
"commander": "^12.1.0",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"google-auth-library": "^9.10.0",
|
|
30
31
|
"google-spreadsheet": "^4.1.2",
|
|
31
32
|
"ignore": "^5.3.1",
|
|
33
|
+
"lodash.isequal": "^4.5.0",
|
|
32
34
|
"md5": "^2.3.0",
|
|
33
35
|
"mime": "^4.0.4",
|
|
34
36
|
"minimatch": "^10.0.1",
|
|
@@ -36,19 +38,21 @@
|
|
|
36
38
|
"picocolors": "^1.0.1",
|
|
37
39
|
"playwright": "^1.44.1",
|
|
38
40
|
"prettier": "^3.2.5",
|
|
41
|
+
"remove-markdown": "^0.5.5",
|
|
39
42
|
"sanitize-html": "^2.13.0",
|
|
40
43
|
"slugify": "^1.6.6",
|
|
41
44
|
"ts-morph": "^23.0.0",
|
|
42
45
|
"tsx": "^4.16.2",
|
|
43
46
|
"typescript": "^5.3.3",
|
|
44
47
|
"@empiricalrun/llm": "^0.9.2",
|
|
45
|
-
"@empiricalrun/r2-uploader": "^0.
|
|
46
|
-
"@empiricalrun/reporter": "^0.18.
|
|
48
|
+
"@empiricalrun/r2-uploader": "^0.3.0",
|
|
49
|
+
"@empiricalrun/reporter": "^0.18.2"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
52
|
"@types/detect-port": "^1.3.5",
|
|
50
53
|
"@types/express": "^4.17.21",
|
|
51
54
|
"@types/fs-extra": "^11.0.4",
|
|
55
|
+
"@types/lodash.isequal": "^4.5.8",
|
|
52
56
|
"@types/md5": "^2.3.5"
|
|
53
57
|
},
|
|
54
58
|
"scripts": {
|
|
@@ -57,6 +61,7 @@
|
|
|
57
61
|
"clean": "tsc --build --clean",
|
|
58
62
|
"lint": "eslint .",
|
|
59
63
|
"test": "vitest run",
|
|
60
|
-
"test:watch": "vitest"
|
|
64
|
+
"test:watch": "vitest",
|
|
65
|
+
"test:watch-files": "vitest $0 --watch"
|
|
61
66
|
}
|
|
62
67
|
}
|