@dev-blinq/cucumber_client 1.0.1475-dev → 1.0.1475-stage

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 (47) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +49 -49
  2. package/bin/assets/scripts/recorder.js +87 -34
  3. package/bin/assets/scripts/snapshot_capturer.js +10 -17
  4. package/bin/assets/scripts/unique_locators.js +78 -28
  5. package/bin/assets/templates/_hooks_template.txt +6 -2
  6. package/bin/assets/templates/utils_template.txt +16 -16
  7. package/bin/client/code_cleanup/codemod/find_harcoded_locators.js +173 -0
  8. package/bin/client/code_cleanup/codemod/fix_hardcoded_locators.js +247 -0
  9. package/bin/client/code_cleanup/utils.js +16 -7
  10. package/bin/client/code_gen/code_inversion.js +125 -1
  11. package/bin/client/code_gen/duplication_analysis.js +2 -1
  12. package/bin/client/code_gen/function_signature.js +8 -0
  13. package/bin/client/code_gen/index.js +4 -0
  14. package/bin/client/code_gen/page_reflection.js +90 -9
  15. package/bin/client/code_gen/playwright_codeget.js +173 -77
  16. package/bin/client/codemod/find_harcoded_locators.js +173 -0
  17. package/bin/client/codemod/fix_hardcoded_locators.js +247 -0
  18. package/bin/client/codemod/index.js +8 -0
  19. package/bin/client/codemod/locators_array/find_misstructured_elements.js +148 -0
  20. package/bin/client/codemod/locators_array/fix_misstructured_elements.js +144 -0
  21. package/bin/client/codemod/locators_array/index.js +114 -0
  22. package/bin/client/codemod/types.js +1 -0
  23. package/bin/client/cucumber/feature.js +4 -17
  24. package/bin/client/cucumber/steps_definitions.js +17 -12
  25. package/bin/client/recorderv3/bvt_init.js +310 -0
  26. package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
  27. package/bin/client/recorderv3/constants.js +45 -0
  28. package/bin/client/recorderv3/implemented_steps.js +2 -0
  29. package/bin/client/recorderv3/index.js +3 -293
  30. package/bin/client/recorderv3/mixpanel.js +39 -0
  31. package/bin/client/recorderv3/services.js +839 -142
  32. package/bin/client/recorderv3/step_runner.js +36 -7
  33. package/bin/client/recorderv3/step_utils.js +316 -98
  34. package/bin/client/recorderv3/update_feature.js +85 -37
  35. package/bin/client/recorderv3/utils.js +80 -0
  36. package/bin/client/recorderv3/wbr_entry.js +61 -0
  37. package/bin/client/recording.js +1 -0
  38. package/bin/client/types/locators.js +2 -0
  39. package/bin/client/upload-service.js +2 -0
  40. package/bin/client/utils/app_dir.js +21 -0
  41. package/bin/client/utils/socket_logger.js +100 -125
  42. package/bin/index.js +5 -0
  43. package/package.json +21 -6
  44. package/bin/client/recorderv3/app_dir.js +0 -23
  45. package/bin/client/recorderv3/network.js +0 -299
  46. package/bin/client/recorderv3/scriptTest.js +0 -5
  47. package/bin/client/recorderv3/ws_server.js +0 -72
@@ -0,0 +1,144 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { parse } from "@babel/parser";
4
+ import traverseImport from "@babel/traverse";
5
+ import * as t from "@babel/types";
6
+ // Normalize babel traverse to handle both CJS (callable export) and ESM (default export)
7
+ const traverse = typeof traverseImport === "function"
8
+ ? traverseImport
9
+ : (traverseImport.default ?? traverseImport);
10
+ function isNumericKey(key) {
11
+ return typeof key === "string" && /^\d+$/.test(key);
12
+ }
13
+ function propName(prop) {
14
+ if (!t.isObjectProperty(prop))
15
+ return undefined;
16
+ const k = prop.key;
17
+ if (t.isIdentifier(k))
18
+ return k.name;
19
+ if (t.isStringLiteral(k))
20
+ return k.value;
21
+ if (t.isNumericLiteral(k))
22
+ return String(k.value);
23
+ return undefined;
24
+ }
25
+ function stringifyValue(node, source) {
26
+ if (typeof node.start === "number" && typeof node.end === "number") {
27
+ return source.slice(node.start, node.end);
28
+ }
29
+ return JSON.stringify(null);
30
+ }
31
+ function leadingWhitespace(text, index) {
32
+ const lineStart = text.lastIndexOf("\n", index - 1) + 1;
33
+ const match = text.slice(lineStart, index).match(/^[ \t]*/);
34
+ return match ? match[0] : "";
35
+ }
36
+ function buildLocatorsArray(numericProps, source, indent) {
37
+ const elements = numericProps
38
+ .sort((a, b) => Number(propName(a)) - Number(propName(b)))
39
+ .map((p) => `${indent} ${stringifyValue(p.value, source)}`);
40
+ if (elements.length === 0)
41
+ return "locators: []";
42
+ return [indent + "locators: [", elements.join(",\n"), indent + "]"].join("\n");
43
+ }
44
+ function fixJsPreserveFormat(code, logger) {
45
+ let touched = false;
46
+ const replacements = [];
47
+ const ast = parse(code, {
48
+ sourceType: "module",
49
+ plugins: ["jsx", "typescript", "classProperties", "optionalChaining", "nullishCoalescingOperator", "topLevelAwait"],
50
+ ranges: true,
51
+ });
52
+ traverse(ast, {
53
+ ObjectExpression(pathObj) {
54
+ const props = pathObj.node.properties.filter(t.isObjectProperty);
55
+ if (!props.length)
56
+ return;
57
+ const names = props.map(propName).filter(Boolean);
58
+ const hasElementMeta = names.some((n) => n === "element_name" || n === "element_key");
59
+ if (!hasElementMeta)
60
+ return;
61
+ const hasLocators = names.includes("locators");
62
+ const numericProps = props.filter((p) => isNumericKey(propName(p)));
63
+ if (hasLocators || numericProps.length === 0)
64
+ return;
65
+ if (typeof pathObj.node.start !== "number" || typeof pathObj.node.end !== "number")
66
+ return;
67
+ const leading = leadingWhitespace(code, pathObj.node.start);
68
+ const otherProps = props.filter((p) => !numericProps.includes(p));
69
+ const otherTexts = otherProps.map((p) => code.slice(p.start ?? 0, p.end ?? 0));
70
+ const locatorsText = buildLocatorsArray(numericProps, code, `${leading} `);
71
+ const pieces = [locatorsText, ...otherTexts];
72
+ const baseIndent = `${leading} `;
73
+ const normalized = pieces.map((p) => (p.startsWith(baseIndent) ? p : baseIndent + p));
74
+ const newObjectText = "{\n" + normalized.join(",\n") + "\n" + leading + "}";
75
+ replacements.push({ start: pathObj.node.start, end: pathObj.node.end, text: newObjectText });
76
+ touched = true;
77
+ },
78
+ });
79
+ if (!touched)
80
+ return code;
81
+ let out = code;
82
+ replacements
83
+ .sort((a, b) => b.start - a.start)
84
+ .forEach(({ start, end, text }) => {
85
+ out = out.slice(0, start) + text + out.slice(end);
86
+ });
87
+ return out;
88
+ }
89
+ function fixJson(content, logger) {
90
+ let data;
91
+ try {
92
+ data = JSON.parse(content);
93
+ }
94
+ catch {
95
+ return content;
96
+ }
97
+ let touched = false;
98
+ const visit = (node, logger) => {
99
+ if (!node || typeof node !== "object")
100
+ return;
101
+ if (Array.isArray(node))
102
+ return node.forEach((n) => visit(n, logger));
103
+ const record = node;
104
+ const keys = Object.keys(record);
105
+ const hasMeta = "element_name" in record || "element_key" in record;
106
+ const hasLoc = "locators" in record;
107
+ const nums = keys.filter(isNumericKey);
108
+ if (hasMeta && !hasLoc && nums.length) {
109
+ record.locators = nums.sort((a, b) => Number(a) - Number(b)).map((k) => record[k]);
110
+ nums.forEach((k) => delete record[k]);
111
+ touched = true;
112
+ }
113
+ Object.values(record).forEach((v) => visit(v, logger));
114
+ };
115
+ visit(data, logger);
116
+ return touched ? `${JSON.stringify(data, null, 2)}\n` : content;
117
+ }
118
+ /**
119
+ * Fixes all files referenced in `issues` under `root`.
120
+ * `issues` is the array returned by findIssues().
121
+ * Returns an array of { filePath, original, fixed } for files that changed.
122
+ */
123
+ export async function fixIssues(issues, root, logger) {
124
+ // Deduplicate — multiple issues can point to the same file
125
+ const filePaths = [...new Set(issues.map((i) => i.file))];
126
+ const changes = [];
127
+ for (const filePath of filePaths) {
128
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(root, filePath);
129
+ const ext = path.extname(absolutePath).toLowerCase();
130
+ const original = await fs.readFile(absolutePath, "utf8");
131
+ let fixed = original;
132
+ if (ext === ".json") {
133
+ fixed = fixJson(original, logger);
134
+ }
135
+ else if (ext === ".mjs" || ext === ".js") {
136
+ fixed = fixJsPreserveFormat(original, logger);
137
+ }
138
+ if (fixed === original)
139
+ continue;
140
+ await fs.writeFile(absolutePath, fixed, "utf8");
141
+ changes.push({ filePath: absolutePath, original, fixed });
142
+ }
143
+ return changes;
144
+ }
@@ -0,0 +1,114 @@
1
+ import { findIssues } from "./find_misstructured_elements.js";
2
+ import { fixIssues } from "./fix_misstructured_elements.js";
3
+ /**
4
+ * Converts legacy locator objects that use numeric keys (e.g., `{ 0: {...}, 1: {...} }`)
5
+ * into a `locators` array, across all JS/MJS/JSON files under a codebase root.
6
+ * The codemod is idempotent, preserves formatting where possible, and reports timing metrics.
7
+ *
8
+ * @param clonedRoot Absolute path to the cloned project directory to scan and fix.
9
+ * @param logger Logger instance used for progress, issue, and summary output.
10
+ * @returns Structured codemod run result containing issues found, file changes written, and metrics.
11
+ *
12
+ * @example Running the codemod standalone:
13
+ * ```ts
14
+ * import { locatorObjectToArray } from "./code_cleanup/codemod/locators_array";
15
+ * import { consoleLogger } from "./logger";
16
+ *
17
+ * const result = await locatorObjectToArray("/tmp/repo", consoleLogger);
18
+ * console.log(result.message); // e.g., "Fixed 3 file(s)"
19
+ * ```
20
+ *
21
+ * @example Running multiple codemods in sequence:
22
+ * ```ts
23
+ * const codemods = [locatorObjectToArray]; // add other codemods as needed
24
+ * for (const mod of codemods) {
25
+ * const outcome = await mod("/tmp/repo", consoleLogger);
26
+ * if (!outcome.success) throw new Error(outcome.message);
27
+ * }
28
+ * ```
29
+ */
30
+ async function locatorObjectToArray(clonedRoot, logger) {
31
+ const start = Date.now();
32
+ logger.info(`Scanning ${clonedRoot} for misstructured locator objects...`);
33
+ const findStart = Date.now();
34
+ const issues = [];
35
+ try {
36
+ issues.push(...(await findIssues(clonedRoot, logger)));
37
+ }
38
+ catch (error) {
39
+ const metricsOnFindError = {
40
+ issueCount: 0,
41
+ affectedFiles: 0,
42
+ findDurationMs: Date.now() - findStart,
43
+ fixDurationMs: 0,
44
+ filesFixed: 0,
45
+ wasAffected: false,
46
+ totalDurationMs: Date.now() - start,
47
+ };
48
+ logger.error(`Error during issue finding: ${error.message}`);
49
+ return {
50
+ name: "Locator Object to Array",
51
+ description: "Converts locator objects with numeric keys to arrays.",
52
+ success: false,
53
+ message: `Failed to find issues: ${error.message}`,
54
+ details: { issues: [], changes: [], metrics: metricsOnFindError },
55
+ };
56
+ }
57
+ const metricsBase = {
58
+ issueCount: issues.length,
59
+ affectedFiles: [...new Set(issues.map((i) => i.file))].length,
60
+ findDurationMs: Date.now() - findStart,
61
+ fixDurationMs: 0,
62
+ filesFixed: 0,
63
+ wasAffected: issues.length > 0,
64
+ totalDurationMs: 0,
65
+ };
66
+ if (issues.length === 0) {
67
+ metricsBase.totalDurationMs = Date.now() - start;
68
+ logger.info("No issues — nothing to fix");
69
+ logger.info(`Summary: issues=0 files=0 fixed=0 find=${metricsBase.findDurationMs}ms ` +
70
+ `fix=${metricsBase.fixDurationMs}ms total=${metricsBase.totalDurationMs}ms`);
71
+ return {
72
+ name: "Locator Object to Array",
73
+ description: "Converts locator objects with numeric keys to arrays.",
74
+ success: true,
75
+ message: "No issues found",
76
+ details: { issues: [], changes: [], metrics: metricsBase },
77
+ };
78
+ }
79
+ logger.info(`Found ${issues.length} issue(s) across ${metricsBase.affectedFiles} file(s)`);
80
+ issues.forEach((i) => logger.info(` issue: ${i.file}:${i.line} — ${i.element} (${i.numericCount} numeric keys)`));
81
+ const fixStart = Date.now();
82
+ const changes = [];
83
+ try {
84
+ changes.push(...(await fixIssues(issues, clonedRoot, logger)));
85
+ }
86
+ catch (error) {
87
+ metricsBase.fixDurationMs = Date.now() - fixStart;
88
+ metricsBase.totalDurationMs = Date.now() - start;
89
+ logger.error(`Error during issue fixing: ${error.message}`);
90
+ return {
91
+ name: "Locator Object to Array",
92
+ description: "Converts locator objects with numeric keys to arrays.",
93
+ success: false,
94
+ message: `Failed to fix issues: ${error.message}`,
95
+ details: { issues, changes: [], metrics: metricsBase },
96
+ };
97
+ }
98
+ metricsBase.fixDurationMs = Date.now() - fixStart;
99
+ metricsBase.filesFixed = changes.length;
100
+ metricsBase.totalDurationMs = Date.now() - start;
101
+ logger.info(`Fixed ${changes.length} file(s) in ${metricsBase.fixDurationMs}ms`);
102
+ changes.forEach((c) => logger.info(` fixed: ${c.filePath}`));
103
+ logger.info(`Summary: issues=${metricsBase.issueCount} files=${metricsBase.affectedFiles} ` +
104
+ `fixed=${metricsBase.filesFixed} find=${metricsBase.findDurationMs}ms ` +
105
+ `fix=${metricsBase.fixDurationMs}ms total=${metricsBase.totalDurationMs}ms`);
106
+ return {
107
+ name: "Locator Object to Array",
108
+ description: "Converts locator objects with numeric keys to arrays.",
109
+ success: true,
110
+ message: `Fixed ${changes.length} file(s)`,
111
+ details: { issues, changes, metrics: metricsBase },
112
+ };
113
+ }
114
+ export { locatorObjectToArray };
@@ -0,0 +1 @@
1
+ export {};
@@ -6,7 +6,6 @@ import os from "os";
6
6
  import path from "path";
7
7
  import { parseStepTextParameters, toCucumberExpression, unEscapeNonPrintables } from "./utils.js";
8
8
  import stream from "stream";
9
- import { testStringForRegex } from "../recorderv3/update_feature.js";
10
9
  import { generateTestData } from "./feature_data.js";
11
10
  import socketLogger from "../utils/socket_logger.js";
12
11
  class DataTable {
@@ -490,8 +489,8 @@ const scenarioResolution = async (featureFilePath) => {
490
489
  let result = generateTestData(featureFilePath);
491
490
  if (result.changed) {
492
491
  fs.writeFileSync(tmpFeatureFilePath, result.newContent);
493
- console.log("Fake data was generated for this scenario");
494
- console.log("Variables:");
492
+ socketLogger.info("Generated fake data for feature", undefined, "scenarioResolution");
493
+ socketLogger.info("Variables generated:", result.variables, "scenarioResolution");
495
494
  for (let key in result.variables) {
496
495
  console.log(`${key}: ${result.variables[key].fake}`);
497
496
  }
@@ -503,8 +502,7 @@ const scenarioResolution = async (featureFilePath) => {
503
502
  fs.copyFileSync(featureFilePath, tmpFeatureFilePath);
504
503
  }
505
504
  const writable = new stream.Writable({
506
- write: function (chunk, encoding, next) {
507
- //console.log(chunk.toString());
505
+ write: (chunk, encoding, next) => {
508
506
  next();
509
507
  },
510
508
  });
@@ -521,23 +519,12 @@ const scenarioResolution = async (featureFilePath) => {
521
519
  // load the support code upfront
522
520
  const support = await loadSupport(runConfiguration, environment);
523
521
  // run cucumber, using the support code we loaded already
524
- await runCucumber({ ...runConfiguration, support }, environment, function (event) {
525
- // if (event.source) {
526
- // scenarioInfo.source = event.source.data;
527
- // }
528
- // if (event.pickle && event.pickle.name === scenarioName) {
529
- // scenarioInfo.pickle = event.pickle;
530
- // }
522
+ await runCucumber({ ...runConfiguration, support }, environment, (event) => {
531
523
  if (event.gherkinDocument) {
532
524
  gherkinDocument = event.gherkinDocument;
533
525
  }
534
- //console.log(event);
535
- //console.log(JSON.stringify(event, null, 2));
536
- // console.log("");
537
526
  });
538
527
  const feature = new Feature(gherkinDocument, featureFileContent);
539
- //const scenario = feature.getScenario(scenarioName);
540
- //return scenario;
541
528
  return feature;
542
529
  } finally {
543
530
  try {
@@ -36,6 +36,19 @@ class StepsDefinitions {
36
36
  // }
37
37
  // });
38
38
  const { expressions, methods } = codePage;
39
+
40
+ if (codePage.fileContent.includes('from "./utils.mjs"')) {
41
+ const filePath = path.join(
42
+ this.baseFolder,
43
+ this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
44
+ "step_definitions",
45
+ "utils.mjs"
46
+ );
47
+ const utilsCodePage = new CodePage(filePath);
48
+ utilsCodePage.generateModel();
49
+ methods.push(...utilsCodePage.methods);
50
+ }
51
+
39
52
  for (let i = 0; i < expressions.length; i++) {
40
53
  const expression = expressions[i];
41
54
  const pattern = expression.pattern;
@@ -87,18 +100,10 @@ class StepsDefinitions {
87
100
  this.initPage(codePage, mjsFile);
88
101
  }
89
102
  let stepCount = Object.keys(this.steps).length;
90
- if (this.steps["Before"]) {
91
- stepCount--;
92
- }
93
- if (this.steps["After"]) {
94
- stepCount--;
95
- }
96
- if (this.steps["BeforeAll"]) {
97
- stepCount--;
98
- }
99
- if (this.steps["AfterAll"]) {
100
- stepCount--;
101
- }
103
+ if (this.steps["Before"]) stepCount--;
104
+ if (this.steps["After"]) stepCount--;
105
+ if (this.steps["BeforeAll"]) stepCount--;
106
+ if (this.steps["AfterAll"]) stepCount--;
102
107
  if (print) {
103
108
  logger.info("total steps definitions found", stepCount);
104
109
  }
@@ -0,0 +1,310 @@
1
+ import { io } from "socket.io-client";
2
+ import { BVTRecorder } from "./bvt_recorder.js";
3
+ import { compareWithScenario } from "../code_gen/duplication_analysis.js";
4
+ import { getAppDataDir } from "../utils/app_dir.js";
5
+ import { readdir } from "fs/promises";
6
+ import socketLogger, { getErrorMessage, responseSize } from "../utils/socket_logger.js";
7
+ import { MIXPANEL_EVENTS, mixpanelTrackEvent } from "./mixpanel.js";
8
+ const port = process.env.EDITOR_PORT || 3003;
9
+ const WS_URL = process.env.WORKER_WS_SERVER_URL || "http://localhost:" + port;
10
+ const userId = process.env.USER_ID || "";
11
+ const SocketIOEvents = {
12
+ REQUEST: "request",
13
+ RESPONSE: "response",
14
+ CONNECT: "connect",
15
+ DISCONNECT: "disconnect",
16
+ CREATE_ROOM: "createRoom",
17
+ JOIN_ROOM: "joinRoom",
18
+ };
19
+ class PromisifiedSocketServer {
20
+ socket;
21
+ routes;
22
+ constructor(socket, routes) {
23
+ this.socket = socket;
24
+ this.routes = routes;
25
+ }
26
+ init() {
27
+ this.socket.on(SocketIOEvents.REQUEST, async (data) => {
28
+ const { event, input, id, roomId, socketId } = data;
29
+ if (event !== "recorderWindow.getCurrentChromiumPath") {
30
+ socketLogger.info("Received request", { event, input, id, roomId, socketId });
31
+ }
32
+ try {
33
+ const handler = this.routes[event];
34
+ if (!handler) {
35
+ socketLogger.error(`No handler found for event: ${event}`, undefined, event);
36
+ return;
37
+ }
38
+ const response = await handler(input);
39
+ if (event !== "recorderWindow.getCurrentChromiumPath") {
40
+ socketLogger.info(`Sending response for ${event}, ${responseSize(response)} bytes`);
41
+ }
42
+ this.socket.emit(SocketIOEvents.RESPONSE, { id, value: response, roomId, socketId });
43
+ }
44
+ catch (error) {
45
+ socketLogger.error("Error handling request", {
46
+ input,
47
+ id,
48
+ roomId,
49
+ socketId,
50
+ error: error instanceof Error ? `${error.message}\n${error.stack}` : error,
51
+ }, event);
52
+ this.socket.emit(SocketIOEvents.RESPONSE, {
53
+ id,
54
+ error: {
55
+ message: error?.message,
56
+ code: error?.code,
57
+ info: error?.info,
58
+ stack: error?.stack,
59
+ },
60
+ roomId,
61
+ socketId,
62
+ });
63
+ }
64
+ });
65
+ }
66
+ }
67
+ const timeOutForFunction = async (promise, timeout = 5000) => {
68
+ const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(), timeout));
69
+ try {
70
+ const res = await Promise.race([promise, timeoutPromise]);
71
+ return res;
72
+ }
73
+ catch (error) {
74
+ socketLogger.error(error, undefined, "timeOutForFunction");
75
+ throw error;
76
+ }
77
+ };
78
+ const CLIENT_IDENTIFIER = "cucumber_client/bvt_recorder";
79
+ async function BVTRecorderInit({ envName, projectDir, roomId, TOKEN, socket = null }) {
80
+ console.log(`Connecting to ${WS_URL}`);
81
+ socket = socket || io(WS_URL);
82
+ socketLogger.init(socket, { context: "BVTRecorder", eventName: "BVTRecorder.log" });
83
+ socket.on(SocketIOEvents.CONNECT, () => {
84
+ socketLogger.info(`${roomId} Connected to BVTRecorder server`);
85
+ });
86
+ socket.on(SocketIOEvents.DISCONNECT, (reason) => {
87
+ socketLogger.info(`${roomId} Disconnected from server: ${reason}`);
88
+ });
89
+ socket.emit(SocketIOEvents.JOIN_ROOM, { id: roomId, window: CLIENT_IDENTIFIER });
90
+ const recorder = new BVTRecorder({
91
+ envName,
92
+ projectDir,
93
+ TOKEN,
94
+ sendEvent: (event, data) => {
95
+ socketLogger.info("Sending event", { event, data, roomId });
96
+ socket.emit(event, data, roomId);
97
+ },
98
+ logger: socketLogger,
99
+ userId,
100
+ });
101
+ // emit connected event for every 50 ms until connection_ack message is recieved
102
+ let connected = false;
103
+ const interval = setInterval(() => {
104
+ if (connected) {
105
+ clearInterval(interval);
106
+ return;
107
+ }
108
+ socket.emit("BVTRecorder.connected", { roomId, window: "cucumber_client/bvt_recorder" }, roomId);
109
+ }, 50);
110
+ const promisifiedSocketServer = new PromisifiedSocketServer(socket, {
111
+ "recorderWindow.connectionAck": async (input) => {
112
+ mixpanelTrackEvent(MIXPANEL_EVENTS.RECORDER_CONNECTED, userId);
113
+ connected = true;
114
+ clearInterval(interval);
115
+ },
116
+ "recorderWindow.openBrowser": async (input) => {
117
+ return recorder
118
+ .openBrowser(input)
119
+ .then(() => {
120
+ mixpanelTrackEvent(MIXPANEL_EVENTS.CHROMIUM_LOADED, userId);
121
+ socketLogger.info("BVTRecorder.browserOpened");
122
+ socket.emit("BVTRecorder.browserOpened", { roomId, window: "cucumber_client/bvt_recorder" });
123
+ })
124
+ .catch((e) => {
125
+ socketLogger.error(`Error opening browser: ${getErrorMessage(e)}`, undefined, "recorderWindow.openBrowser");
126
+ socket.emit("BVTRecorder.browserLaunchFailed", { roomId, window: "cucumber_client/bvt_recorder" });
127
+ });
128
+ },
129
+ "recorderWindow.closeBrowser": async (input) => {
130
+ return recorder.closeBrowser(input);
131
+ },
132
+ "recorderWindow.reOpenBrowser": async (input) => {
133
+ return recorder
134
+ .reOpenBrowser(input)
135
+ .then(() => {
136
+ socketLogger.info("BVTRecorder.browserOpened");
137
+ socket.emit("BVTRecorder.browserOpened", null, roomId);
138
+ })
139
+ .catch((e) => {
140
+ socketLogger.error(`Error reopening browser: ${getErrorMessage(e)}`, undefined, "recorderWindow.reOpenBrowser");
141
+ socket.emit("BVTRecorder.browserLaunchFailed", null, roomId);
142
+ });
143
+ },
144
+ "recorderWindow.startRecordingInput": async (input) => {
145
+ return timeOutForFunction(recorder.startRecordingInput(input));
146
+ },
147
+ "recorderWindow.stopRecordingInput": async (input) => {
148
+ return timeOutForFunction(recorder.stopRecordingInput(input));
149
+ },
150
+ "recorderWindow.startRecordingText": async (input) => {
151
+ // console.log("--- {{ }} -- : recorderWindow.startRecordingText", input);
152
+ return timeOutForFunction(recorder.startRecordingText(input));
153
+ },
154
+ "recorderWindow.stopRecordingText": async (input) => {
155
+ return timeOutForFunction(recorder.stopRecordingText(input));
156
+ },
157
+ "recorderWindow.startRecordingContext": async (input) => {
158
+ return timeOutForFunction(recorder.startRecordingContext(input));
159
+ },
160
+ "recorderWindow.stopRecordingContext": async (input) => {
161
+ return timeOutForFunction(recorder.stopRecordingContext(input));
162
+ },
163
+ "recorderWindow.runStep": async (input) => {
164
+ return recorder.runStep(input);
165
+ },
166
+ "recorderWindow.saveScenario": async (input) => {
167
+ return recorder.saveScenario(input);
168
+ },
169
+ "recorderWindow.getImplementedSteps": async (input) => {
170
+ return (await recorder.getImplementedSteps(input)).implementedSteps;
171
+ },
172
+ "recorderWindow.getImplementedScenarios": async (input) => {
173
+ return (await recorder.getImplementedSteps(input)).scenarios;
174
+ },
175
+ "recorderWindow.getCurrentChromiumPath": async () => {
176
+ return recorder.getCurrentChromiumPath();
177
+ },
178
+ "recorderWindow.overwriteTestData": async (input) => {
179
+ return await recorder.overwriteTestData(input);
180
+ },
181
+ "recorderWindow.generateStepName": async (input) => {
182
+ return recorder.generateStepName(input);
183
+ },
184
+ "recorderWindow.getFeatureAndScenario": async (input) => {
185
+ return recorder.generateScenarioAndFeatureNames(input);
186
+ },
187
+ "recorderWindow.generateCommandName": async (input) => {
188
+ return recorder.generateCommandName(input);
189
+ },
190
+ "recorderWindow.loadTestData": async (input) => {
191
+ return recorder.loadTestData(input);
192
+ },
193
+ "recorderWindow.discard": async (input) => {
194
+ return await recorder.discardTestData(input);
195
+ },
196
+ "recorderWindow.addToTestData": async (input) => {
197
+ return await recorder.addToTestData(input);
198
+ },
199
+ "recorderWindow.getScenarios": async () => {
200
+ return recorder.getScenarios();
201
+ },
202
+ "recorderWindow.setShouldTakeScreenshot": async (input) => {
203
+ return recorder.setShouldTakeScreenshot(input);
204
+ },
205
+ "recorderWindow.compareWithScenario": async ({ projectDir, scenario }, roomId) => {
206
+ return await compareWithScenario(getAppDataDir(projectDir), scenario);
207
+ },
208
+ "recorderWindow.getCommandsForImplementedStep": async (input) => {
209
+ return recorder.getCommandsForImplementedStep(input);
210
+ },
211
+ "recorderWindow.getNumberOfOccurrences": async (input) => {
212
+ return recorder.getNumberOfOccurrences(input);
213
+ },
214
+ "recorderWindow.getFakeParams": async ({ parametersMap }) => {
215
+ return recorder.fakeParams(parametersMap);
216
+ },
217
+ "recorderWindow.abortExecution": async (_) => {
218
+ return recorder.abortExecution();
219
+ },
220
+ "recorderWindow.pauseExecution": async (input) => {
221
+ return recorder.pauseExecution(input);
222
+ },
223
+ "recorderWindow.resumeExecution": async (input) => {
224
+ return recorder.resumeExecution(input);
225
+ },
226
+ "recorderWindow.loadExistingScenario": async (input) => {
227
+ return recorder.loadExistingScenario(input);
228
+ },
229
+ "recorderWindow.findRelatedTextInAllFrames": async (input) => {
230
+ return recorder.findRelatedTextInAllFrames(input);
231
+ },
232
+ "recorderWindow.getReportFolder": async (input) => {
233
+ return recorder.getReportFolder();
234
+ },
235
+ "recorderWindow.getSnapshotFiles": async (input) => {
236
+ const snapshotFolder = recorder.getSnapshotFolder();
237
+ if (snapshotFolder) {
238
+ const files = await readdir(snapshotFolder);
239
+ const ymlFiles = files.filter((file) => file.endsWith(".yml") || file.endsWith(".yaml"));
240
+ return { folder: snapshotFolder, files: ymlFiles };
241
+ }
242
+ else
243
+ return { folder: null, files: [] };
244
+ },
245
+ "recorderWindow.getCurrentPageTitle": async () => {
246
+ return await recorder.getCurrentPageTitle();
247
+ },
248
+ "recorderWindow.getCurrentPageUrl": async () => {
249
+ return recorder.getCurrentPageUrl();
250
+ },
251
+ "recorderWindow.sendAriaSnapshot": async (input) => {
252
+ const snapshot = input?.snapshot;
253
+ const deselect = input?.deselect;
254
+ if (deselect === true) {
255
+ return await recorder.deselectAriaElements();
256
+ }
257
+ if (snapshot !== null) {
258
+ return await recorder.processAriaSnapshot(snapshot);
259
+ }
260
+ },
261
+ "recorderWindow.revertMode": async () => {
262
+ await recorder.revertMode();
263
+ },
264
+ "recorderWindow.setMode": async (input) => {
265
+ const mode = input?.mode;
266
+ return recorder.setMode(mode);
267
+ },
268
+ "recorderWindow.getStepsAndCommandsForScenario": async (input) => {
269
+ return await recorder.getStepsAndCommandsForScenario(input);
270
+ },
271
+ "recorderWindow.initExecution": async (input) => {
272
+ return await recorder.initExecution(input);
273
+ },
274
+ "recorderWindow.cleanupExecution": async (input) => {
275
+ return await recorder.cleanupExecution(input);
276
+ },
277
+ "recorderWindow.resetExecution": async (input) => {
278
+ return await recorder.resetExecution(input);
279
+ },
280
+ "recorderWindow.stopRecordingNetwork": async (input) => {
281
+ return recorder.stopRecordingNetwork(input);
282
+ },
283
+ "recorderWindow.cleanup": async (input) => {
284
+ return recorder.cleanup(input);
285
+ },
286
+ "recorderWindow.getStepCodeByScenario": async (input) => {
287
+ return await recorder.getStepCodeByScenario(input);
288
+ },
289
+ "recorderWindow.setStepCodeByScenario": async (input) => {
290
+ return await recorder.setStepCodeByScenario(input);
291
+ },
292
+ "recorderWindow.getRecorderContext": async (input) => {
293
+ return await recorder.getContext();
294
+ },
295
+ "recorderWindow.addCommandToStepCode": async (input) => {
296
+ return await recorder.addCommandToStepCode(input);
297
+ },
298
+ "recorderWindow.deleteCommandFromStepCode": async (input) => {
299
+ return await recorder.deleteCommandFromStepCode(input);
300
+ },
301
+ "recorderWindow.generateLocatorSummaries": async (input) => {
302
+ return await recorder.generateLocatorSummaries(input);
303
+ },
304
+ });
305
+ socket.on("targetBrowser.command.event", async (input) => {
306
+ return recorder.onAction(input);
307
+ });
308
+ promisifiedSocketServer.init();
309
+ }
310
+ export { BVTRecorderInit };