@empiricalrun/test-gen 0.23.14 → 0.24.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4b1a0cc: feat: support for code agent in update flow
8
+
3
9
  ## 0.23.14
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAoBA,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,CAkJrB"}
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;AAG7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CA+IrB"}
@@ -12,6 +12,7 @@ const context_1 = require("../../bin/utils/context");
12
12
  const web_1 = require("../../bin/utils/platform/web");
13
13
  const constants_1 = require("../../constants");
14
14
  const session_1 = require("../../session");
15
+ const update_flow_1 = require("./update-flow");
15
16
  async function generateTest(testCase, file, options) {
16
17
  const logger = new logger_1.CustomLogger();
17
18
  if (!fs_extra_1.default.existsSync(file)) {
@@ -20,6 +21,10 @@ async function generateTest(testCase, file, options) {
20
21
  }
21
22
  const context = await (0, context_1.contextForGeneration)(file);
22
23
  const { codePrompt, pomPrompt, testFileContent } = context;
24
+ const isUpdate = testFileContent.includes(`test("${testCase?.name}"`);
25
+ if (isUpdate) {
26
+ return await (0, update_flow_1.updateTest)(testCase, file, options);
27
+ }
23
28
  const generatedTestCases = [];
24
29
  logger.logEmptyLine();
25
30
  const session = (0, session_1.getSessionDetails)();
@@ -38,12 +43,10 @@ async function generateTest(testCase, file, options) {
38
43
  },
39
44
  });
40
45
  trace.update({ input: { testCase } });
41
- const isUpdate = testFileContent.includes(`test("${testCase?.name}"`);
42
46
  const promptSpan = trace.span({
43
- name: isUpdate ? "update-scenario-prompt" : "add-scenario-prompt",
47
+ name: "add-scenario-prompt",
44
48
  });
45
- const promptName = isUpdate ? "update-scenario" : "add-scenario";
46
- const instruction = await (0, llm_1.getPrompt)(promptName, {
49
+ const instruction = await (0, llm_1.getPrompt)("add-scenario", {
47
50
  testFiles: codePrompt,
48
51
  pageFiles: pomPrompt,
49
52
  scenarioName: testCase.name,
@@ -70,11 +73,6 @@ async function generateTest(testCase, file, options) {
70
73
  let contents = fs_extra_1.default.readFileSync(file, "utf-8");
71
74
  const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(response, testCase?.name);
72
75
  let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
73
- if (isUpdate) {
74
- const { testBlock } = (0, web_1.getTypescriptTestBlock)(testCase?.name, contents);
75
- contents = contents.replace(testBlock, `\n\n${strippedContent}`);
76
- updatedContent = prependContent + contents;
77
- }
78
76
  await fs_extra_1.default.writeFile(file, updatedContent, "utf-8");
79
77
  readWriteFileSpan.end({ output: { updatedContent } });
80
78
  logger.log("Linting generated code...");
@@ -0,0 +1,3 @@
1
+ import { TestCase, TestGenConfigOptions } from "../../types";
2
+ export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions): Promise<TestCase[]>;
3
+ //# sourceMappingURL=update-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAoB7D,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAiGrB"}
@@ -0,0 +1,115 @@
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.updateTest = void 0;
7
+ const llm_1 = require("@empiricalrun/llm");
8
+ const crypto_1 = __importDefault(require("crypto"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const logger_1 = require("../../bin/logger");
11
+ const context_1 = require("../../bin/utils/context");
12
+ const web_1 = require("../../bin/utils/platform/web");
13
+ const constants_1 = require("../../constants");
14
+ const session_1 = require("../../session");
15
+ function extractInformation(input) {
16
+ const result = [];
17
+ const regex = /<filepath>(.*?)<\/filepath.*?>[\s\S]*?<old_code_block>([\s\S]*?)<\/old_code_block>[\s\S]*?<new_code_block>([\s\S]*?)<\/new_code_block>[\s\S]*?<reason>([\s\S]*?)<\/reason>/g;
18
+ let match;
19
+ while ((match = regex.exec(input)) !== null) {
20
+ const [, filePath, oldCode, newCode, reason] = match;
21
+ result.push({
22
+ filePath: filePath?.trim(),
23
+ oldCode: oldCode?.trim(),
24
+ newCode: newCode?.trim(),
25
+ reason: reason?.trim(),
26
+ });
27
+ }
28
+ return result;
29
+ }
30
+ async function updateTest(testCase, file, options) {
31
+ const logger = new logger_1.CustomLogger();
32
+ const context = await (0, context_1.contextForGeneration)(file);
33
+ const { codePrompt, pomPrompt, testFileContent } = context;
34
+ const generatedTestCases = [];
35
+ logger.logEmptyLine();
36
+ const session = (0, session_1.getSessionDetails)();
37
+ const trace = llm_1.langfuseInstance.trace({
38
+ name: "update-test",
39
+ id: crypto_1.default.randomUUID(),
40
+ release: session.version,
41
+ tags: [options.metadata.projectName, options.metadata.environment].filter((s) => !!s),
42
+ });
43
+ trace.event({
44
+ name: "collate-files-as-text",
45
+ output: {
46
+ codePrompt,
47
+ pomPrompt,
48
+ testFileContent,
49
+ },
50
+ });
51
+ trace.update({ input: { testCase } });
52
+ const promptSpan = trace.span({
53
+ name: "update-scenario-prompt",
54
+ });
55
+ const promptName = "update-scenario";
56
+ const instruction = await (0, llm_1.getPrompt)(promptName, {
57
+ testFiles: codePrompt,
58
+ pageFiles: pomPrompt,
59
+ scenarioName: testCase.name,
60
+ scenarioSteps: testCase.steps.join("\n"),
61
+ scenarioFile: file,
62
+ }, 8);
63
+ promptSpan.end({ output: { instruction } });
64
+ const llm = new llm_1.LLM({
65
+ trace,
66
+ provider: options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
67
+ defaultModel: options.model || constants_1.DEFAULT_MODEL,
68
+ providerApiKey: constants_1.MODEL_API_KEYS[options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
69
+ });
70
+ const firstShotMessage = await llm.createChatCompletion({
71
+ messages: instruction,
72
+ modelParameters: {
73
+ ...constants_1.DEFAULT_MODEL_PARAMETERS,
74
+ ...options.modelParameters,
75
+ },
76
+ });
77
+ let response = firstShotMessage?.content || "";
78
+ logger.success("Test generated successfully!");
79
+ const fileChanges = extractInformation(response);
80
+ fileChanges.forEach(async (fileChange) => {
81
+ if (fileChange.filePath?.includes("spec.ts")) {
82
+ // assuming the test case getting updated
83
+ // maintaining the previous accuracy of the test case update
84
+ const readWriteFileSpan = trace.span({ name: "write-to-file" });
85
+ let contents = fs_extra_1.default.readFileSync(fileChange.filePath, "utf-8");
86
+ const [prependContent, strippedContent] = await (0, web_1.stripAndPrependImports)(fileChange.newCode, testCase?.name);
87
+ let updatedContent = prependContent + contents + `\n\n${strippedContent}`;
88
+ const { testBlock } = (0, web_1.getTypescriptTestBlock)(testCase?.name, contents);
89
+ contents = contents.replace(testBlock, `\n\n${strippedContent}`);
90
+ updatedContent = prependContent + contents;
91
+ await fs_extra_1.default.writeFile(file, updatedContent, "utf-8");
92
+ readWriteFileSpan.end({ output: { updatedContent } });
93
+ trace.event({ name: "format-file" });
94
+ await (0, web_1.formatCode)(fileChange.filePath);
95
+ logger.success("File formatted successfully!");
96
+ }
97
+ else {
98
+ // since we dont know what is getting updated,
99
+ // we believe that the patch is correct and contains few before and after lines to make the change unique
100
+ const readWriteFileSpan = trace.span({ name: "write-to-file" });
101
+ let contents = fs_extra_1.default.readFileSync(fileChange.filePath, "utf-8");
102
+ contents = contents.replace(fileChange.oldCode, `\n\n${fileChange.newCode}`);
103
+ await fs_extra_1.default.writeFile(fileChange.filePath, contents, "utf-8");
104
+ readWriteFileSpan.end({ output: { contents } });
105
+ trace.event({ name: "format-file" });
106
+ await (0, web_1.formatCode)(fileChange.filePath);
107
+ logger.success("File formatted successfully!");
108
+ }
109
+ });
110
+ logger.log(`Trace: ${trace.getTraceUrl()}`);
111
+ generatedTestCases.push(testCase);
112
+ trace.update({ input: { testCase }, output: { response } });
113
+ return generatedTestCases;
114
+ }
115
+ exports.updateTest = updateTest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.23.14",
3
+ "version": "0.24.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"