@dev-blinq/cucumber_client 1.0.1175-dev → 1.0.1175-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 +220 -0
  2. package/bin/assets/preload/accessibility.js +1 -1
  3. package/bin/assets/preload/find_context.js +1 -1
  4. package/bin/assets/preload/generateSelector.js +24 -0
  5. package/bin/assets/preload/locators.js +18 -0
  6. package/bin/assets/preload/recorderv3.js +85 -11
  7. package/bin/assets/preload/unique_locators.js +24 -3
  8. package/bin/assets/scripts/aria_snapshot.js +235 -0
  9. package/bin/assets/scripts/dom_attr.js +372 -0
  10. package/bin/assets/scripts/dom_element.js +0 -0
  11. package/bin/assets/scripts/dom_parent.js +185 -0
  12. package/bin/assets/scripts/event_utils.js +105 -0
  13. package/bin/assets/scripts/pw.js +7886 -0
  14. package/bin/assets/scripts/recorder.js +1147 -0
  15. package/bin/assets/scripts/snapshot_capturer.js +155 -0
  16. package/bin/assets/scripts/unique_locators.js +844 -0
  17. package/bin/assets/scripts/yaml.js +4770 -0
  18. package/bin/assets/templates/page_template.txt +2 -16
  19. package/bin/assets/templates/utils_template.txt +65 -12
  20. package/bin/client/cli_helpers.js +0 -1
  21. package/bin/client/code_cleanup/utils.js +43 -14
  22. package/bin/client/code_gen/code_inversion.js +112 -18
  23. package/bin/client/code_gen/index.js +3 -0
  24. package/bin/client/code_gen/page_reflection.js +37 -20
  25. package/bin/client/code_gen/playwright_codeget.js +152 -48
  26. package/bin/client/cucumber/feature.js +96 -42
  27. package/bin/client/cucumber/project_to_document.js +8 -7
  28. package/bin/client/cucumber/steps_definitions.js +59 -16
  29. package/bin/client/local_agent.js +9 -7
  30. package/bin/client/operations/dump_tree.js +159 -5
  31. package/bin/client/playground/playground.js +1 -1
  32. package/bin/client/project.js +6 -2
  33. package/bin/client/recorderv3/bvt_recorder.js +236 -79
  34. package/bin/client/recorderv3/cli.js +1 -0
  35. package/bin/client/recorderv3/implemented_steps.js +111 -11
  36. package/bin/client/recorderv3/index.js +45 -4
  37. package/bin/client/recorderv3/network.js +299 -0
  38. package/bin/client/recorderv3/step_runner.js +179 -13
  39. package/bin/client/recorderv3/step_utils.js +159 -14
  40. package/bin/client/recorderv3/update_feature.js +55 -30
  41. package/bin/client/recording.js +8 -0
  42. package/bin/client/run_cucumber.js +116 -4
  43. package/bin/client/scenario_report.js +112 -50
  44. package/bin/client/test_scenario.js +0 -1
  45. package/bin/index.js +1 -0
  46. package/package.json +15 -8
  47. package/bin/client/code_gen/get_implemented_steps.js +0 -27
@@ -51,6 +51,37 @@ class PromisifiedSocketServer {
51
51
  }
52
52
  }
53
53
 
54
+ function memorySizeOf(obj) {
55
+ var bytes = 0;
56
+
57
+ function sizeOf(obj) {
58
+ if (obj !== null && obj !== undefined) {
59
+ switch (typeof obj) {
60
+ case "number":
61
+ bytes += 8;
62
+ break;
63
+ case "string":
64
+ bytes += obj.length * 2;
65
+ break;
66
+ case "boolean":
67
+ bytes += 4;
68
+ break;
69
+ case "object":
70
+ var objClass = Object.prototype.toString.call(obj).slice(8, -1);
71
+ if (objClass === "Object" || objClass === "Array") {
72
+ for (var key in obj) {
73
+ if (!obj.hasOwnProperty(key)) continue;
74
+ sizeOf(obj[key]);
75
+ }
76
+ } else bytes += obj.toString().length * 2;
77
+ break;
78
+ }
79
+ }
80
+ return bytes;
81
+ }
82
+ return sizeOf(obj);
83
+ }
84
+
54
85
  const init = ({ envName, projectDir, roomId, TOKEN }) => {
55
86
  console.log("connecting to " + WS_URL);
56
87
  const socket = io(WS_URL);
@@ -66,6 +97,7 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
66
97
  projectDir,
67
98
  TOKEN,
68
99
  sendEvent: (event, data) => {
100
+ console.log("Size of data", memorySizeOf(data), "bytes");
69
101
  console.log("----", event, data, "roomId", roomId);
70
102
  socket.emit(event, data, roomId);
71
103
  },
@@ -77,7 +109,7 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
77
109
  socket.emit("BVTRecorder.browserOpened", null, roomId);
78
110
  })
79
111
  .catch((e) => {
80
- socket.emit("BVTRecorder.browserLaunchFailed", null, roomId);
112
+ socket.emit("BVTRecorder.browserLaunchFailed", e, roomId);
81
113
  });
82
114
  const timeOutForFunction = async (promise, timeout = 5000) => {
83
115
  const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(), timeout));
@@ -139,9 +171,6 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
139
171
  "recorderWindow.runStep": async (input) => {
140
172
  return recorder.runStep(input);
141
173
  },
142
- "recorderWindow.runScenario": async (input) => {
143
- return recorder.runScenario(input);
144
- },
145
174
  "recorderWindow.saveScenario": async (input) => {
146
175
  return recorder.saveScenario(input);
147
176
  },
@@ -216,6 +245,12 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
216
245
  return { folder: snapshotFolder, files: ymlFiles };
217
246
  } else return { folder: null, files: [] };
218
247
  },
248
+ "recorderWindow.getCurrentPageTitle": async (input) => {
249
+ return await recorder.getCurrentPageTitle();
250
+ },
251
+ "recorderWindow.getCurrentPageUrl": async (input) => {
252
+ return await recorder.getCurrentPageUrl();
253
+ },
219
254
  "recorderWindow.sendAriaSnapshot": async (input) => {
220
255
  const snapshot = input?.snapshot;
221
256
  const deselect = input?.deselect;
@@ -233,6 +268,12 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
233
268
  const mode = input?.mode;
234
269
  return recorder.setMode(mode);
235
270
  },
271
+ "recorderWindow.getStepsAndCommandsForScenario": async (input) => {
272
+ return await recorder.getStepsAndCommandsForScenario(input);
273
+ },
274
+ "recorderWindow.getNetworkEvents": async (input) => {
275
+ return await recorder.getNetworkEvents(input);
276
+ },
236
277
  });
237
278
  socket.on("targetBrowser.command.event", async (input) => {
238
279
  return recorder.onAction(input);
@@ -0,0 +1,299 @@
1
+ /**
2
+ * @typedef {Object} NetworkEvent
3
+ * @property {import('playwright').Request} request
4
+ * @property {import('playwright').Response|null} response
5
+ * @property {string} id
6
+ * @property {number} timestamp
7
+ * @property {string} status - 'completed', 'failed'
8
+ */
9
+
10
+ class NetworkMonitor {
11
+ constructor() {
12
+ this.networkId = 0;
13
+ /** @type {Map<string, NetworkEvent>} */
14
+ this.requestIdMap = new Map();
15
+ this.networkEvents = new Map();
16
+ /** @type {Map<import('playwright').Page, {responseListener: Function, requestFailedListener: Function}>} */
17
+ this.pageListeners = new Map();
18
+ }
19
+
20
+ /**
21
+ * Add network event listeners to a page
22
+ * @param {import('playwright').Page} page
23
+ */
24
+ addNetworkEventListener(page) {
25
+ // Create the listener functions
26
+ const responseListener = async (response) => {
27
+ const request = response.request();
28
+ let eventId = this.requestIdMap.get(request);
29
+
30
+ if (!eventId) {
31
+ this.networkId++;
32
+ eventId = this.networkId.toString();
33
+ this.requestIdMap.set(request, eventId);
34
+ }
35
+
36
+ const networkEvent = {
37
+ id: eventId,
38
+ request,
39
+ response,
40
+ timestamp: Date.now(),
41
+ status: "completed",
42
+ };
43
+
44
+ this.networkEvents.set(eventId, networkEvent);
45
+ };
46
+
47
+ const requestFailedListener = (request) => {
48
+ let eventId = this.requestIdMap.get(request);
49
+
50
+ if (!eventId) {
51
+ this.networkId++;
52
+ eventId = this.networkId.toString();
53
+ this.requestIdMap.set(request, eventId);
54
+ }
55
+
56
+ const networkEvent = {
57
+ id: eventId,
58
+ request,
59
+ response: null,
60
+ timestamp: Date.now(),
61
+ status: "failed",
62
+ };
63
+
64
+ this.networkEvents.set(eventId, networkEvent);
65
+ };
66
+ // Store the listeners for later removal
67
+ this.pageListeners.set(page, {
68
+ responseListener,
69
+ requestFailedListener,
70
+ });
71
+
72
+ // Add the listeners to the page
73
+ page.on("response", responseListener);
74
+ page.on("requestfailed", requestFailedListener);
75
+ }
76
+
77
+ /**
78
+ * Remove network event listeners from a specific page
79
+ * @param {import('playwright').Page} page
80
+ */
81
+ removeNetworkEventListener(page) {
82
+ const listeners = this.pageListeners.get(page);
83
+ if (listeners) {
84
+ page.off("response", listeners.responseListener);
85
+ page.off("requestfailed", listeners.requestFailedListener);
86
+ this.pageListeners.delete(page);
87
+ console.log("Network event listeners removed from page");
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Remove network event listeners from all pages
93
+ */
94
+ removeAllNetworkEventListeners() {
95
+ for (const [page, listeners] of this.pageListeners) {
96
+ page.off("response", listeners.responseListener);
97
+ page.off("requestfailed", listeners.requestFailedListener);
98
+ }
99
+ this.pageListeners.clear();
100
+ console.log("All network event listeners removed");
101
+ }
102
+
103
+ /**
104
+ * Check if a page has active listeners
105
+ * @param {import('playwright').Page} page
106
+ * @returns {boolean}
107
+ */
108
+ hasListeners(page) {
109
+ return this.pageListeners.has(page);
110
+ }
111
+
112
+ /**
113
+ * Get the number of pages with active listeners
114
+ * @returns {number}
115
+ */
116
+ getActiveListenersCount() {
117
+ return this.pageListeners.size;
118
+ }
119
+
120
+ /**
121
+ * Get all network events until now
122
+ */
123
+ getAllNetworkEvents() {
124
+ return Array.from(this.networkEvents.values());
125
+ }
126
+
127
+ /**
128
+ * Get network events within a range
129
+ * @param {number} startId
130
+ * @param {number} endId
131
+ * @returns {NetworkEvent[]}
132
+ */
133
+ getNetworkEventsInRange(startId, endId) {
134
+ const events = [];
135
+ for (let i = startId; i <= endId; i++) {
136
+ const event = this.networkEvents.get(i.toString());
137
+ if (event) {
138
+ events.push(event);
139
+ }
140
+ }
141
+ return events;
142
+ }
143
+
144
+ /**
145
+ * Get network events since a specific ID
146
+ * @param {number} sinceId
147
+ * @returns {NetworkEvent[]}
148
+ */
149
+ getNetworkEventsSince(sinceId) {
150
+ return this.getNetworkEventsInRange(sinceId + 1, this.networkId);
151
+ }
152
+
153
+ /**
154
+ * Get events by status
155
+ * @param {string} status - 'completed', 'failed'
156
+ * @returns {NetworkEvent[]}
157
+ */
158
+ getEventsByStatus(status) {
159
+ return Array.from(this.networkEvents.values()).filter((event) => event.status === status);
160
+ }
161
+
162
+ /**
163
+ * Get current network ID (latest)
164
+ * @returns {number}
165
+ */
166
+ getCurrentNetworkId() {
167
+ return this.networkId;
168
+ }
169
+
170
+ getCurrentNetworkEventsLength() {
171
+ return this.networkEvents.size;
172
+ }
173
+
174
+ /**
175
+ * Get statistics about stored events
176
+ * @returns {Object}
177
+ */
178
+ getStats() {
179
+ const events = Array.from(this.networkEvents.values());
180
+ return {
181
+ total: events.length,
182
+ completed: events.filter((e) => e.status === "completed").length,
183
+ failed: events.filter((e) => e.status === "failed").length,
184
+ oldestTimestamp: events.length > 0 ? Math.min(...events.map((e) => e.timestamp)) : null,
185
+ newestTimestamp: events.length > 0 ? Math.max(...events.map((e) => e.timestamp)) : null,
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Marshall network event for serialization
191
+ * @param {NetworkEvent} networkEvent
192
+ * @returns {Promise<Object>}
193
+ */
194
+ async marshallNetworkEvent(networkEvent) {
195
+ const { request, response } = networkEvent;
196
+
197
+ try {
198
+ const url = new URL(request.url());
199
+ const marshalledEvent = {
200
+ id: networkEvent.id,
201
+ timestamp: networkEvent.timestamp,
202
+ status: networkEvent.status,
203
+ url: url.href,
204
+ method: request.method(),
205
+ statusCode: response?.status() ?? null,
206
+ statusText: response?.statusText() ?? null,
207
+ queryParams: Object.fromEntries(url.searchParams.entries()),
208
+ };
209
+
210
+ // Try to get response body safely (only for successful responses)
211
+ if (response && networkEvent.status === "completed") {
212
+ try {
213
+ const isBinary =
214
+ !response.headers()["content-type"]?.includes("application/json") &&
215
+ !response.headers()["content-type"]?.includes("text");
216
+ let body;
217
+ if (isBinary) {
218
+ body = await response.body();
219
+ } else {
220
+ body = await response.text();
221
+ }
222
+ let json;
223
+ try {
224
+ if (typeof body === "string") {
225
+ json = JSON.parse(body); // Check if body is valid JSON
226
+ }
227
+ } catch (_) {
228
+ //Ignore
229
+ }
230
+ const responseBody = isBinary ? body : json ? JSON.stringify(json) : body;
231
+ marshalledEvent.contentType = isBinary ? "binary" : json ? "json" : "text";
232
+ marshalledEvent.responseBody = responseBody;
233
+ } catch (error) {
234
+ marshalledEvent.responseBody = `[Error reading response: ${error.message}]`;
235
+ }
236
+ } else if (networkEvent.status === "failed") {
237
+ marshalledEvent.failureReason = request.failure()?.errorText || "Unknown error";
238
+ }
239
+
240
+ console.log("Marshalled network event:", marshalledEvent);
241
+
242
+ return marshalledEvent;
243
+ } catch (error) {
244
+ console.error("Error marshalling network event:", error);
245
+ return {
246
+ id: networkEvent.id,
247
+ timestamp: networkEvent.timestamp,
248
+ status: networkEvent.status,
249
+ url: request.url(),
250
+ method: request.method(),
251
+ error: error.message,
252
+ };
253
+ }
254
+ }
255
+
256
+ /**
257
+ *@returns {Promise<Object[]>}
258
+ * Get all marshalled network events
259
+ * This is useful for sending to the server or saving to a file.
260
+ * */
261
+ async getAllMarshalledNetworkEvents() {
262
+ const events = this.getAllNetworkEvents();
263
+ const marshalledEvents = await Promise.all(events.map((event) => this.marshallNetworkEvent(event)));
264
+ return marshalledEvents;
265
+ }
266
+
267
+ /**
268
+ * Get marshalled network events since this ID
269
+ * @returns {Promise<Object[]>}
270
+ * @param {number} sinceId
271
+ */
272
+ async getMarshalledNetworkEvents(sinceId) {
273
+ const events = this.getNetworkEventsSince(sinceId);
274
+ const marshalledEvents = await Promise.all(events.map((event) => this.marshallNetworkEvent(event)));
275
+ return marshalledEvents;
276
+ }
277
+
278
+ /**
279
+ * Get marshalled network events in a range
280
+ * @param {number} startId
281
+ * @param {number} endId
282
+ * @returns {Promise<Object[]>}
283
+ */
284
+ async getMarshalledNetworkEventsInRange(startId, endId) {
285
+ const events = this.getNetworkEventsInRange(startId, endId);
286
+ const marshalledEvents = await Promise.all(events.map((event) => this.marshallNetworkEvent(event)));
287
+ return marshalledEvents;
288
+ }
289
+
290
+ /**
291
+ * Clear all network events
292
+ */
293
+ clearNetworkEvents() {
294
+ this.networkEvents.clear();
295
+ this.networkId = 0;
296
+ }
297
+ }
298
+
299
+ export default NetworkMonitor;
@@ -1,6 +1,5 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "fs";
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
2
2
  import path from "path";
3
- import fs from "fs";
4
3
  import { generatePageName } from "../code_gen/playwright_codeget.js";
5
4
  import {
6
5
  executeStep,
@@ -9,26 +8,36 @@ import {
9
8
  getUtilsCodePage,
10
9
  loadStepDefinitions,
11
10
  saveRecording,
11
+ saveRoutes,
12
12
  } from "./step_utils.js";
13
13
  import { escapeString, getExamplesContent } from "./update_feature.js";
14
+ import fs from "fs";
15
+ import { locateDefinitionPath } from "../cucumber/steps_definitions.js";
16
+ import { tmpdir } from "os";
14
17
 
15
18
  // let copiedCodeToTemp = false;
16
19
  async function withAbort(fn, signal) {
17
20
  if (!signal) {
18
21
  return await fn();
19
22
  }
23
+ return new Promise((resolve, reject) => {
24
+ const abortHandler = () => reject(new Error("Aborted"));
25
+ signal.addEventListener("abort", abortHandler, { once: true });
20
26
 
21
- const abortPromise = new Promise((_, reject) => {
22
- signal.addEventListener("abort", () => reject(new Error("Aborted")), { once: true });
27
+ fn()
28
+ .then(resolve)
29
+ .catch(reject)
30
+ .finally(() => {
31
+ signal.removeEventListener("abort", abortHandler);
32
+ });
23
33
  });
24
-
25
- return await Promise.race([fn(), abortPromise]);
26
34
  }
27
35
  export class BVTStepRunner {
28
36
  #currentStepController;
29
37
  #port;
30
- constructor({ projectDir }) {
38
+ constructor({ projectDir, sendExecutionStatus }) {
31
39
  this.projectDir = projectDir;
40
+ this.sendExecutionStatus = sendExecutionStatus;
32
41
  }
33
42
  setRemoteDebugPort(port) {
34
43
  this.#port = port;
@@ -55,11 +64,12 @@ export class BVTStepRunner {
55
64
  // copiedCodeToTemp = true;
56
65
  }
57
66
 
58
- async writeTempFeatureFile({ step, parametersMap, tempFolderPath }) {
67
+ async writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags }) {
59
68
  const tFilePath = path.join(tempFolderPath, "__temp.feature");
60
69
  // console.log(tFilePath);
61
70
  let tFileContent = `# temp feature file
62
71
  Feature: Temp feature
72
+ ${tags ? tags.join(" ") : ""}
63
73
  Scenario Outline: Temp Scenario
64
74
  Given ${escapeString(step.text)}
65
75
  `;
@@ -67,7 +77,132 @@ export class BVTStepRunner {
67
77
  writeFileSync(tFilePath, tFileContent);
68
78
  return tFilePath;
69
79
  }
70
- async runStep({ step, parametersMap, envPath }, bvtContext, options) {
80
+
81
+ executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText }, options) => {
82
+ if (!options) {
83
+ options = {
84
+ skipAfter: true,
85
+ };
86
+ }
87
+ const environment = {
88
+ ...process.env,
89
+ };
90
+
91
+ try {
92
+ const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
93
+ const { runConfiguration } = await loadConfiguration(
94
+ {
95
+ provided: {
96
+ name: [scenario],
97
+ paths: [feature_file_path],
98
+ import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
99
+ // format: ["bvt"],
100
+ },
101
+ },
102
+ { cwd: process.cwd(), env: environment }
103
+ );
104
+ // const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
105
+ // console.log("Files found:", files);
106
+ const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
107
+ // console.log("found ", support.stepDefinitions.length, "step definitions");
108
+ // support.stepDefinitions.map((step) => {
109
+ // console.log("step", step.pattern);
110
+ // });
111
+
112
+ if (options.skipAfter) {
113
+ // ignore afterAll/after hooks
114
+ support.afterTestCaseHookDefinitions = [];
115
+ support.afterTestRunHookDefinitions = [];
116
+ }
117
+
118
+ let errorMesssage = null;
119
+ let info = null;
120
+ let errInfo = null;
121
+ const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
122
+ if (message.testStepFinished) {
123
+ const { testStepFinished } = message;
124
+ const { testStepResult } = testStepFinished;
125
+ if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
126
+ if (!errorMesssage) {
127
+ errorMesssage = testStepResult.message;
128
+ if (info) {
129
+ errInfo = info;
130
+ }
131
+ }
132
+ }
133
+ if (testStepResult.status === "UNDEFINED") {
134
+ if (!errorMesssage) {
135
+ errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
136
+ if (info) {
137
+ errInfo = info;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ if (message.attachment) {
143
+ const attachment = message.attachment;
144
+ if (attachment.mediaType === "application/json" && attachment.body) {
145
+ const body = JSON.parse(attachment.body);
146
+ info = body.info;
147
+ const result = body.result;
148
+
149
+ if (result.status === "PASSED") {
150
+ this.sendExecutionStatus({
151
+ type: "cmdExecutionSuccess",
152
+ cmdId: body.cmdId,
153
+ });
154
+ } else {
155
+ this.sendExecutionStatus({
156
+ type: "cmdExecutionError",
157
+ cmdId: body.cmdId,
158
+ error: {
159
+ message: result.message,
160
+ info,
161
+ },
162
+ });
163
+ }
164
+ } else if (attachment.mediaType === "application/json+intercept-results" && attachment.body) {
165
+ const body = JSON.parse(attachment.body);
166
+ if (body) {
167
+ this.sendExecutionStatus({
168
+ type: "interceptResults",
169
+ interceptResults: body,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ });
175
+ if (errorMesssage) {
176
+ const bvtError = new Error(errorMesssage);
177
+ Object.assign(bvtError, { info: errInfo });
178
+ throw bvtError;
179
+ }
180
+
181
+ return {
182
+ result,
183
+ info,
184
+ };
185
+ } catch (error) {
186
+ console.error("Error running cucumber-js", error);
187
+ throw error;
188
+ }
189
+ };
190
+
191
+ async runStep({ step, parametersMap, envPath, tags }, bvtContext, options) {
192
+ let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
193
+ if (bvtContext.web) {
194
+ bvtContext.web.getCmdId = () => {
195
+ if (cmdIDs.length === 0) {
196
+ cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
197
+ }
198
+ const cId = cmdIDs.shift();
199
+ this.sendExecutionStatus({
200
+ type: "cmdExecutionStart",
201
+ cmdId: cId,
202
+ });
203
+ return cId;
204
+ };
205
+ }
71
206
  let codePage; // = getCodePage();
72
207
  // const tempFolderPath = process.env.tempFeaturesFolderPath;
73
208
  const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
@@ -96,7 +231,8 @@ export class BVTStepRunner {
96
231
  if (!existsSync(stepDefinitionFolderPath)) {
97
232
  mkdirSync(stepDefinitionFolderPath, { recursive: true });
98
233
  }
99
- const stepDefsFilePath = path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
234
+ const stepDefsFilePath = locateDefinitionPath(tempFolderPath, pageName);
235
+ //path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
100
236
  codePage = getCodePage(stepDefsFilePath);
101
237
  codePage = await saveRecording({ step, cucumberStep, codePage, projectDir: this.projectDir, stepsDefinitions });
102
238
  if (codePage) {
@@ -106,14 +242,43 @@ export class BVTStepRunner {
106
242
  if (!codePage) {
107
243
  codePage = getUtilsCodePage(this.projectDir);
108
244
  }
245
+ } else {
246
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
247
+ if (process.env.TEMP_RUN === "true") {
248
+ // console.log("Save routes in temp folder for running:", routesPath);
249
+ if (existsSync(routesPath)) {
250
+ // console.log("Removing existing temp_routes_folder:", routesPath);
251
+ rmSync(routesPath, { recursive: true });
252
+ }
253
+ mkdirSync(routesPath, { recursive: true });
254
+ // console.log("Created temp_routes_folder:", routesPath);
255
+ saveRoutes({ step, folderPath: routesPath });
256
+ } else {
257
+ // console.log("Saving routes in project directory:", this.projectDir);
258
+ if (existsSync(routesPath)) {
259
+ // remove the folder
260
+ try {
261
+ rmSync(routesPath, { recursive: true });
262
+ // console.log("Removed temp_routes_folder:", routesPath);
263
+ } catch (error) {
264
+ // console.error("Error removing temp_routes folder", error);
265
+ }
266
+ }
267
+ routesPath = path.join(this.projectDir, "data", "routes");
268
+ // console.log("Saving routes to:", routesPath);
269
+ if (!existsSync(routesPath)) {
270
+ mkdirSync(routesPath, { recursive: true });
271
+ }
272
+ saveRoutes({ step, folderPath: routesPath });
273
+ }
109
274
  }
110
- const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath });
275
+ const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags });
111
276
  // console.log({ feature_file_path, step_text: step.text });
112
277
 
113
278
  const stepExecController = new AbortController();
114
279
  this.#currentStepController = stepExecController;
115
- await withAbort(async () => {
116
- await stepsDefinitions.executeStepRemote(
280
+ const { result, info } = await withAbort(async () => {
281
+ return await this.executeStepRemote(
117
282
  {
118
283
  feature_file_path,
119
284
  tempFolderPath,
@@ -128,5 +293,6 @@ export class BVTStepRunner {
128
293
  fs.rmSync(tempFolderPath, { recursive: true });
129
294
  }
130
295
  });
296
+ return { result, info };
131
297
  }
132
298
  }