@empiricalrun/test-gen 0.27.6 → 0.27.8

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/actions/assert.js +1 -1
  3. package/dist/actions/click.js +1 -1
  4. package/dist/actions/fill.js +1 -1
  5. package/dist/actions/goto.d.ts.map +1 -1
  6. package/dist/actions/goto.js +2 -1
  7. package/dist/actions/reload-page.d.ts.map +1 -1
  8. package/dist/actions/reload-page.js +2 -1
  9. package/dist/actions/utils/index.d.ts +1 -0
  10. package/dist/actions/utils/index.d.ts.map +1 -1
  11. package/dist/actions/utils/index.js +13 -2
  12. package/dist/agent/browsing/run.d.ts.map +1 -1
  13. package/dist/agent/browsing/run.js +2 -0
  14. package/dist/agent/browsing/utils.d.ts.map +1 -1
  15. package/dist/agent/browsing/utils.js +13 -10
  16. package/dist/agent/codegen/fix-ts-errors.d.ts +13 -0
  17. package/dist/agent/codegen/fix-ts-errors.d.ts.map +1 -0
  18. package/dist/agent/codegen/fix-ts-errors.js +76 -0
  19. package/dist/agent/codegen/run.d.ts.map +1 -1
  20. package/dist/agent/codegen/run.js +10 -57
  21. package/dist/agent/codegen/update-flow.d.ts +1 -1
  22. package/dist/agent/codegen/update-flow.d.ts.map +1 -1
  23. package/dist/agent/codegen/update-flow.js +20 -27
  24. package/dist/agent/codegen/utils.d.ts +19 -0
  25. package/dist/agent/codegen/utils.d.ts.map +1 -0
  26. package/dist/agent/codegen/utils.js +36 -0
  27. package/dist/agent/master/run.d.ts.map +1 -1
  28. package/dist/agent/master/run.js +3 -2
  29. package/dist/bin/index.js +6 -1
  30. package/dist/bin/utils/platform/web/index.d.ts +1 -0
  31. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  32. package/dist/bin/utils/platform/web/index.js +17 -11
  33. package/dist/reporter/index.d.ts.map +1 -1
  34. package/dist/reporter/index.js +23 -11
  35. package/dist/uploader/index.d.ts +1 -0
  36. package/dist/uploader/index.d.ts.map +1 -1
  37. package/dist/uploader/index.js +17 -23
  38. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.27.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 849ec6a: fix: trace and video not playing after generation
8
+
9
+ ## 0.27.7
10
+
11
+ ### Patch Changes
12
+
13
+ - ae6e28a: fix: handle different image input for different llms and handle escape characters in llm response
14
+ - cf0a651: fix: master agent should generate code with correct page variable name
15
+ - b1825cf: fix: handle typescript errors and add tests
16
+ - Updated dependencies [ae6e28a]
17
+ - @empiricalrun/llm@0.9.2
18
+
3
19
  ## 0.27.6
4
20
 
5
21
  ### Patch Changes
@@ -16,7 +16,7 @@ const assertTextVisibilityActionGenerator = (page) => {
16
16
  },
17
17
  // TODO: args transformer to be kept at a single place
18
18
  template: (args, options) => {
19
- return `await expect(page.${options.locator}).toBeVisible();`;
19
+ return `await expect(${(0, utils_1.getPageVarName)()}.${options.locator}).toBeVisible();`;
20
20
  },
21
21
  name: exports.PLAYWRIGHT_ASSERT_TEXT_VISIBILITY_ACTION_NAME,
22
22
  schema: {
@@ -18,7 +18,7 @@ const clickActionGenerator = (page) => {
18
18
  },
19
19
  // TODO: args transformer to be kept at a single place
20
20
  template: (args, options) => {
21
- return `await page.${options.locator}.click();`;
21
+ return `await ${(0, utils_1.getPageVarName)()}.${options.locator}.click();`;
22
22
  },
23
23
  name: exports.PLAYWRIGHT_CLICK_ACTION_NAME,
24
24
  schema: {
@@ -17,7 +17,7 @@ const fillActionGenerator = (page) => {
17
17
  },
18
18
  // TODO: args transformer to be kept at a single place
19
19
  template: (args, options) => {
20
- return `await page.${options.locator}.fill("${args.text}");`;
20
+ return `await ${(0, utils_1.getPageVarName)()}.${options.locator}.fill("${args.text}");`;
21
21
  },
22
22
  name: exports.PLAYWRIGHT_FILL_ACTION_NAME,
23
23
  schema: {
@@ -1 +1 @@
1
- {"version":3,"file":"goto.d.ts","sourceRoot":"","sources":["../../src/actions/goto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGrD,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD,eAAO,MAAM,mBAAmB,EAAE,yBAqCjC,CAAC"}
1
+ {"version":3,"file":"goto.d.ts","sourceRoot":"","sources":["../../src/actions/goto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD,eAAO,MAAM,mBAAmB,EAAE,yBAqCjC,CAAC"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.gotoActionGenerator = exports.PLAYWRIGHT_GOTO_ACTION_NAME = void 0;
4
4
  const utils_1 = require("../agent/browsing/utils");
5
5
  const constants_1 = require("./constants");
6
+ const utils_2 = require("./utils");
6
7
  exports.PLAYWRIGHT_GOTO_ACTION_NAME = "page_goto";
7
8
  const gotoActionGenerator = (page) => {
8
9
  return {
@@ -15,7 +16,7 @@ const gotoActionGenerator = (page) => {
15
16
  // TODO: args transformer to be kept at a single place
16
17
  template: (args) => {
17
18
  const url = args.url;
18
- const templ = `await page.goto("${url}");`;
19
+ const templ = `await ${(0, utils_2.getPageVarName)()}.goto("${url}");`;
19
20
  return templ;
20
21
  },
21
22
  name: exports.PLAYWRIGHT_GOTO_ACTION_NAME,
@@ -1 +1 @@
1
- {"version":3,"file":"reload-page.d.ts","sourceRoot":"","sources":["../../src/actions/reload-page.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGrD,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,yBA+BnC,CAAC"}
1
+ {"version":3,"file":"reload-page.d.ts","sourceRoot":"","sources":["../../src/actions/reload-page.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,yBA+BnC,CAAC"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.reloadActionGenerator = exports.PLAYWRIGHT_RELOAD_ACTION_NAME = void 0;
4
4
  const utils_1 = require("../agent/browsing/utils");
5
5
  const constants_1 = require("./constants");
6
+ const utils_2 = require("./utils");
6
7
  exports.PLAYWRIGHT_RELOAD_ACTION_NAME = "page_reload";
7
8
  const reloadActionGenerator = (page) => {
8
9
  return {
@@ -12,7 +13,7 @@ const reloadActionGenerator = (page) => {
12
13
  await (0, utils_1.injectPwLocatorGenerator)(page);
13
14
  },
14
15
  template: () => {
15
- const templ = `await page.reload();`;
16
+ const templ = `await ${(0, utils_2.getPageVarName)()}.reload();`;
16
17
  return templ;
17
18
  },
18
19
  name: exports.PLAYWRIGHT_RELOAD_ACTION_NAME,
@@ -6,4 +6,5 @@ declare global {
6
6
  }
7
7
  }
8
8
  export declare function getPlaywrightLocatorUsingCssSelector(cssSelector: string, page: Page): Promise<any>;
9
+ export declare function getPageVarName(): string;
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/actions/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE,GAAG,CAAC;QAChB,MAAM,EAAE,GAAG,CAAC;KACb;CACF;AAED,wBAAsB,oCAAoC,CACxD,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,IAAI,gBAkBX"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/actions/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE,GAAG,CAAC;QAChB,MAAM,EAAE,GAAG,CAAC;KACb;CACF;AAED,wBAAsB,oCAAoC,CACxD,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,IAAI,gBAwBX;AAED,wBAAgB,cAAc,WAG7B"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getPlaywrightLocatorUsingCssSelector = void 0;
3
+ exports.getPageVarName = exports.getPlaywrightLocatorUsingCssSelector = void 0;
4
4
  async function getPlaywrightLocatorUsingCssSelector(cssSelector, page) {
5
5
  // TODO: analyse other solutions than just css. Also find other solutions to support :has-text -> prompting ?
6
6
  // jquery doesnt support :has-text. neither css. Only playwright locator supports this selector though.
@@ -9,7 +9,13 @@ async function getPlaywrightLocatorUsingCssSelector(cssSelector, page) {
9
9
  .replaceAll(":text", ":contains");
10
10
  return await page.evaluate((locator) => {
11
11
  const elements = window.jQuery(locator.cssForJq);
12
- let selectedElem = elements[0];
12
+ let elIdx = 0;
13
+ Array.from(elements).forEach((el, i) => {
14
+ if (window.jQuery(el).is(":visible")) {
15
+ elIdx = i;
16
+ }
17
+ });
18
+ let selectedElem = elements[elIdx];
13
19
  if (!selectedElem) {
14
20
  throw Error(`Unable to find element, css: ${locator.css}`);
15
21
  }
@@ -17,3 +23,8 @@ async function getPlaywrightLocatorUsingCssSelector(cssSelector, page) {
17
23
  }, { css: cssSelector, cssForJq: sanitizedCssSelectorForJQuery });
18
24
  }
19
25
  exports.getPlaywrightLocatorUsingCssSelector = getPlaywrightLocatorUsingCssSelector;
26
+ function getPageVarName() {
27
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
28
+ return process.env.PAGE_VAR_NAME || "page";
29
+ }
30
+ exports.getPageVarName = getPageVarName;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,GACjB,EAAE,iBAAiB,iBAqDnB"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAkBA,KAAK,iBAAiB,GAAG;IACvB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,GACjB,EAAE,iBAAiB,iBAuDnB"}
@@ -35,6 +35,7 @@ async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, }
35
35
  const playwrightConfig = await (0, utils_2.readPlaywrightConfig)();
36
36
  // detect the playwright project name for the given test file and playwright config
37
37
  const project = await (0, utils_2.detectProjectName)(testFilePath, playwrightConfig);
38
+ const pageVar = await (0, web_1.getPageVariableNameFromCreateTest)(testFilePath);
38
39
  console.log(`Detected playwright project name: ${project}`);
39
40
  // run playwright test which will internally run the master agent
40
41
  let command = `npx playwright test ${testFilePath} --retries 0 --project ${project} --timeout 0`;
@@ -49,6 +50,7 @@ async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, }
49
50
  PW_TEST_HTML_REPORT_OPEN: "never",
50
51
  // pass the test gen token so that the agent has the same configuration as cli
51
52
  TEST_GEN_TOKEN: (0, utils_1.getTestConfigCliArg)(),
53
+ PAGE_VAR_NAME: pageVar || "page",
52
54
  },
53
55
  });
54
56
  }
@@ -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;AAUvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAyFD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAwCD,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"}
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;AAI5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAqFD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAwCD,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"}
@@ -10,7 +10,9 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
10
10
  const minimatch_1 = require("minimatch");
11
11
  const api_1 = __importDefault(require("tsx/cjs/api"));
12
12
  const logger_1 = require("../../bin/logger");
13
+ const context_1 = require("../../bin/utils/context");
13
14
  const web_1 = require("../../bin/utils/platform/web");
15
+ const fix_ts_errors_1 = require("../codegen/fix-ts-errors");
14
16
  const update_flow_1 = require("../codegen/update-flow");
15
17
  function isRegExp(obj) {
16
18
  return (obj instanceof RegExp ||
@@ -47,24 +49,25 @@ async function prepareFileForUpdateScenario(genConfig) {
47
49
  - Once the methods is placed in the right location with a task and playwright page provided, assume the task is done.
48
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`,
49
51
  ],
50
- }, specPath, genConfig.options, false);
52
+ }, specPath, genConfig.options, false, false);
51
53
  const createTestFilePath = suggestion?.updatedFiles[0] || "";
52
54
  console.log("appending to existing test block");
53
55
  console.log("updated test file path", createTestFilePath);
54
- // if change suggested by LLM is in spec file
55
- const isChangeInSpecFile = createTestFilePath.includes("spec.ts");
56
- // if the file is not a spec file, add import for the test-gen
57
- if (!isChangeInSpecFile) {
58
- await fs_extra_1.default.writeFile(createTestFilePath, (0, web_1.addNewImport)(await fs_extra_1.default.readFile(createTestFilePath, "utf-8"), ["createTest"], "@empiricalrun/test-gen"));
59
- }
56
+ await fs_extra_1.default.writeFile(createTestFilePath, (0, web_1.addNewImport)(await fs_extra_1.default.readFile(createTestFilePath, "utf-8"), ["createTest"], "@empiricalrun/test-gen"));
57
+ const { codePrompt, pomPrompt } = await (0, context_1.contextForGeneration)(createTestFilePath);
58
+ await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
59
+ file: createTestFilePath,
60
+ testCode: codePrompt,
61
+ pomCode: pomPrompt,
62
+ testCase: testCase,
63
+ options: genConfig.options,
64
+ });
60
65
  const testFileContent = await fs_extra_1.default.readFile(specPath, "utf-8");
61
66
  const { testBlock, testNode } = (0, web_1.getTypescriptTestBlock)(name, testFileContent);
62
67
  const parentDescribe = (0, web_1.findFirstSerialDescribeBlock)(testNode);
63
68
  // add test.only / describe.only to the spec file so that only that block is executed
64
69
  const updatedTestFileContent = newContentsWithTestOnly(testFileContent, testBlock, testBlock, parentDescribe?.getText() || "");
65
- await fs_extra_1.default.writeFile(specPath, isChangeInSpecFile
66
- ? (0, web_1.addNewImport)(updatedTestFileContent, ["createTest"], "@empiricalrun/test-gen")
67
- : updatedTestFileContent);
70
+ await fs_extra_1.default.writeFile(specPath, updatedTestFileContent);
68
71
  }
69
72
  /**
70
73
  * Function to prepare test file for new scenarios for master agent to run
@@ -0,0 +1,13 @@
1
+ import { TraceClient } from "@empiricalrun/llm";
2
+ import { CustomLogger } from "../../bin/logger";
3
+ import { TestCase, TestGenConfigOptions } from "../../types";
4
+ export declare function validateAndFixTypescriptErrors({ trace, logger, file, testCode, pomCode, testCase, options, }: {
5
+ trace?: TraceClient;
6
+ logger?: CustomLogger;
7
+ file: string;
8
+ testCode: string;
9
+ pomCode: string;
10
+ testCase: TestCase;
11
+ options?: TestGenConfigOptions;
12
+ }): Promise<void>;
13
+ //# sourceMappingURL=fix-ts-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-ts-errors.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/fix-ts-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAQhD,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7D,wBAAsB,8BAA8B,CAAC,EACnD,KAAK,EACL,MAA2B,EAC3B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,OAAO,GACR,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;IACnB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,iBAoEA"}
@@ -0,0 +1,76 @@
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.validateAndFixTypescriptErrors = void 0;
7
+ const llm_1 = require("@empiricalrun/llm");
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const logger_1 = require("../../bin/logger");
10
+ const web_1 = require("../../bin/utils/platform/web");
11
+ const constants_1 = require("../../constants");
12
+ async function validateAndFixTypescriptErrors({ trace, logger = new logger_1.CustomLogger(), file, testCode, pomCode, testCase, options, }) {
13
+ const validateTypesSpan = trace?.span({ name: "detect-type-errors-in-file" });
14
+ logger.log("Validating types...");
15
+ let errors = (0, web_1.validateTypescript)(file);
16
+ validateTypesSpan?.end({ output: { errors } });
17
+ if (!errors.length) {
18
+ logger.success("Found no type issues!");
19
+ }
20
+ const maxIteration = 2;
21
+ let counter = 0;
22
+ while (errors.length > 0) {
23
+ const fileContent = await fs_extra_1.default.readFile(file, "utf-8");
24
+ counter += 1;
25
+ if (counter > maxIteration) {
26
+ trace?.event({ name: "code-fix-iteration-max-out" });
27
+ logger.error([
28
+ `Unable to fix typescript errors. Please review ${file} manually and fix the typescript errors.`,
29
+ `Trace: ${trace?.getTraceUrl()}`,
30
+ ].join("\n"));
31
+ break;
32
+ }
33
+ trace?.event({ name: "Found errors fixing" });
34
+ logger.warn("Found few errors while validating types:");
35
+ errors.forEach((e) => logger.warn(e));
36
+ logger.log("Trying to fix above errors...");
37
+ const promptSpan = trace?.span({ name: "fix-type-errors-prompt" });
38
+ const instruction = await (0, llm_1.getPrompt)("fix-file-errors-ts", {
39
+ testFiles: testCode || "",
40
+ pageFiles: pomCode || "",
41
+ scenarioFile: file,
42
+ errors: errors,
43
+ fileContent: fileContent,
44
+ scenaioName: testCase.name,
45
+ });
46
+ promptSpan?.end({ output: { instruction } });
47
+ const llm = new llm_1.LLM({
48
+ trace,
49
+ provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
50
+ defaultModel: options?.model || constants_1.DEFAULT_MODEL,
51
+ providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
52
+ });
53
+ const message = await llm.createChatCompletion({
54
+ messages: instruction,
55
+ modelParameters: {
56
+ ...constants_1.DEFAULT_MODEL_PARAMETERS,
57
+ ...options?.modelParameters,
58
+ },
59
+ });
60
+ const response = message?.content || "";
61
+ const readWriteFileSpan = trace?.span({ name: "write-to-file" });
62
+ await fs_extra_1.default.writeFile(file, response, "utf-8");
63
+ readWriteFileSpan?.end({ output: { response } });
64
+ trace?.event({ name: "lint-file" });
65
+ await (0, web_1.lintErrors)(file);
66
+ const validateTypesSpan = trace?.span({
67
+ name: "detect-type-errors-in-file",
68
+ });
69
+ errors = (0, web_1.validateTypescript)(file);
70
+ validateTypesSpan?.end({ output: { errors } });
71
+ if (!errors.length) {
72
+ logger.success("Found no type issues!");
73
+ }
74
+ }
75
+ }
76
+ exports.validateAndFixTypescriptErrors = validateAndFixTypescriptErrors;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAyBA,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,CAoJrB"}
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,CA+FrB"}
@@ -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 fix_ts_errors_1 = require("./fix-ts-errors");
15
16
  const update_flow_1 = require("./update-flow");
16
17
  async function generateTest(testCase, file, options) {
17
18
  const logger = new logger_1.CustomLogger();
@@ -79,63 +80,15 @@ async function generateTest(testCase, file, options) {
79
80
  logger.log("Linting generated code...");
80
81
  trace.event({ name: "lint-file" });
81
82
  await (0, web_1.lintErrors)(file);
82
- const validateTypesSpan = trace.span({ name: "detect-type-errors-in-file" });
83
- logger.log("Validating types...");
84
- let errors = (0, web_1.validateTypescript)(file);
85
- validateTypesSpan.end({ output: { errors } });
86
- if (!errors.length) {
87
- logger.success("Found no type issues!");
88
- }
89
- const maxIteration = 2;
90
- let counter = 0;
91
- while (errors.length > 0) {
92
- const fileContent = fs_extra_1.default.readFileSync(file, "utf-8");
93
- counter += 1;
94
- if (counter > maxIteration) {
95
- trace.event({ name: "code-fix-iteration-max-out" });
96
- logger.error([
97
- `Unable to fix typescript errors. Please review ${file} manually and fix the typescript errors.`,
98
- `Run the test-gen command again, once errors are fixed.`,
99
- `Trace: ${trace.getTraceUrl()}`,
100
- ].join("\n"));
101
- break;
102
- }
103
- trace.event({ name: "Found errors fixing" });
104
- logger.warn("Found few errors while validating types:");
105
- errors.forEach((e) => logger.warn(e));
106
- logger.log("Trying to fix above errors...");
107
- const promptSpan = trace.span({ name: "fix-type-errors-prompt" });
108
- const instruction = await (0, llm_1.getPrompt)("fix-file-errors-ts", {
109
- testFiles: codePrompt || "",
110
- pageFiles: pomPrompt || "",
111
- scenarioFile: file,
112
- errors: errors,
113
- fileContent: fileContent,
114
- scenaioName: testCase.name,
115
- });
116
- promptSpan.end({ output: { instruction } });
117
- const message = await llm.createChatCompletion({
118
- messages: instruction,
119
- modelParameters: {
120
- ...constants_1.DEFAULT_MODEL_PARAMETERS,
121
- ...options.modelParameters,
122
- },
123
- });
124
- response = message?.content || "";
125
- const readWriteFileSpan = trace.span({ name: "write-to-file" });
126
- await fs_extra_1.default.writeFile(file, response, "utf-8");
127
- readWriteFileSpan.end({ output: { response } });
128
- trace.event({ name: "lint-file" });
129
- await (0, web_1.lintErrors)(file);
130
- const validateTypesSpan = trace.span({
131
- name: "detect-type-errors-in-file",
132
- });
133
- errors = (0, web_1.validateTypescript)(file);
134
- validateTypesSpan.end({ output: { errors } });
135
- if (!errors.length) {
136
- logger.success("Found no type issues!");
137
- }
138
- }
83
+ await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
84
+ trace,
85
+ logger,
86
+ file,
87
+ testCode: codePrompt,
88
+ pomCode: pomPrompt,
89
+ testCase: testCase,
90
+ options,
91
+ });
139
92
  trace.event({ name: "format-file" });
140
93
  await (0, web_1.formatCode)(file);
141
94
  logger.success("File formatted successfully!");
@@ -2,6 +2,6 @@ import { TestCase, TestGenConfigOptions } from "../../types";
2
2
  type UpdatedTestCase = TestCase & {
3
3
  updatedFiles: string[];
4
4
  };
5
- export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions | undefined, logging?: boolean): Promise<UpdatedTestCase[]>;
5
+ export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions | undefined, logging?: boolean, validate?: boolean): Promise<UpdatedTestCase[]>;
6
6
  export {};
7
7
  //# 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":"AAyBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAqB7D,KAAK,eAAe,GAAG,QAAQ,GAAG;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,GAAE,OAAc,GACtB,OAAO,CAAC,eAAe,EAAE,CAAC,CA6I5B"}
1
+ {"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,KAAK,eAAe,GAAG,QAAQ,GAAG;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,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,CAoJ5B"}
@@ -13,23 +13,9 @@ const context_1 = require("../../bin/utils/context");
13
13
  const web_1 = require("../../bin/utils/platform/web");
14
14
  const constants_1 = require("../../constants");
15
15
  const session_1 = require("../../session");
16
- function extractInformation(input) {
17
- const result = [];
18
- // TODO: use better structure for this. Do not kill me for this please.
19
- const regex = /<file_path>(.*?)<\/file_path>[\s\S]*?<old_code_block>([\s\S]*?)<\/old_code_block>[\s\S]*?<new_code_block>([\s\S]*?)<\/new_code_block>[\s\S]*?<change>([\s\S]*?)<\/change>/g;
20
- let match;
21
- while ((match = regex.exec(input)) !== null) {
22
- const [, filePath, oldCode, newCode, reason] = match;
23
- result.push({
24
- filePath: filePath?.trim(),
25
- oldCode: oldCode?.trim(),
26
- newCode: newCode?.trim(),
27
- reason: reason?.trim(),
28
- });
29
- }
30
- return result;
31
- }
32
- async function updateTest(testCase, file, options, logging = true) {
16
+ const fix_ts_errors_1 = require("./fix-ts-errors");
17
+ const utils_1 = require("./utils");
18
+ async function updateTest(testCase, file, options, logging = true, validate = true) {
33
19
  const logger = new logger_1.CustomLogger({ useReporter: logging });
34
20
  const context = await (0, context_1.contextForGeneration)(file);
35
21
  const { codePrompt, pomPrompt, testFileContent } = context;
@@ -81,7 +67,7 @@ async function updateTest(testCase, file, options, logging = true) {
81
67
  });
82
68
  let response = firstShotMessage?.content || "";
83
69
  logger.success("Test generated successfully!");
84
- const fileChanges = extractInformation(response);
70
+ const fileChanges = (0, utils_1.extractTestUpdates)(response);
85
71
  await Promise.allSettled(fileChanges.map(async (fileChange) => {
86
72
  if (!fileChange.filePath) {
87
73
  return;
@@ -97,12 +83,8 @@ async function updateTest(testCase, file, options, logging = true) {
97
83
  const { testBlock } = (0, web_1.getTypescriptTestBlock)(testCase?.name, contents);
98
84
  contents = contents.replace(testBlock, `\n\n${strippedContent}`);
99
85
  updatedContent = prependContent + contents;
100
- await fs_extra_1.default.writeFile(file, updatedContent, "utf-8");
86
+ await fs_extra_1.default.writeFile(fileChange.filePath, updatedContent, "utf-8");
101
87
  readWriteFileSpan.end({ output: { updatedContent } });
102
- trace.event({ name: "format-file" });
103
- await (0, web_1.lintErrors)(fileChange.filePath);
104
- await (0, web_1.formatCode)(fileChange.filePath);
105
- logger.success(`${fileChange.filePath} file formatted successfully!`);
106
88
  }
107
89
  else {
108
90
  const readWriteFileSpan = trace.span({ name: "write-to-file" });
@@ -129,11 +111,22 @@ async function updateTest(testCase, file, options, logging = true) {
129
111
  }
130
112
  await fs_extra_1.default.writeFile(fileChange.filePath, contents, "utf-8");
131
113
  readWriteFileSpan.end({ output: { contents } });
132
- trace.event({ name: "format-file" });
133
- await (0, web_1.lintErrors)(fileChange.filePath);
134
- await (0, web_1.formatCode)(fileChange.filePath);
135
- logger.success(`${fileChange.filePath} file formatted successfully!`);
136
114
  }
115
+ // format and validate file change
116
+ if (validate) {
117
+ await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
118
+ trace,
119
+ logger,
120
+ file: fileChange.filePath,
121
+ testCode: codePrompt,
122
+ pomCode: pomPrompt,
123
+ testCase: testCase,
124
+ options,
125
+ });
126
+ }
127
+ trace.event({ name: "format-file" });
128
+ await (0, web_1.formatCode)(fileChange.filePath);
129
+ logger.success(`${fileChange.filePath} file formatted successfully!`);
137
130
  }));
138
131
  logger.log(`Trace: ${trace.getTraceUrl()}`);
139
132
  generatedTestCases.push({
@@ -0,0 +1,19 @@
1
+ /**
2
+ *
3
+ * method to extract file path and code updates for the LLM response of update flow
4
+ * @export
5
+ * @param {string} input
6
+ * @return {*} {({
7
+ * filePath: string | undefined;
8
+ * oldCode: string | undefined;
9
+ * newCode: string | undefined;
10
+ * reason: string | undefined;
11
+ * }[])}
12
+ */
13
+ export declare function extractTestUpdates(input: string): {
14
+ filePath: string | undefined;
15
+ oldCode: string | undefined;
16
+ newCode: string | undefined;
17
+ reason: string | undefined;
18
+ }[];
19
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IACjD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,EAAE,CAqBF"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractTestUpdates = void 0;
4
+ /**
5
+ *
6
+ * method to extract file path and code updates for the LLM response of update flow
7
+ * @export
8
+ * @param {string} input
9
+ * @return {*} {({
10
+ * filePath: string | undefined;
11
+ * oldCode: string | undefined;
12
+ * newCode: string | undefined;
13
+ * reason: string | undefined;
14
+ * }[])}
15
+ */
16
+ function extractTestUpdates(input) {
17
+ const result = [];
18
+ // TODO: use better structure for this. Do not kill me for this please.
19
+ const regex = /<file_path>(.*?)<\/file_path>[\s\S]*?<old_code_block>([\s\S]*?)<\/old_code_block>[\s\S]*?<new_code_block>([\s\S]*?)<\/new_code_block>[\s\S]*?<change>([\s\S]*?)<\/change>/g;
20
+ let match;
21
+ // sometimes LLM responds with single backslashes instead of double backslashe for escapes
22
+ // we only have a scenario of double quotes being escaped.
23
+ // eslint-disable-next-line no-useless-escape, prettier/prettier
24
+ input = input.replaceAll(' \"', ' \\"').replaceAll('\" ', '\\" ');
25
+ while ((match = regex.exec(input)) !== null) {
26
+ const [, filePath, oldCode, newCode, reason] = match;
27
+ result.push({
28
+ filePath: filePath?.trim(),
29
+ oldCode: oldCode?.trim(),
30
+ newCode: newCode?.trim(),
31
+ reason: reason?.trim(),
32
+ });
33
+ }
34
+ return result;
35
+ }
36
+ exports.extractTestUpdates = extractTestUpdates;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/master/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,GAAG,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAE1D,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,EAAE,EACzB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,oBAAoB,gBAsE9B"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/master/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,GAAG,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAE1D,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,EAAE,EACzB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,oBAAoB,gBAyE9B"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.masterAgent = void 0;
4
4
  const llm_1 = require("@empiricalrun/llm");
5
+ const vision_1 = require("@empiricalrun/llm/vision");
5
6
  const done_1 = require("../../actions/done");
6
7
  const next_task_1 = require("../../actions/next-task");
7
8
  const constants_1 = require("../../constants");
@@ -12,7 +13,7 @@ async function masterAgent(task, page, executedActions, trace, llm, options) {
12
13
  const buffer = await page.screenshot({ fullPage: true });
13
14
  const testGenReporter = new reporter_1.TestGenUpdatesReporter();
14
15
  const testGenSnapshotUpdatePromise = testGenReporter.sendCurrentView(buffer);
15
- const pageScreenshot = `data:image/png;base64,${buffer.toString("base64")}`;
16
+ const pageScreenshot = buffer.toString("base64");
16
17
  const promptMessages = await (0, llm_1.getPrompt)("test-gen", {
17
18
  task,
18
19
  executedActions: executedActions.map((a) => a).join("\n"),
@@ -29,7 +30,7 @@ async function masterAgent(task, page, executedActions, trace, llm, options) {
29
30
  {
30
31
  type: "image_url",
31
32
  image_url: {
32
- url: pageScreenshot,
33
+ url: (0, vision_1.imageFormatForProvider)(options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER, pageScreenshot),
33
34
  },
34
35
  },
35
36
  ];
package/dist/bin/index.js CHANGED
@@ -61,7 +61,12 @@ async function runAgent(testGenConfig) {
61
61
  sessionId: testGenConfig.options?.metadata.testSessionId,
62
62
  generationId: testGenConfig.options?.metadata.generationId,
63
63
  });
64
- await runAgent(testGenConfig);
64
+ try {
65
+ await runAgent(testGenConfig);
66
+ }
67
+ catch (e) {
68
+ logger.error("Failed to run agent for the scenario", e);
69
+ }
65
70
  // TODO: move these reporters to a better lifecycle
66
71
  await (0, ci_1.reportOnCI)(testGenConfig.testCase);
67
72
  await (0, llm_1.flushAllTraces)();
@@ -42,5 +42,6 @@ export declare function formatCode(filePath: string): Promise<void>;
42
42
  export declare function addNewImport(contents: string, modules: string[], pkg: string): string;
43
43
  export declare function removeTestOnly(filePath: string): Promise<void>;
44
44
  export declare function getFixtureImportPath(filePath: string): string;
45
+ export declare function getPageVariableNameFromCreateTest(filePath: string): Promise<string | undefined>;
45
46
  export declare function replaceCreateTestWithNewCode(filePath: string, contents: string, generatedCode: string): string;
46
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqB,IAAI,EAAuB,MAAM,UAAU,CAAC;AAGxE;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd;IACD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;CAC5B,CAeA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAwD7D;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,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UA0CtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqB,IAAI,EAAuB,MAAM,UAAU,CAAC;AAGxE;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd;IACD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;CAC5B,CAeA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,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"}
@@ -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.replaceCreateTestWithNewCode = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.getTypescriptTestBlock = void 0;
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
9
  const prettier_1 = __importDefault(require("prettier"));
@@ -96,16 +96,6 @@ function validateTypescript(filePath) {
96
96
  const syntacticDiagnostics = program.getSyntacticDiagnostics(sourceFile);
97
97
  if (syntacticDiagnostics.length > 0) {
98
98
  syntacticDiagnostics.forEach((diagnostic) => {
99
- // const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
100
- // diagnostic.start,
101
- // );
102
- // const message = ts.flattenDiagnosticMessageText(
103
- // diagnostic.messageText,
104
- // "\n",
105
- // );
106
- // logger.log(
107
- // `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`,
108
- // );
109
99
  if (typeof diagnostic.messageText === "string") {
110
100
  errors.push(diagnostic.messageText);
111
101
  }
@@ -188,6 +178,22 @@ function getFixtureImportPath(filePath) {
188
178
  return fixturesPath;
189
179
  }
190
180
  exports.getFixtureImportPath = getFixtureImportPath;
181
+ async function getPageVariableNameFromCreateTest(filePath) {
182
+ const contents = await fs_extra_1.default.readFile(filePath, "utf-8");
183
+ const project = new ts_morph_1.Project();
184
+ const sourceFile = project.createSourceFile("test.ts", contents);
185
+ const createTestNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.CallExpression) &&
186
+ node.getExpression().getText() === "createTest"));
187
+ if (!createTestNode) {
188
+ throw new Error("createTest not found in file");
189
+ }
190
+ const descendants = createTestNode?.getDescendants();
191
+ const descendentTexts = descendants.map((d) => d.getText());
192
+ const parameterSeparatorIdx = descendentTexts.findIndex((s) => s === ",");
193
+ const pageVariableName = descendentTexts[parameterSeparatorIdx + 1];
194
+ return pageVariableName;
195
+ }
196
+ exports.getPageVariableNameFromCreateTest = getPageVariableNameFromCreateTest;
191
197
  function replaceCreateTestWithNewCode(filePath, contents, generatedCode) {
192
198
  const project = new ts_morph_1.Project();
193
199
  const sourceFile = project.createSourceFile("test.ts", contents);
@@ -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;IAkCK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B9C,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"}
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TestGenUpdatesReporter = exports.setReporterConfig = exports.getReporter = void 0;
7
7
  const r2_uploader_1 = require("@empiricalrun/r2-uploader");
8
8
  const reporter_1 = require("@empiricalrun/reporter");
9
- const promises_1 = __importDefault(require("fs/promises"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const logger_1 = require("../bin/logger");
12
12
  const uploader_1 = require("../uploader");
@@ -61,13 +61,23 @@ class TestGenUpdatesReporter {
61
61
  videoUrls,
62
62
  };
63
63
  await Promise.allSettled([
64
- reporter?.report(new reporter_1.ProcessLogMessageBuilder({ message: JSON.stringify(message) })),
65
- reporter?.report(new reporter_1.ProcessLogMessageBuilder({
66
- message: JSON.stringify({
67
- type: "trace",
68
- traceFiles,
69
- }),
70
- })),
64
+ ...(videoUrls.length
65
+ ? [
66
+ reporter?.report(new reporter_1.ProcessLogMessageBuilder({
67
+ message: JSON.stringify(message),
68
+ })),
69
+ ]
70
+ : []),
71
+ ...(traceFiles.length
72
+ ? [
73
+ reporter?.report(new reporter_1.ProcessLogMessageBuilder({
74
+ message: JSON.stringify({
75
+ type: "trace",
76
+ traceFiles,
77
+ }),
78
+ })),
79
+ ]
80
+ : []),
71
81
  ]);
72
82
  }
73
83
  catch (err) {
@@ -77,8 +87,10 @@ class TestGenUpdatesReporter {
77
87
  }
78
88
  async sendCurrentView(buffer) {
79
89
  // upload current screenshot to r2 and report it to reporter
80
- await promises_1.default.mkdir((process.cwd(), "gen-assets"));
81
- await promises_1.default.writeFile(path_1.default.join(process.cwd(), "gen-assets", `current-view-${Date.now()}.png`), buffer);
90
+ if (!fs_extra_1.default.existsSync(path_1.default.join(process.cwd(), "gen-assets"))) {
91
+ await fs_extra_1.default.mkdir((process.cwd(), "gen-assets"));
92
+ }
93
+ await fs_extra_1.default.writeFile(path_1.default.join(process.cwd(), "gen-assets", `current-view-${Date.now()}.png`), buffer);
82
94
  const uploadDir = (0, uploader_1.getUploadPathForRun)(reporterConfig?.projectRepoName);
83
95
  const files = await (0, r2_uploader_1.uploadDirectory)({
84
96
  sourceDir: path_1.default.join(process.cwd(), "gen-assets"),
@@ -91,7 +103,7 @@ class TestGenUpdatesReporter {
91
103
  await getReporter()?.report(new reporter_1.ProcessLogMessageBuilder({
92
104
  message: JSON.stringify({ type: "current-view", url }),
93
105
  }));
94
- await promises_1.default.rmdir((process.cwd(), "gen-assets"), { recursive: true });
106
+ await fs_extra_1.default.rmdir((process.cwd(), "gen-assets"), { recursive: true });
95
107
  }
96
108
  async sendMessage(message) {
97
109
  const reporter = getReporter();
@@ -1,6 +1,7 @@
1
1
  export declare const UPLOAD_BUCKET = "test-report";
2
2
  export declare const UPLOAD_DOMAIN = "https://reports.empirical.run";
3
3
  export declare function getFullUploadPath(filePath: string, uploadDir: string): string;
4
+ export declare function getRelativeUploadPath(filePath: string): string;
4
5
  /**
5
6
  * Function to upload test results to R2 using the project repo name and test name.
6
7
  * This function uploads both the JSON summary of test results and associated video files.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAUpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,eAAe,EACf,QAAQ,GACT,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CAmED;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAIpE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,UAErD;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,eAAe,EACf,QAAQ,GACT,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CAwDD;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
@@ -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.checkIfResultsUploadAllowed = exports.getUploadPathForRun = exports.uploadTestResultsUsingPrjRepo = exports.getFullUploadPath = exports.UPLOAD_DOMAIN = exports.UPLOAD_BUCKET = void 0;
6
+ exports.checkIfResultsUploadAllowed = exports.getUploadPathForRun = exports.uploadTestResultsUsingPrjRepo = exports.getRelativeUploadPath = exports.getFullUploadPath = exports.UPLOAD_DOMAIN = exports.UPLOAD_BUCKET = void 0;
7
7
  const r2_uploader_1 = require("@empiricalrun/r2-uploader");
8
8
  const reporter_1 = require("@empiricalrun/reporter");
9
9
  const path_1 = __importDefault(require("path"));
@@ -18,13 +18,14 @@ exports.UPLOAD_DOMAIN = "https://reports.empirical.run"; // domain based on buck
18
18
  const uploadId = crypto.randomUUID();
19
19
  function getFullUploadPath(filePath, uploadDir) {
20
20
  // remove the source dir from the file path - only keep the relative path
21
- // old path for data: ~/source-repo/test-results/testName/
22
- // new path for data: ~/source-repo/playwright-report/data/testName
23
- const relativeFilePath = filePath.replace(path_1.default.join(process.cwd(), PLAYWRIGHT_REPORT_DATA), "");
24
- // new relativeFilePath will look like
21
+ const relativeFilePath = getRelativeUploadPath(filePath);
25
22
  return `${exports.UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
26
23
  }
27
24
  exports.getFullUploadPath = getFullUploadPath;
25
+ function getRelativeUploadPath(filePath) {
26
+ return filePath.replace(path_1.default.join(process.cwd(), PLAYWRIGHT_REPORT_DATA), "");
27
+ }
28
+ exports.getRelativeUploadPath = getRelativeUploadPath;
28
29
  /**
29
30
  * Function to upload test results to R2 using the project repo name and test name.
30
31
  * This function uploads both the JSON summary of test results and associated video files.
@@ -36,27 +37,21 @@ exports.getFullUploadPath = getFullUploadPath;
36
37
  * @returns {string} returns.summaryUrl - URL of the uploaded summary JSON file.
37
38
  */
38
39
  async function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, }) {
39
- // project repo name is the github repo name
40
- // test-generation/<project-name>/<random-uuid>
41
40
  const uploadDir = getUploadPathForRun(projectRepoName);
42
- // upload test assets
43
41
  const files = await (0, r2_uploader_1.uploadDirectory)({
44
- // ~/source-repo/playwright-report -> has index.html, data, trace(they are assets for index.html, not sure why they are called trace), and summary.json
45
- sourceDir: path_1.default.join(process.cwd(), PLAYWRIGHT_REPORT_DATA), // upload data dir
42
+ sourceDir: path_1.default.join(process.cwd(), PLAYWRIGHT_REPORT_DATA),
46
43
  destinationDir: uploadDir,
47
44
  uploadBucket: exports.UPLOAD_BUCKET,
48
45
  });
49
46
  // upload summary.json
50
47
  await (0, r2_uploader_1.uploadDirectory)({
51
- // ~/source-repo/playwright-report -> has index.html, data, trace(they are assets for index.html, not sure why they are called trace), and summary.json
52
- sourceDir: path_1.default.join(process.cwd(), "playwright-report"), // upload data dir
48
+ sourceDir: path_1.default.join(process.cwd(), "playwright-report"),
53
49
  fileList: [path_1.default.join(process.cwd(), "playwright-report", "summary.json")],
54
50
  destinationDir: uploadDir,
55
51
  uploadBucket: exports.UPLOAD_BUCKET,
56
52
  });
57
- const fileNames = Object.keys(files); // fileNames are absolute paths of the input files
58
- // old default location: ..../source-repo/test-results/summary.json
59
- // new default location: ..../source-repo/playwright-report/summary.json
53
+ // fileNames are relative upload paths
54
+ const fileNames = Object.keys(files);
60
55
  const defaultLocation = path_1.default.join(process.cwd(), "playwright-report", "summary.json");
61
56
  const results = (0, reporter_1.parseJsonReport)(defaultLocation);
62
57
  const flatTestsList = (0, reporter_1.getFlattenedTestList)(results.suites);
@@ -68,24 +63,23 @@ async function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, }) {
68
63
  // results array is basically made by retries
69
64
  for (const attachments of test.tests[0].results[0].attachments) {
70
65
  if (attachments.path) {
71
- testAttachmentPaths.push(attachments.path);
66
+ testAttachmentPaths.push(getRelativeUploadPath(attachments.path));
72
67
  }
73
68
  }
74
69
  }
75
70
  }
76
71
  }
77
72
  }
78
- // current assumption
79
- // - test gen will only run on a single spec file
80
- // - the video files are of the format - <some-directory>/video.webm
81
73
  const videoFiles = fileNames.filter((fileName) => fileName.endsWith(".webm") && testAttachmentPaths.includes(fileName));
82
74
  const traceFiles = fileNames.filter((fileName) => fileName.endsWith(".zip") && testAttachmentPaths.includes(fileName));
83
75
  return {
84
- videoUrls: videoFiles.map((fileName) => getFullUploadPath(fileName, uploadDir)),
85
- // uploaded separately
76
+ videoUrls: videoFiles
77
+ .map((fileName) => getFullUploadPath(fileName, uploadDir))
78
+ .filter((url) => !!url),
86
79
  summaryUrl: `${exports.UPLOAD_DOMAIN}/${uploadDir}/summary.json`,
87
- // summaryUrl: getFullUploadPath("/test-results/summary.json", uploadDir),
88
- traceFiles: traceFiles.map((fileName) => getFullUploadPath(fileName, uploadDir)),
80
+ traceFiles: traceFiles
81
+ .map((fileName) => getFullUploadPath(fileName, uploadDir))
82
+ .filter((url) => !!url),
89
83
  };
90
84
  }
91
85
  exports.uploadTestResultsUsingPrjRepo = uploadTestResultsUsingPrjRepo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.27.6",
3
+ "version": "0.27.8",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -41,7 +41,7 @@
41
41
  "ts-morph": "^23.0.0",
42
42
  "tsx": "^4.16.2",
43
43
  "typescript": "^5.3.3",
44
- "@empiricalrun/llm": "^0.9.1",
44
+ "@empiricalrun/llm": "^0.9.2",
45
45
  "@empiricalrun/r2-uploader": "^0.2.0",
46
46
  "@empiricalrun/reporter": "^0.18.0"
47
47
  },