@empiricalrun/test-gen 0.16.13 → 0.17.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 +10 -0
- package/dist/agent/browsing/utils.d.ts +1 -1
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +5 -5
- package/dist/agent/codegen/run.d.ts +2 -2
- package/dist/agent/codegen/run.d.ts.map +1 -1
- package/dist/agent/codegen/run.js +99 -107
- package/dist/bin/index.js +18 -25
- package/dist/bin/utils/index.d.ts +1 -2
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +2 -4
- 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 +11 -106
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/reporter/ci.d.ts +2 -2
- package/dist/reporter/ci.d.ts.map +1 -1
- package/dist/reporter/ci.js +4 -9
- package/dist/types/index.d.ts +2 -3
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@ import { Page } from "playwright";
|
|
|
2
2
|
import { PlaywrightTestConfig } from "playwright/test";
|
|
3
3
|
import { TestGenConfig } from "../../types";
|
|
4
4
|
export declare function isRegExp(obj: any): obj is RegExp;
|
|
5
|
-
export declare function prepareBrowsingAgentTask(steps: string[]
|
|
5
|
+
export declare function prepareBrowsingAgentTask(steps: string[]): string;
|
|
6
6
|
export declare function prepareFileForBrowsingAgent(genConfig: TestGenConfig): Promise<void>;
|
|
7
7
|
export declare function injectPwLocatorGenerator(page: Page): Promise<void>;
|
|
8
8
|
export declare function canRunBrowsingAgent(filePath: string): void;
|
|
@@ -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;AASvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,
|
|
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;AASvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAED,wBAAsB,2BAA2B,CAAC,SAAS,EAAE,aAAa,iBAqCzE;AAiBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBASxD;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,QA4BnD;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAM1E;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,CAAC,CAkDjB;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,YAAiB,EACjB,IAAS,EACT,eAAoB,EACpB,gBAAqB,GACtB,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,8EASA"}
|
|
@@ -16,15 +16,15 @@ function isRegExp(obj) {
|
|
|
16
16
|
Object.prototype.toString.call(obj) === "[object RegExp]");
|
|
17
17
|
}
|
|
18
18
|
exports.isRegExp = isRegExp;
|
|
19
|
-
function prepareBrowsingAgentTask(steps
|
|
19
|
+
function prepareBrowsingAgentTask(steps) {
|
|
20
20
|
const sanitizedSteps = steps.map((step) => step.replace(/`/g, "\\`"));
|
|
21
|
-
const task = `${sanitizedSteps.join("\n")}\n
|
|
21
|
+
const task = `${sanitizedSteps.join("\n")}\n`;
|
|
22
22
|
return task;
|
|
23
23
|
}
|
|
24
24
|
exports.prepareBrowsingAgentTask = prepareBrowsingAgentTask;
|
|
25
25
|
async function prepareFileForBrowsingAgent(genConfig) {
|
|
26
|
-
const { specPath,
|
|
27
|
-
const { name, steps
|
|
26
|
+
const { specPath, testCase } = genConfig;
|
|
27
|
+
const { name, steps } = testCase;
|
|
28
28
|
const logger = new logger_1.CustomLogger();
|
|
29
29
|
if (!fs_extra_1.default.existsSync(specPath)) {
|
|
30
30
|
await fs_extra_1.default.createFile(specPath);
|
|
@@ -33,7 +33,7 @@ async function prepareFileForBrowsingAgent(genConfig) {
|
|
|
33
33
|
if (name && steps && steps.length) {
|
|
34
34
|
const existingContents = await fs_extra_1.default.readFile(specPath, "utf-8");
|
|
35
35
|
const testBlock = (0, web_1.getTypescriptTestBlock)(name, existingContents);
|
|
36
|
-
const mergedSteps = prepareBrowsingAgentTask(steps
|
|
36
|
+
const mergedSteps = prepareBrowsingAgentTask(steps);
|
|
37
37
|
let newContents = existingContents;
|
|
38
38
|
if (testBlock) {
|
|
39
39
|
logger.log("appending to existing test block");
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function generateTest(
|
|
1
|
+
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
2
|
+
export declare function generateTest(testCase: TestCase, file: string, options: TestGenConfigOptions): Promise<TestCase[]>;
|
|
3
3
|
//# sourceMappingURL=run.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,wBAAsB,YAAY,CAChC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CA+IrB"}
|
|
@@ -11,7 +11,7 @@ const context_1 = require("../../bin/utils/context");
|
|
|
11
11
|
const web_1 = require("../../bin/utils/platform/web");
|
|
12
12
|
const constants_1 = require("../../constants");
|
|
13
13
|
const session_1 = require("../../session");
|
|
14
|
-
async function generateTest(
|
|
14
|
+
async function generateTest(testCase, file, options) {
|
|
15
15
|
const logger = new logger_1.CustomLogger();
|
|
16
16
|
if (!fs_extra_1.default.existsSync(file)) {
|
|
17
17
|
logger.log(`Creating a new spec file: ${file}`);
|
|
@@ -19,42 +19,97 @@ async function generateTest(scenarios, file, isUpdate, options) {
|
|
|
19
19
|
}
|
|
20
20
|
const context = await (0, context_1.contextForGeneration)(file);
|
|
21
21
|
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
const generatedTestCases = [];
|
|
23
|
+
logger.logEmptyLine();
|
|
24
|
+
const trace = new llm_1.LLMTracing({
|
|
25
|
+
name: "generate-test",
|
|
26
|
+
sessionDetails: (0, session_1.getSessionDetails)(),
|
|
27
|
+
tags: [options.metadata.projectName, options.metadata.environment].filter((s) => !!s),
|
|
28
|
+
});
|
|
29
|
+
trace.event({
|
|
30
|
+
name: "collate-files-as-text",
|
|
31
|
+
output: {
|
|
32
|
+
codePrompt,
|
|
33
|
+
pomPrompt,
|
|
34
|
+
testFileContent,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
trace.update({ input: { testCase } });
|
|
38
|
+
logger.log("Generating test for scenario:", testCase?.name);
|
|
39
|
+
const isUpdate = testFileContent.includes(`test("${testCase?.name}"`);
|
|
40
|
+
const promptSpan = trace.startSpan(isUpdate ? "update-scenario-prompt" : "add-scenario-prompt");
|
|
41
|
+
const promptName = isUpdate ? "update-scenario" : "add-scenario";
|
|
42
|
+
const instruction = await (0, llm_1.getPrompt)(promptName, {
|
|
43
|
+
testFiles: codePrompt,
|
|
44
|
+
pageFiles: pomPrompt,
|
|
45
|
+
scenarioName: testCase.name,
|
|
46
|
+
scenarioSteps: testCase.steps.join("\n"),
|
|
47
|
+
scenarioFile: file,
|
|
48
|
+
});
|
|
49
|
+
promptSpan.end({ output: { instruction } });
|
|
50
|
+
const firstShotMessage = await (0, llm_1.getLLMResult)({
|
|
51
|
+
messages: instruction,
|
|
52
|
+
trace,
|
|
53
|
+
model: options.model || constants_1.DEFAULT_MODEL,
|
|
54
|
+
provider: options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
55
|
+
providerApiKey: constants_1.MODEL_API_KEYS[options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
56
|
+
modelParameters: {
|
|
57
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
58
|
+
...options.modelParameters,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
let response = firstShotMessage?.content || "";
|
|
62
|
+
logger.success("Test generated successfully!");
|
|
63
|
+
const readWriteFileSpan = trace.startSpan("write-to-file");
|
|
64
|
+
let contents = fs_extra_1.default.readFileSync(file, "utf-8");
|
|
65
|
+
const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(response);
|
|
66
|
+
let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
|
|
67
|
+
if (isUpdate) {
|
|
68
|
+
const testBlock = (0, web_1.getTypescriptTestBlock)(testCase?.name, contents);
|
|
69
|
+
contents = contents.replace(testBlock, `\n\n${strippedContent}`);
|
|
70
|
+
updatedContent = prependContent + contents;
|
|
71
|
+
}
|
|
72
|
+
await fs_extra_1.default.writeFile(file, updatedContent, "utf-8");
|
|
73
|
+
readWriteFileSpan.end({ output: { updatedContent } });
|
|
74
|
+
logger.log("Linting generated code...");
|
|
75
|
+
trace.event({ name: "lint-file" });
|
|
76
|
+
await (0, web_1.lintErrors)(file);
|
|
77
|
+
const validateTypesSpan = trace.startSpan("detect-type-errors-in-file");
|
|
78
|
+
logger.log("Validating types...");
|
|
79
|
+
let errors = (0, web_1.validateTypescript)(file);
|
|
80
|
+
validateTypesSpan.end({ output: { errors } });
|
|
81
|
+
if (!errors.length) {
|
|
82
|
+
logger.success("Found no type issues!");
|
|
83
|
+
}
|
|
84
|
+
const maxIteration = 2;
|
|
85
|
+
let counter = 0;
|
|
86
|
+
while (errors.length > 0) {
|
|
87
|
+
const fileContent = fs_extra_1.default.readFileSync(file, "utf-8");
|
|
88
|
+
counter += 1;
|
|
89
|
+
if (counter > maxIteration) {
|
|
90
|
+
trace.event({ name: "code-fix-iteration-max-out" });
|
|
91
|
+
logger.error([
|
|
92
|
+
`Unable to fix typescript errors. Please review ${file} manually and fix the typescript errors.`,
|
|
93
|
+
`Run the test-gen command again, once errors are fixed.`,
|
|
94
|
+
`Trace: ${trace.url}`,
|
|
95
|
+
].join("\n"));
|
|
96
|
+
break;
|
|
45
97
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
trace.event({ name: "Found errors fixing" });
|
|
99
|
+
logger.warn("Found few errors while validating types:");
|
|
100
|
+
errors.forEach((e) => logger.warn(e));
|
|
101
|
+
logger.log("Trying to fix above errors...");
|
|
102
|
+
const promptSpan = trace.startSpan("fix-type-errors-prompt");
|
|
103
|
+
const instruction = await (0, llm_1.getPrompt)("fix-file-errors-ts", {
|
|
104
|
+
testFiles: codePrompt || "",
|
|
105
|
+
pageFiles: pomPrompt || "",
|
|
54
106
|
scenarioFile: file,
|
|
107
|
+
errors: errors,
|
|
108
|
+
fileContent: fileContent,
|
|
109
|
+
scenaioName: testCase.name,
|
|
55
110
|
});
|
|
56
111
|
promptSpan.end({ output: { instruction } });
|
|
57
|
-
const
|
|
112
|
+
const message = await (0, llm_1.getLLMResult)({
|
|
58
113
|
messages: instruction,
|
|
59
114
|
trace,
|
|
60
115
|
model: options.model || constants_1.DEFAULT_MODEL,
|
|
@@ -65,88 +120,25 @@ async function generateTest(scenarios, file, isUpdate, options) {
|
|
|
65
120
|
...options.modelParameters,
|
|
66
121
|
},
|
|
67
122
|
});
|
|
68
|
-
|
|
69
|
-
logger.success("Test generated successfully!");
|
|
123
|
+
response = message?.content || "";
|
|
70
124
|
const readWriteFileSpan = trace.startSpan("write-to-file");
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
|
|
74
|
-
if (isUpdate) {
|
|
75
|
-
const testBlock = (0, web_1.getTypescriptTestBlock)(scenario?.name, contents);
|
|
76
|
-
contents = contents.replace(testBlock, `\n\n${strippedContent}`);
|
|
77
|
-
updatedContent = prependContent + contents;
|
|
78
|
-
}
|
|
79
|
-
await fs_extra_1.default.writeFile(file, updatedContent, "utf-8");
|
|
80
|
-
readWriteFileSpan.end({ output: { updatedContent } });
|
|
81
|
-
logger.log("Linting generated code...");
|
|
125
|
+
await fs_extra_1.default.writeFile(file, response, "utf-8");
|
|
126
|
+
readWriteFileSpan.end({ output: { response } });
|
|
82
127
|
trace.event({ name: "lint-file" });
|
|
83
128
|
await (0, web_1.lintErrors)(file);
|
|
84
129
|
const validateTypesSpan = trace.startSpan("detect-type-errors-in-file");
|
|
85
|
-
|
|
86
|
-
let errors = (0, web_1.validateTypescript)(file);
|
|
130
|
+
errors = (0, web_1.validateTypescript)(file);
|
|
87
131
|
validateTypesSpan.end({ output: { errors } });
|
|
88
132
|
if (!errors.length) {
|
|
89
133
|
logger.success("Found no type issues!");
|
|
90
134
|
}
|
|
91
|
-
const maxIteration = 2;
|
|
92
|
-
let counter = 0;
|
|
93
|
-
while (errors.length > 0) {
|
|
94
|
-
const fileContent = fs_extra_1.default.readFileSync(file, "utf-8");
|
|
95
|
-
counter += 1;
|
|
96
|
-
if (counter > maxIteration) {
|
|
97
|
-
trace.event({ name: "code-fix-iteration-max-out" });
|
|
98
|
-
logger.error([
|
|
99
|
-
`Unable to fix typescript errors. Please review ${file} manually and fix the typescript errors.`,
|
|
100
|
-
`Run the test-gen command again, once errors are fixed.`,
|
|
101
|
-
`Trace: ${trace.url}`,
|
|
102
|
-
].join("\n"));
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
trace.event({ name: "Found errors fixing" });
|
|
106
|
-
logger.warn("Found few errors while validating types:");
|
|
107
|
-
errors.forEach((e) => logger.warn(e));
|
|
108
|
-
logger.log("Trying to fix above errors...");
|
|
109
|
-
const promptSpan = trace.startSpan("fix-type-errors-prompt");
|
|
110
|
-
const instruction = await (0, llm_1.getPrompt)("fix-file-errors-ts", {
|
|
111
|
-
testFiles: codePrompt || "",
|
|
112
|
-
pageFiles: pomPrompt || "",
|
|
113
|
-
scenarioFile: file,
|
|
114
|
-
errors: errors,
|
|
115
|
-
fileContent: fileContent,
|
|
116
|
-
scenaioName: scenario.name,
|
|
117
|
-
});
|
|
118
|
-
promptSpan.end({ output: { instruction } });
|
|
119
|
-
const message = await (0, llm_1.getLLMResult)({
|
|
120
|
-
messages: instruction,
|
|
121
|
-
trace,
|
|
122
|
-
model: options.model || constants_1.DEFAULT_MODEL,
|
|
123
|
-
provider: options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
124
|
-
providerApiKey: constants_1.MODEL_API_KEYS[options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
125
|
-
modelParameters: {
|
|
126
|
-
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
127
|
-
...options.modelParameters,
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
response = message?.content || "";
|
|
131
|
-
const readWriteFileSpan = trace.startSpan("write-to-file");
|
|
132
|
-
await fs_extra_1.default.writeFile(file, response, "utf-8");
|
|
133
|
-
readWriteFileSpan.end({ output: { response } });
|
|
134
|
-
trace.event({ name: "lint-file" });
|
|
135
|
-
await (0, web_1.lintErrors)(file);
|
|
136
|
-
const validateTypesSpan = trace.startSpan("detect-type-errors-in-file");
|
|
137
|
-
errors = (0, web_1.validateTypescript)(file);
|
|
138
|
-
validateTypesSpan.end({ output: { errors } });
|
|
139
|
-
if (!errors.length) {
|
|
140
|
-
logger.success("Found no type issues!");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
trace.event({ name: "format-file" });
|
|
144
|
-
await (0, web_1.formatCode)(file);
|
|
145
|
-
logger.success("File formatted successfully!");
|
|
146
|
-
logger.log(`Trace: ${trace.url}`);
|
|
147
|
-
generatedScenarios.push(scenario);
|
|
148
|
-
trace.update({ input: { scenario }, output: { response } });
|
|
149
135
|
}
|
|
150
|
-
|
|
136
|
+
trace.event({ name: "format-file" });
|
|
137
|
+
await (0, web_1.formatCode)(file);
|
|
138
|
+
logger.success("File formatted successfully!");
|
|
139
|
+
logger.log(`Trace: ${trace.url}`);
|
|
140
|
+
generatedTestCases.push(testCase);
|
|
141
|
+
trace.update({ input: { testCase }, output: { response } });
|
|
142
|
+
return generatedTestCases;
|
|
151
143
|
}
|
|
152
144
|
exports.generateTest = generateTest;
|
package/dist/bin/index.js
CHANGED
|
@@ -19,29 +19,22 @@ dotenv_1.default.config({
|
|
|
19
19
|
process.on("exit", async () => await (0, llm_1.flushAllTraces)());
|
|
20
20
|
process.on("SIGINT", async () => await (0, llm_1.flushAllTraces)());
|
|
21
21
|
process.on("SIGTERM", async () => await (0, llm_1.flushAllTraces)());
|
|
22
|
-
async function runAgent(sourceFile,
|
|
22
|
+
async function runAgent(sourceFile, testGenConfig) {
|
|
23
23
|
const logger = new logger_1.CustomLogger();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
logger.success("Generating test using coding agent");
|
|
40
|
-
const gen = await (0, run_2.generateTest)(scenarios, specPath, isUpdate, testGenConfig.options);
|
|
41
|
-
generatedTestScenarios.push(...gen);
|
|
42
|
-
}
|
|
24
|
+
const { specPath, testCase } = testGenConfig;
|
|
25
|
+
if (testGenConfig.options?.agent !== "code") {
|
|
26
|
+
// this assumes we have only one scenario in test config
|
|
27
|
+
logger.success("Generating test using browsing agent");
|
|
28
|
+
await (0, utils_1.prepareFileForBrowsingAgent)(testGenConfig);
|
|
29
|
+
await (0, run_1.generateTestsUsingBrowsingAgent)(specPath);
|
|
30
|
+
await (0, reporter_1.reportTestGenVideos)({
|
|
31
|
+
projectRepoName: testGenConfig.options.metadata.projectRepoName,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
logger.success("Generating test using coding agent");
|
|
36
|
+
await (0, run_2.generateTest)(testCase, specPath, testGenConfig.options);
|
|
43
37
|
}
|
|
44
|
-
return generatedTestScenarios;
|
|
45
38
|
}
|
|
46
39
|
(async function main() {
|
|
47
40
|
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
@@ -49,10 +42,10 @@ async function runAgent(sourceFile, isUpdate, testGenConfigs) {
|
|
|
49
42
|
logger.error("Please provide path to scenarios using command:", "npx @empiricalrun/test-gen <SCENARIOS_FILE_PATH> -u");
|
|
50
43
|
process.exit(1);
|
|
51
44
|
}
|
|
52
|
-
const { sourceFile,
|
|
53
|
-
(0, reporter_1.setReporterConfig)(
|
|
54
|
-
|
|
45
|
+
const { sourceFile, testGenConfig } = await (0, utils_2.parseCliArgs)();
|
|
46
|
+
(0, reporter_1.setReporterConfig)(testGenConfig?.options?.metadata);
|
|
47
|
+
await runAgent(sourceFile, testGenConfig);
|
|
55
48
|
// TODO: move these reporters to a better lifecycle
|
|
56
|
-
await (0, ci_1.reportOnCI)(
|
|
49
|
+
await (0, ci_1.reportOnCI)(testGenConfig.testCase);
|
|
57
50
|
process.exit(0);
|
|
58
51
|
})();
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { TestGenConfig } from "../../types";
|
|
2
2
|
export declare function parseCliArgs(scenarioOrScenariosPath?: string): Promise<{
|
|
3
3
|
sourceFile: string;
|
|
4
|
-
|
|
5
|
-
isUpdate: boolean;
|
|
4
|
+
testGenConfig: TestGenConfig;
|
|
6
5
|
}>;
|
|
7
6
|
export declare function getTestConfigCliArg(): string;
|
|
8
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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,uBAAuB,GAAE,MAA8B
|
|
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,uBAAuB,GAAE,MAA8B;;;GASxD;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C"}
|
package/dist/bin/utils/index.js
CHANGED
|
@@ -3,12 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getTestConfigCliArg = exports.parseCliArgs = void 0;
|
|
4
4
|
const scenarios_1 = require("./scenarios");
|
|
5
5
|
async function parseCliArgs(scenarioOrScenariosPath = getTestConfigCliArg()) {
|
|
6
|
-
const
|
|
7
|
-
const testGenConfigs = await (0, scenarios_1.loadTestConfigs)(scenarioOrScenariosPath);
|
|
6
|
+
const testGenConfig = await (0, scenarios_1.loadTestConfigs)(scenarioOrScenariosPath);
|
|
8
7
|
return {
|
|
9
8
|
sourceFile: scenarioOrScenariosPath,
|
|
10
|
-
|
|
11
|
-
isUpdate,
|
|
9
|
+
testGenConfig,
|
|
12
10
|
};
|
|
13
11
|
}
|
|
14
12
|
exports.parseCliArgs = parseCliArgs;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { TestGenConfig } from "../../../types";
|
|
2
|
-
declare function loadTestConfigs(scenariosPath: string): Promise<TestGenConfig
|
|
2
|
+
declare function loadTestConfigs(scenariosPath: 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":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bin/utils/scenarios/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAwB,MAAM,gBAAgB,CAAC;AAUrE,iBAAe,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAY5E;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -1,112 +1,17 @@
|
|
|
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.loadTestConfigs = void 0;
|
|
7
|
-
const google_auth_library_1 = require("google-auth-library");
|
|
8
|
-
const slugify_1 = __importDefault(require("slugify"));
|
|
9
|
-
function isValidJSON(str) {
|
|
10
|
-
try {
|
|
11
|
-
JSON.parse(str);
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
catch (e) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Method to update / add scenarios to the repo.
|
|
20
|
-
* @param path
|
|
21
|
-
* @returns updated paths of scenarios
|
|
22
|
-
*/
|
|
23
|
-
async function loadScenariosFromGsheet(path) {
|
|
24
|
-
const { GoogleSpreadsheet } = await import("google-spreadsheet");
|
|
25
|
-
const url = new URL(path);
|
|
26
|
-
const docId = url.pathname.split("/")[3];
|
|
27
|
-
const searchParams = new URLSearchParams(url.hash.split("#")[1]);
|
|
28
|
-
const sheetId = Number(searchParams.get("gid")) || 0;
|
|
29
|
-
// TODO: use oauth 2
|
|
30
|
-
const serviceAccountAuth = new google_auth_library_1.JWT({
|
|
31
|
-
email: process.env.GOOGLE_SERVICE_EMAIL,
|
|
32
|
-
key: Buffer.from(process.env.GOOGLE_SERVICE_EMAIL_PRIVATE_KEY, "base64").toString(),
|
|
33
|
-
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
|
|
34
|
-
});
|
|
35
|
-
const doc = new GoogleSpreadsheet(docId, serviceAccountAuth);
|
|
36
|
-
await doc.loadInfo();
|
|
37
|
-
const sheet = doc.sheetsById[sheetId];
|
|
38
|
-
const rows = await sheet.getRows();
|
|
39
|
-
const map = new Map();
|
|
40
|
-
rows.forEach((r) => {
|
|
41
|
-
// TODO: fix for case insensitive
|
|
42
|
-
const category = r.get("Category");
|
|
43
|
-
const name = r.get("Scenario");
|
|
44
|
-
const steps = r
|
|
45
|
-
.get("Steps")
|
|
46
|
-
.split("\n")
|
|
47
|
-
.map((s) => s.trim())
|
|
48
|
-
.filter((s) => !!s.length);
|
|
49
|
-
const assert = r.get("Assert");
|
|
50
|
-
const specPath = category
|
|
51
|
-
? `./tests/${category}.spec.ts`
|
|
52
|
-
: `./tests/${(0, slugify_1.default)(name)}.spec.ts`;
|
|
53
|
-
const scenario = {
|
|
54
|
-
steps,
|
|
55
|
-
name,
|
|
56
|
-
assert,
|
|
57
|
-
};
|
|
58
|
-
if (!map.get(specPath)) {
|
|
59
|
-
map.set(specPath, [scenario]);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
const scenarios = map.get(specPath);
|
|
63
|
-
scenarios.push(scenario);
|
|
64
|
-
map.set(specPath, scenarios);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
const results = [];
|
|
68
|
-
for (const [specPath, scenarios] of map.entries()) {
|
|
69
|
-
results.push({
|
|
70
|
-
specPath,
|
|
71
|
-
scenarios,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return results;
|
|
75
|
-
}
|
|
76
4
|
async function loadTestConfigs(scenariosPath) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
];
|
|
90
|
-
// api flow
|
|
91
|
-
}
|
|
92
|
-
else if (isValidJSON(atob(scenariosPath))) {
|
|
93
|
-
const str = atob(scenariosPath);
|
|
94
|
-
const config = JSON.parse(str);
|
|
95
|
-
const specPath = `./tests/${config.group || "index"}.spec.ts`;
|
|
96
|
-
return [
|
|
97
|
-
{
|
|
98
|
-
specPath,
|
|
99
|
-
scenarios: [
|
|
100
|
-
{
|
|
101
|
-
name: config.name,
|
|
102
|
-
steps: config.steps.filter((s) => !!s),
|
|
103
|
-
assert: config.assert,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
options: config.options,
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
}
|
|
110
|
-
throw Error("Invalid path for test scenarios");
|
|
5
|
+
const str = atob(scenariosPath);
|
|
6
|
+
const config = JSON.parse(str);
|
|
7
|
+
const specPath = `./tests/${config.group || "index"}.spec.ts`;
|
|
8
|
+
return {
|
|
9
|
+
specPath,
|
|
10
|
+
testCase: {
|
|
11
|
+
name: config.name,
|
|
12
|
+
steps: config.steps.filter((s) => !!s),
|
|
13
|
+
},
|
|
14
|
+
options: config.options,
|
|
15
|
+
};
|
|
111
16
|
}
|
|
112
17
|
exports.loadTestConfigs = loadTestConfigs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"AAMA,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,IAAI,CAAa;gBACb,EAAE,IAAI,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAGtC,WAAW,CAAC,QAAQ,EAAE,MAAM;IAGtB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;CAkC1C;AAED,wBAAsB,gBAAgB,kBAAK"}
|
package/dist/file/server.js
CHANGED
|
@@ -8,7 +8,6 @@ const express_1 = __importDefault(require("express"));
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const web_1 = require("../bin/utils/platform/web");
|
|
11
|
-
const string_1 = require("../utils/string");
|
|
12
11
|
class FileService {
|
|
13
12
|
filePath = "";
|
|
14
13
|
port = 0;
|
|
@@ -21,17 +20,16 @@ class FileService {
|
|
|
21
20
|
async startFileService() {
|
|
22
21
|
const app = (0, express_1.default)();
|
|
23
22
|
app.use(express_1.default.json());
|
|
24
|
-
app.post("/test", (req, res) => {
|
|
25
|
-
const { generatedCode
|
|
23
|
+
app.post("/test", async (req, res) => {
|
|
24
|
+
const { generatedCode } = req.body;
|
|
26
25
|
try {
|
|
27
26
|
const testFilePath = path_1.default.resolve(process.cwd(), this.filePath);
|
|
28
27
|
if (testFilePath) {
|
|
29
28
|
const testFile = fs_1.default.readFileSync(testFilePath, "utf-8");
|
|
30
|
-
const
|
|
31
|
-
const updatedTestFile = testFile.replace(/await createTest\([\s\S]*?\);\n/, jsComments + "\n" + generatedCode);
|
|
29
|
+
const updatedTestFile = testFile.replace(/await createTest\([\s\S]*?\);\n/, "\n" + generatedCode);
|
|
32
30
|
const importStatement = `import { test, expect } from "@playwright/test";`;
|
|
33
31
|
fs_1.default.writeFileSync(testFilePath, importStatement + "\n" + updatedTestFile, "utf-8");
|
|
34
|
-
(0, web_1.lintErrors)(testFilePath);
|
|
32
|
+
await (0, web_1.lintErrors)(testFilePath);
|
|
35
33
|
return res.send({ success: true });
|
|
36
34
|
}
|
|
37
35
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAWlC,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAWlC,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,iBAiBnE"}
|
package/dist/index.js
CHANGED
|
@@ -15,8 +15,7 @@ process.on("SIGTERM", async () => await (0, llm_1.flushAllTraces)());
|
|
|
15
15
|
async function createTest(task, page, test) {
|
|
16
16
|
const port = process.env.APP_PORT || 3030;
|
|
17
17
|
const testConfigArg = process.env.TEST_GEN_TOKEN;
|
|
18
|
-
const {
|
|
19
|
-
const [testGenConfig] = testGenConfigs;
|
|
18
|
+
const { testGenConfig } = await (0, utils_1.parseCliArgs)(testConfigArg);
|
|
20
19
|
(0, reporter_1.setReporterConfig)(testGenConfig.options?.metadata);
|
|
21
20
|
const fileService = new client_1.default(Number(port));
|
|
22
21
|
test.setTimeout(900000);
|
package/dist/reporter/ci.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function reportOnCI(
|
|
1
|
+
import { TestCase } from "../types";
|
|
2
|
+
export declare function reportOnCI(testCase: TestCase): Promise<TestCase>;
|
|
3
3
|
//# sourceMappingURL=ci.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../src/reporter/ci.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAsB,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../src/reporter/ci.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOtE"}
|
package/dist/reporter/ci.js
CHANGED
|
@@ -25,17 +25,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.reportOnCI = void 0;
|
|
27
27
|
const core = __importStar(require("@actions/core"));
|
|
28
|
-
async function reportOnCI(
|
|
28
|
+
async function reportOnCI(testCase) {
|
|
29
29
|
if ("true") {
|
|
30
|
-
const
|
|
31
|
-
const scenariosOutput = scenarios
|
|
32
|
-
.map((s) => {
|
|
33
|
-
return `**Scenario:** ${s.name} \n\n**Steps:**\n - ${s.steps.join("\n - ")}`;
|
|
34
|
-
})
|
|
35
|
-
.join("\n ----- \n");
|
|
30
|
+
const scenariosOutput = `**Scenario:** ${testCase.name} \n\n**Steps:**\n - ${testCase.steps.join("\n - ")}`;
|
|
36
31
|
core.setOutput("summary", scenariosOutput);
|
|
37
|
-
core.setOutput("test_names",
|
|
32
|
+
core.setOutput("test_names", testCase.name);
|
|
38
33
|
}
|
|
39
|
-
return
|
|
34
|
+
return testCase;
|
|
40
35
|
}
|
|
41
36
|
exports.reportOnCI = reportOnCI;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -20,13 +20,12 @@ export type TestGenConfigOptions = {
|
|
|
20
20
|
};
|
|
21
21
|
export type TestGenConfig = {
|
|
22
22
|
specPath: string;
|
|
23
|
-
|
|
23
|
+
testCase: TestCase;
|
|
24
24
|
options?: TestGenConfigOptions;
|
|
25
25
|
};
|
|
26
|
-
export type
|
|
26
|
+
export type TestCase = {
|
|
27
27
|
name: string;
|
|
28
28
|
steps: string[];
|
|
29
|
-
assert: string;
|
|
30
29
|
};
|
|
31
30
|
export type PlaywrightActionGenerator = (page: Page) => Action;
|
|
32
31
|
export type ActionSchema = OpenAI.Chat.Completions.ChatCompletionTool;
|
|
@@ -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,SAAS,CAAC;IAC1B,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,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,
|
|
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,SAAS,CAAC;IAC1B,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,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;CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;AAE/D,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"}
|