@empiricalrun/test-gen 0.22.11 → 0.23.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,17 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3b4b2f3: feat: update the dashboard ux with updated messages between dashboard and test-gen
8
+
9
+ ## 0.22.12
10
+
11
+ ### Patch Changes
12
+
13
+ - bfd6724: fix: avoid duplicate fixture imports
14
+
3
15
  ## 0.22.11
4
16
 
5
17
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,OAAO,EAAU,YAAY,EAAE,MAAM,UAAU,CAAC;AAQhD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,eAAe,CAAmC;gBAC9C,IAAI,EAAE,IAAI;IAYhB,aAAa,CAAC,IAAI,oBAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAqBhE,gBAAgB,IAAI,YAAY,EAAE;IAIlC,YAAY;IAIZ,UAAU;CASX"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIlC,OAAO,EAAU,YAAY,EAAE,MAAM,UAAU,CAAC;AAQhD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,eAAe,CAAmC;gBAC9C,IAAI,EAAE,IAAI;IAYhB,aAAa,CAAC,IAAI,oBAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAuBhE,gBAAgB,IAAI,YAAY,EAAE;IAIlC,YAAY;IAIZ,UAAU;CASX"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PlaywrightActions = void 0;
4
4
  const logger_1 = require("../bin/logger");
5
+ const reporter_1 = require("../reporter");
5
6
  const assert_1 = require("./assert");
6
7
  const click_1 = require("./click");
7
8
  const done_1 = require("./done");
@@ -27,7 +28,8 @@ class PlaywrightActions {
27
28
  if (!action) {
28
29
  throw Error(`No action registered for action: ${name}`);
29
30
  }
30
- const logger = new logger_1.CustomLogger();
31
+ const logger = new logger_1.CustomLogger({ useReporter: false });
32
+ const testgenUpdatesReporter = new reporter_1.TestGenUpdatesReporter();
31
33
  logger.logEmptyLine();
32
34
  try {
33
35
  const templateOptions = (await action.execute(args)) || { locator: "" };
@@ -36,6 +38,7 @@ class PlaywrightActions {
36
38
  this.recordedActions.push({ name, code });
37
39
  if (code) {
38
40
  logger.log(`action: ${name} \ncode: ${code} \nreason: ${args.reason}`);
41
+ void testgenUpdatesReporter.sendMessage("```ts\n" + code + "\n```");
39
42
  }
40
43
  }
41
44
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAWlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC1D,YAAY,CAAC,EAAE;QACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,mBAmK9B;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,mBAqG9B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMnD,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC1D,YAAY,CAAC,EAAE;QACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,mBA2K9B;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,oBAAoB,mBAqG9B"}
@@ -5,13 +5,15 @@ const llm_1 = require("@empiricalrun/llm");
5
5
  const actions_1 = require("../../actions");
6
6
  const logger_1 = require("../../bin/logger");
7
7
  const constants_1 = require("../../constants");
8
+ const reporter_1 = require("../../reporter");
8
9
  const session_1 = require("../../session");
9
10
  const html_1 = require("../../utils/html");
10
11
  const run_1 = require("../master/run");
11
12
  const verification_1 = require("../verification");
12
13
  const utils_1 = require("./utils");
13
14
  async function browsingAgentUsingMasterAgent(task, page, options) {
14
- const logger = new logger_1.CustomLogger();
15
+ const logger = new logger_1.CustomLogger({ useReporter: false });
16
+ const testgenUpdatesReporter = new reporter_1.TestGenUpdatesReporter();
15
17
  const trace = llm_1.langfuseInstance.trace({
16
18
  name: "test-generator",
17
19
  ...(0, session_1.getSessionDetails)(),
@@ -44,12 +46,15 @@ async function browsingAgentUsingMasterAgent(task, page, options) {
44
46
  });
45
47
  isGivenTaskDone = verificationAgentResp.isDone;
46
48
  if (isGivenTaskDone) {
47
- logger.log(`Master agent task is done: ${verificationAgentResp.reason}`);
49
+ await testgenUpdatesReporter.sendMessage(`${verificationAgentResp.reason} Marking the task as done.`);
48
50
  break;
49
51
  }
50
52
  }
51
53
  const { action, reason } = await (0, run_1.masterAgent)(task, page, masterAgentActions, masterAgentSpan, llm, options);
52
54
  logger.log(`Next action: ${action} \n reason: ${reason}`);
55
+ if (!action) {
56
+ break;
57
+ }
53
58
  if (isGivenTaskDone) {
54
59
  break;
55
60
  }
@@ -129,6 +134,7 @@ async function browsingAgentUsingMasterAgent(task, page, options) {
129
134
  ?.reason,
130
135
  });
131
136
  lastActionExecTrace = e.message;
137
+ void testgenUpdatesReporter.sendMessage(e.message);
132
138
  logger.error(lastActionExecTrace, e);
133
139
  }
134
140
  }
@@ -152,6 +158,7 @@ async function browsingAgentUsingMasterAgent(task, page, options) {
152
158
  const code = actions.generateCode();
153
159
  trace.update({ input: { task }, output: { code } });
154
160
  logger.success("Successfully generated code for the given task");
161
+ await testgenUpdatesReporter.sendMessage(`Successfully generated code for the given task. \n View [trace](${trace.getTraceUrl()})`);
155
162
  logger.log(`Trace: ${trace.getTraceUrl()}`);
156
163
  return code;
157
164
  }
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAaA,wBAAsB,+BAA+B,CAAC,YAAY,EAAE,MAAM,iBA4BzE"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAcA,wBAAsB,+BAA+B,CAAC,YAAY,EAAE,MAAM,iBA6BzE"}
@@ -9,10 +9,11 @@ const logger_1 = require("../../bin/logger");
9
9
  const utils_1 = require("../../bin/utils");
10
10
  const web_1 = require("../../bin/utils/platform/web");
11
11
  const server_1 = require("../../file/server");
12
+ const reporter_1 = require("../../reporter");
12
13
  const exec_1 = require("../../utils/exec");
13
14
  const utils_2 = require("./utils");
14
15
  async function generateTestsUsingBrowsingAgent(testFilePath) {
15
- const logger = new logger_1.CustomLogger();
16
+ const logger = new logger_1.CustomLogger({ useReporter: false });
16
17
  (0, utils_2.canRunBrowsingAgent)(testFilePath);
17
18
  const port = await (0, detect_port_1.default)(3030);
18
19
  const fileService = new server_1.FileService({ port });
@@ -37,6 +38,7 @@ async function generateTestsUsingBrowsingAgent(testFilePath) {
37
38
  }
38
39
  catch (e) {
39
40
  logger.error(e);
41
+ await new reporter_1.TestGenUpdatesReporter().sendMessage(e);
40
42
  process.exit(1);
41
43
  }
42
44
  await (0, web_1.removeTestOnly)(testFilePath);
@@ -25,7 +25,7 @@ exports.prepareBrowsingAgentTask = prepareBrowsingAgentTask;
25
25
  async function prepareFileForBrowsingAgent(genConfig) {
26
26
  const { specPath, testCase } = genConfig;
27
27
  const { name, steps } = testCase;
28
- const logger = new logger_1.CustomLogger();
28
+ const logger = new logger_1.CustomLogger({ useReporter: false });
29
29
  if (!fs_extra_1.default.existsSync(specPath)) {
30
30
  await fs_extra_1.default.createFile(specPath);
31
31
  const fileContentWithImports = (0, web_1.addNewImport)("", ["test", "expect"], (0, web_1.getFixtureImportPath)(specPath));
@@ -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;AAKlC,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,gBAgE9B"}
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"}
@@ -5,10 +5,13 @@ const llm_1 = require("@empiricalrun/llm");
5
5
  const done_1 = require("../../actions/done");
6
6
  const next_task_1 = require("../../actions/next-task");
7
7
  const constants_1 = require("../../constants");
8
+ const reporter_1 = require("../../reporter");
8
9
  async function masterAgent(task, page, executedActions, trace, llm, options) {
9
10
  trace.update({ input: { task } });
10
11
  const promptSpan = trace.span({ name: "page-prompt" });
11
12
  const buffer = await page.screenshot({ fullPage: true });
13
+ const testGenReporter = new reporter_1.TestGenUpdatesReporter();
14
+ const testGenSnapshotUpdatePromise = testGenReporter.sendCurrentView(buffer);
12
15
  const pageScreenshot = `data:image/png;base64,${buffer.toString("base64")}`;
13
16
  const promptMessages = await (0, llm_1.getPrompt)("test-gen", {
14
17
  task,
@@ -66,6 +69,10 @@ async function masterAgent(task, page, executedActions, trace, llm, options) {
66
69
  }
67
70
  }
68
71
  trace.update({ input: { task }, output: { output } });
72
+ if (output.action) {
73
+ await testGenReporter.sendMessage(output.action);
74
+ }
75
+ await testGenSnapshotUpdatePromise;
69
76
  return output;
70
77
  }
71
78
  exports.masterAgent = masterAgent;
package/dist/bin/index.js CHANGED
@@ -28,7 +28,7 @@ async function runAgent(sourceFile, testGenConfig) {
28
28
  logger.success(`Generating test using ${testGenConfig.options?.agent} agent`);
29
29
  await (0, utils_1.prepareFileForBrowsingAgent)(testGenConfig);
30
30
  await (0, run_1.generateTestsUsingBrowsingAgent)(specPath);
31
- await (0, reporter_1.reportTestGenVideos)({
31
+ await new reporter_1.TestGenUpdatesReporter().reportGenAssets({
32
32
  projectRepoName: testGenConfig.options.metadata.projectRepoName,
33
33
  testName: testCase.name,
34
34
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AAMA,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;EAwB3E;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,UAsBtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AAMA,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;EAwB3E;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"}
@@ -143,13 +143,27 @@ function replaceCreateTestWithNewCode(filePath, contents, generatedCode) {
143
143
  const sourceFile = project.createSourceFile("test.ts", contents);
144
144
  const createTestNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.CallExpression) &&
145
145
  node.getExpression().getText() === "createTest"));
146
- const updatedTestFile = contents.replace(createTestNode?.getText(), function () {
146
+ const createTestWithAwait = `await ${createTestNode?.getText()}`;
147
+ let updatedTestFile = contents.replace(createTestWithAwait, function () {
147
148
  // str.replace treats characters like $ differently
148
149
  // Using a function helps us avoid special handling
149
150
  // https://stackoverflow.com/a/28103073
150
151
  return "\n" + generatedCode;
151
152
  });
152
- const importStatement = `import { test, expect } from "${getFixtureImportPath(filePath)}";`;
153
- return importStatement + "\n" + updatedTestFile;
153
+ const fixtureImportNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.ImportDeclaration) &&
154
+ node.getText().includes("fixtures")));
155
+ if (!fixtureImportNode) {
156
+ throw new Error("No import from fixtures found.");
157
+ }
158
+ const importClause = fixtureImportNode.getImportClause();
159
+ const namedImports = importClause
160
+ .getNamedImports()
161
+ .map((imp) => imp.getName());
162
+ const isComplete = namedImports.includes("test") && namedImports.includes("expect");
163
+ if (!isComplete) {
164
+ const expectedImports = `import { test, expect } from "${getFixtureImportPath(filePath)}";`;
165
+ updatedTestFile = updatedTestFile.replace(fixtureImportNode.getText(), expectedImports);
166
+ }
167
+ return updatedTestFile;
154
168
  }
155
169
  exports.replaceCreateTestWithNewCode = replaceCreateTestWithNewCode;
@@ -16,10 +16,16 @@ export declare function getReporter(): Reporter | undefined;
16
16
  * }
17
17
  * @returns Promise<void> returns void
18
18
  */
19
- export declare function reportTestGenVideos({ projectRepoName, testName, }: {
20
- projectRepoName: string;
21
- testName: string;
22
- }): Promise<void>;
23
19
  export declare function setReporterConfig(config: ReporterConfigType): void;
20
+ export declare class TestGenUpdatesReporter {
21
+ constructor();
22
+ sendGenTrace(trace: string): Promise<void>;
23
+ reportGenAssets({ projectRepoName, testName, }: {
24
+ projectRepoName: string;
25
+ testName: string;
26
+ }): Promise<void>;
27
+ sendCurrentView(buffer: Buffer): Promise<void>;
28
+ sendMessage(message: string): Promise<void>;
29
+ }
24
30
  export {};
25
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAQ5E,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,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,wBAAsB,mBAAmB,CAAC,EACxC,eAAe,EACf,QAAQ,GACT,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBA6BA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAc5E,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,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,9 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setReporterConfig = exports.reportTestGenVideos = exports.getReporter = void 0;
6
+ exports.TestGenUpdatesReporter = exports.setReporterConfig = exports.getReporter = void 0;
4
7
  const reporter_1 = require("@empiricalrun/reporter");
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const path_1 = __importDefault(require("path"));
5
10
  const logger_1 = require("../bin/logger");
6
11
  const uploader_1 = require("../uploader");
12
+ const r2_1 = require("../uploader/r2");
7
13
  let reporterInstance = undefined;
8
14
  let reporterConfig = undefined;
9
15
  function getReporter() {
@@ -28,36 +34,72 @@ exports.getReporter = getReporter;
28
34
  * }
29
35
  * @returns Promise<void> returns void
30
36
  */
31
- async function reportTestGenVideos({ projectRepoName, testName, }) {
32
- const logger = new logger_1.CustomLogger();
33
- try {
34
- if (!(0, uploader_1.checkIfResultsUploadAllowed)()) {
35
- logger.log("Skipped uploading generated test video");
36
- }
37
- const { videoUrls } = await (0, uploader_1.uploadTestResultsUsingPrjtRepo)({
38
- projectRepoName,
39
- testName,
40
- });
41
- const reporter = getReporter();
42
- const reporterMessage = `
43
-
44
- Here are the videos of the generated test:
45
-
46
- ${videoUrls
47
- .map((url) => `
48
- <video src="${url}" autoplay="true" muted="true" controls playsinline></video>`)
49
- .join("\n")}
50
- `;
51
- await reporter?.report(new reporter_1.ProcessLogMessageBuilder({ message: reporterMessage }));
52
- }
53
- catch (err) {
54
- logger.error("Failed to report test results");
55
- console.error(err);
56
- }
57
- }
58
- exports.reportTestGenVideos = reportTestGenVideos;
59
37
  function setReporterConfig(config) {
60
38
  console.info("initialised reporter config");
61
39
  reporterConfig = config;
62
40
  }
63
41
  exports.setReporterConfig = setReporterConfig;
42
+ class TestGenUpdatesReporter {
43
+ constructor() { }
44
+ async sendGenTrace(trace) {
45
+ console.log("trace", trace);
46
+ // upload trace to r2 and report it to reporter
47
+ }
48
+ async reportGenAssets({ projectRepoName, testName, }) {
49
+ const logger = new logger_1.CustomLogger();
50
+ try {
51
+ if (!(0, uploader_1.checkIfResultsUploadAllowed)()) {
52
+ logger.log("Skipped uploading generated test video");
53
+ }
54
+ const { videoUrls, traceFiles } = await (0, uploader_1.uploadTestResultsUsingPrjRepo)({
55
+ projectRepoName,
56
+ testName,
57
+ });
58
+ const reporter = getReporter();
59
+ const message = {
60
+ type: "video",
61
+ videoUrls,
62
+ };
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
+ })),
71
+ ]);
72
+ }
73
+ catch (err) {
74
+ logger.error("Failed to report test results");
75
+ console.error(err);
76
+ }
77
+ }
78
+ async sendCurrentView(buffer) {
79
+ // 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);
82
+ const uploadDir = (0, uploader_1.getUploadPathForRun)(reporterConfig?.projectRepoName);
83
+ const files = await (0, r2_1.uploadDirectory)({
84
+ sourceDir: path_1.default.join(process.cwd(), "gen-assets"),
85
+ destinationDir: uploadDir,
86
+ uploadBucket: uploader_1.UPLOAD_BUCKET,
87
+ });
88
+ const filePath = Object.keys(files)[0];
89
+ const relativeFilePath = filePath.replace(path_1.default.join(process.cwd(), "gen-assets"), "");
90
+ const url = `${uploader_1.UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
91
+ await getReporter()?.report(new reporter_1.ProcessLogMessageBuilder({
92
+ message: JSON.stringify({ type: "current-view", url }),
93
+ }));
94
+ await promises_1.default.rmdir((process.cwd(), "gen-assets"), { recursive: true });
95
+ }
96
+ async sendMessage(message) {
97
+ const reporter = getReporter();
98
+ if (reporter) {
99
+ await reporter.report(new reporter_1.ProcessLogMessageBuilder({
100
+ message,
101
+ }));
102
+ }
103
+ }
104
+ }
105
+ exports.TestGenUpdatesReporter = TestGenUpdatesReporter;
@@ -1,3 +1,6 @@
1
+ export declare const UPLOAD_BUCKET = "test-report";
2
+ export declare const UPLOAD_DOMAIN = "https://reports.empirical.run";
3
+ export declare function getFullUploadPath(filePath: string, uploadDir: string): string;
1
4
  /**
2
5
  * Function to upload test results to R2 using the project repo name and test name.
3
6
  * This function uploads both the JSON summary of test results and associated video files.
@@ -8,12 +11,14 @@
8
11
  * @returns {string[]} returns.videoUrls - URLs of the uploaded video files.
9
12
  * @returns {string} returns.summaryUrl - URL of the uploaded summary JSON file.
10
13
  */
11
- export declare function uploadTestResultsUsingPrjtRepo({ projectRepoName, testName, }: {
14
+ export declare function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, }: {
12
15
  projectRepoName: string;
13
16
  testName: string;
14
17
  }): Promise<{
15
18
  videoUrls: string[];
16
19
  summaryUrl: string;
20
+ traceFiles: string[];
17
21
  }>;
22
+ export declare function getUploadPathForRun(projectRepoName: string): string;
18
23
  export declare function checkIfResultsUploadAllowed(): string | undefined;
19
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;GASG;AACH,wBAAsB,8BAA8B,CAAC,EACnD,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;CACpB,CAAC,CAgDD;AAED,wBAAgB,2BAA2B,uBAQ1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAMpE;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,CAoDD;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
@@ -3,18 +3,20 @@ 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.uploadTestResultsUsingPrjtRepo = void 0;
6
+ exports.checkIfResultsUploadAllowed = exports.getUploadPathForRun = exports.uploadTestResultsUsingPrjRepo = exports.getFullUploadPath = exports.UPLOAD_DOMAIN = exports.UPLOAD_BUCKET = void 0;
7
7
  const reporter_1 = require("@empiricalrun/reporter");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const r2_1 = require("./r2");
10
10
  // json summary of test results
11
11
  const TEST_RESULTS_DIR = "test-results";
12
- const UPLOAD_BUCKET = "test-report";
13
- const UPLOAD_DOMAIN = "https://reports.empirical.run"; // domain based on bucket mentioned above
12
+ exports.UPLOAD_BUCKET = "test-report";
13
+ exports.UPLOAD_DOMAIN = "https://reports.empirical.run"; // domain based on bucket mentioned above
14
+ const uploadId = crypto.randomUUID();
14
15
  function getFullUploadPath(filePath, uploadDir) {
15
16
  const relativeFilePath = filePath.replace(path_1.default.join(process.cwd(), TEST_RESULTS_DIR), "");
16
- return `${UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
17
+ return `${exports.UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
17
18
  }
19
+ exports.getFullUploadPath = getFullUploadPath;
18
20
  /**
19
21
  * Function to upload test results to R2 using the project repo name and test name.
20
22
  * This function uploads both the JSON summary of test results and associated video files.
@@ -25,21 +27,19 @@ function getFullUploadPath(filePath, uploadDir) {
25
27
  * @returns {string[]} returns.videoUrls - URLs of the uploaded video files.
26
28
  * @returns {string} returns.summaryUrl - URL of the uploaded summary JSON file.
27
29
  */
28
- async function uploadTestResultsUsingPrjtRepo({ projectRepoName, testName, }) {
29
- const uploadUniqueId = crypto.randomUUID();
30
+ async function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, }) {
30
31
  // project repo name is the github repo name
31
- // the folder names in r2 are the github repo name without the `-tests` suffix
32
- const uploadDir = `test-generation/${projectRepoName.replace("-tests", "")}/${uploadUniqueId}`;
32
+ const uploadDir = getUploadPathForRun(projectRepoName);
33
33
  const files = await (0, r2_1.uploadDirectory)({
34
34
  sourceDir: path_1.default.join(process.cwd(), TEST_RESULTS_DIR),
35
35
  destinationDir: uploadDir,
36
- uploadBucket: UPLOAD_BUCKET,
36
+ uploadBucket: exports.UPLOAD_BUCKET,
37
37
  });
38
38
  const fileNames = Object.keys(files); // fileNames are absolute paths of the input files
39
39
  const defaultLocation = path_1.default.join(process.cwd(), "test-results", "summary.json");
40
40
  const results = (0, reporter_1.parseJsonReport)(defaultLocation);
41
41
  const flatTestsList = (0, reporter_1.getFlattenedTestList)(results.suites);
42
- const testVideos = [];
42
+ const testAttachmentPaths = [];
43
43
  for (const test of flatTestsList) {
44
44
  if (test.title === testName) {
45
45
  if (test.tests[0]) {
@@ -47,7 +47,7 @@ async function uploadTestResultsUsingPrjtRepo({ projectRepoName, testName, }) {
47
47
  // results array is basically made by retries
48
48
  for (const attachments of test.tests[0].results[0].attachments) {
49
49
  if (attachments.path) {
50
- testVideos.push(attachments.path);
50
+ testAttachmentPaths.push(attachments.path);
51
51
  }
52
52
  }
53
53
  }
@@ -57,13 +57,20 @@ async function uploadTestResultsUsingPrjtRepo({ projectRepoName, testName, }) {
57
57
  // current assumption
58
58
  // - test gen will only run on a single spec file
59
59
  // - the video files are of the format - <some-directory>/video.webm
60
- const videoFiles = fileNames.filter((fileName) => fileName.endsWith(".webm") && testVideos.includes(fileName));
60
+ const videoFiles = fileNames.filter((fileName) => fileName.endsWith(".webm") && testAttachmentPaths.includes(fileName));
61
+ const traceFiles = fileNames.filter((fileName) => fileName.endsWith(".zip") && testAttachmentPaths.includes(fileName));
61
62
  return {
62
63
  videoUrls: videoFiles.map((fileName) => getFullUploadPath(fileName, uploadDir)),
63
64
  summaryUrl: getFullUploadPath("/test-results/summary.json", uploadDir),
65
+ traceFiles: traceFiles.map((fileName) => getFullUploadPath(fileName, uploadDir)),
64
66
  };
65
67
  }
66
- exports.uploadTestResultsUsingPrjtRepo = uploadTestResultsUsingPrjtRepo;
68
+ exports.uploadTestResultsUsingPrjRepo = uploadTestResultsUsingPrjRepo;
69
+ function getUploadPathForRun(projectRepoName) {
70
+ const uploadDir = `test-generation/${projectRepoName.replace("-tests", "")}/${uploadId}`;
71
+ return uploadDir;
72
+ }
73
+ exports.getUploadPathForRun = getUploadPathForRun;
67
74
  function checkIfResultsUploadAllowed() {
68
75
  // TODO: check for valid R2 credentials
69
76
  // check for project repo name, and r2 creds
@@ -1 +1 @@
1
- {"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../../src/uploader/r2.ts"],"names":[],"mappings":"AAqBA,UAAU,OAAO;IACf,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAgGD,wBAAsB,eAAe,CAAC,EACpC,SAAS,EACT,cAAc,EACd,YAAY,GACb,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,OAAO,CAAC,CAWnB"}
1
+ {"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../../src/uploader/r2.ts"],"names":[],"mappings":"AAqBA,UAAU,OAAO;IACf,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAsGD,wBAAsB,eAAe,CAAC,EACpC,SAAS,EACT,cAAc,EACd,YAAY,GACb,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,OAAO,CAAC,CAWnB"}
@@ -61,7 +61,6 @@ const run = async (config) => {
61
61
  },
62
62
  });
63
63
  const files = getFileList(config.sourceDir);
64
- const mime = (await import("mime")).default;
65
64
  await Promise.all(files.map(async (file) => {
66
65
  console.log(file);
67
66
  const fileStream = fs.readFileSync(file);
@@ -73,7 +72,14 @@ const run = async (config) => {
73
72
  if (fileKey.includes(".gitkeep"))
74
73
  return;
75
74
  console.log(fileKey);
76
- const mimeType = mime.getType(file);
75
+ let mimeType = "application/octet-stream";
76
+ try {
77
+ const mime = (await import("mime")).default;
78
+ mimeType = mime.getType(file) || "application/octet-stream";
79
+ }
80
+ catch (err) {
81
+ console.warn("Failed to get mime type for file", file, err);
82
+ }
77
83
  const uploadParams = {
78
84
  Bucket: config.bucket,
79
85
  Key: fileKey,
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAGA,wBAAgB,GAAG,CACjB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAGA,wBAAgB,GAAG,CACjB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CA2BjB"}
@@ -7,25 +7,27 @@ exports.cmd = void 0;
7
7
  const child_process_1 = require("child_process");
8
8
  const process_1 = __importDefault(require("process"));
9
9
  function cmd(command, options) {
10
- let lastLog = "";
10
+ let errorLogs = [];
11
11
  return new Promise((resolveFunc, rejectFunc) => {
12
12
  let p = (0, child_process_1.spawn)(command[0], command.slice(1), {
13
13
  env: { ...process_1.default.env, ...options.env },
14
14
  });
15
15
  p.stdout.on("data", (x) => {
16
16
  const log = x.toString();
17
+ if (log.includes("Error")) {
18
+ errorLogs.push(log);
19
+ }
17
20
  process_1.default.stdout.write(log);
18
- lastLog = log;
19
21
  });
20
22
  p.stderr.on("data", (x) => {
21
23
  const log = x.toString();
22
24
  process_1.default.stderr.write(x.toString());
23
- lastLog = log;
25
+ errorLogs.push(log);
24
26
  });
25
27
  p.on("exit", (code) => {
26
28
  if (code != 0) {
27
29
  // assuming last log is the error message before exiting
28
- rejectFunc(lastLog);
30
+ rejectFunc(errorLogs.slice(-3).join("\n"));
29
31
  }
30
32
  else {
31
33
  resolveFunc(code);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.22.11",
3
+ "version": "0.23.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"