@dev-blinq/cucumber_client 1.0.1317-dev → 1.0.1319-dev

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.
@@ -735,6 +735,13 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
735
735
  codeLines.push(...generateReportCommand("start", recordingStep.cmdId));
736
736
  }
737
737
  const result = _generateCodeFromCommand(recordingStep, elements, userData);
738
+ if (process.env.TEMP_RUN) {
739
+ // Add try catch block to the generated code, should attach commandId to world object
740
+ result.codeLines.unshift(`try {`);
741
+ result.codeLines.push(
742
+ `} catch (err) { throw new Error("Error in command ${recordingStep.cmdId}: " + err.message); }`
743
+ );
744
+ }
738
745
  codeLines.push(...result.codeLines);
739
746
  if (process.env.TEMP_RUN === "true") {
740
747
  codeLines.push(...generateReportCommand("end", recordingStep.cmdId));
@@ -15,7 +15,7 @@ import chokidar from "chokidar";
15
15
  import logger from "../../logger.js";
16
16
  import { unEscapeNonPrintables } from "../cucumber/utils.js";
17
17
  import { findAvailablePort } from "../utils/index.js";
18
- import NetworkMonitor from "./network.js";
18
+ import { Step } from "../cucumber/feature.js";
19
19
 
20
20
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
21
21
 
@@ -209,7 +209,6 @@ export class BVTRecorder {
209
209
  },
210
210
  });
211
211
  this.pageSet = new Set();
212
- this.networkMonitor = new NetworkMonitor();
213
212
  this.lastKnownUrlPath = "";
214
213
  // TODO: what is world?
215
214
  this.world = { attach: () => {} };
@@ -619,7 +618,6 @@ export class BVTRecorder {
619
618
  this.pageSet.add(page);
620
619
 
621
620
  await page.waitForLoadState("domcontentloaded");
622
-
623
621
  // add listener for frame navigation on new tab
624
622
  this._addFrameNavigateListener(page);
625
623
  } catch (error) {
@@ -709,6 +707,7 @@ export class BVTRecorder {
709
707
  if (this.shouldTakeScreenshot) {
710
708
  await this.storeScreenshot(event);
711
709
  }
710
+
712
711
  this.sendEvent(this.events.onNewCommand, cmdEvent);
713
712
  this._updateUrlPath();
714
713
  }
@@ -784,6 +783,7 @@ export class BVTRecorder {
784
783
  }
785
784
 
786
785
  async startRecordingInput() {
786
+ console.log("startRecordingInput");
787
787
  await this.setMode("recordingInput");
788
788
  }
789
789
  async stopRecordingInput() {
@@ -1016,6 +1016,53 @@ export class BVTRecorder {
1016
1016
  const stepParams = parseStepTextParameters(stepName);
1017
1017
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
1018
1018
  }
1019
+
1020
+ parseRouteFiles(step) {
1021
+ const routeFolder = path.join(this.projectDir, "data", "routes");
1022
+ const templateRouteMap = new Map();
1023
+
1024
+ // Go over all the files in the route folder and parse them
1025
+ const routeFiles = readdirSync(routeFolder).filter((file) => file.endsWith(".json"));
1026
+ for (const file of routeFiles) {
1027
+ const filePath = path.join(routeFolder, file);
1028
+ const routeData = JSON.parse(readFileSync(filePath, "utf8"));
1029
+ if (routeData && routeData.template) {
1030
+ const template = routeData.template;
1031
+ const routes = routeData.routes;
1032
+
1033
+ templateRouteMap.set(template, routes);
1034
+ }
1035
+ }
1036
+
1037
+ if (!existsSync(routeFolder)) {
1038
+ return null;
1039
+ } else if (step && step.text) {
1040
+ // Convert the step text to cucumber template
1041
+ const cucumberStep = new Step();
1042
+ cucumberStep.text = step.text;
1043
+ const template = cucumberStep.getTemplate();
1044
+ if (templateRouteMap.has(template)) {
1045
+ const routeItems = templateRouteMap.get(template);
1046
+ console.log("Route Items from template:", routeItems);
1047
+ routeItems.forEach((item) => {
1048
+ const filters = item.filters || {};
1049
+ const queryParams = filters?.queryParams || {};
1050
+ console.log("Query Params:", queryParams);
1051
+ const queryParamsArray = Object.keys(queryParams).map((key) => ({
1052
+ paramKey: key,
1053
+ paramValue: queryParams[key],
1054
+ }));
1055
+ console.log("Query Params Array:", queryParamsArray);
1056
+ filters.queryParams = queryParamsArray || [];
1057
+ });
1058
+ step.routeItems = routeItems;
1059
+ console.log("Route Items:", step.routeItems);
1060
+ } else {
1061
+ step.routeItems = null;
1062
+ }
1063
+ }
1064
+ }
1065
+
1019
1066
  loadExistingScenario({ featureName, scenarioName }) {
1020
1067
  const step_definitions = loadStepDefinitions(this.projectDir);
1021
1068
  const featureFilePath = path.join(this.projectDir, "features", featureName);
@@ -1044,6 +1091,7 @@ export class BVTRecorder {
1044
1091
  ..._s,
1045
1092
  keyword: step.keyword.trim(),
1046
1093
  };
1094
+ this.parseRouteFiles(_step);
1047
1095
  steps.push(_step);
1048
1096
  }
1049
1097
  return {
@@ -16,7 +16,7 @@ class PromisifiedSocketServer {
16
16
  init() {
17
17
  this.socket.on("request", async (data) => {
18
18
  const { event, input, id, roomId, socketId } = data;
19
- // console.log("request", { event, input, id, roomId, socketId });
19
+ console.log("request", { event, input, id, roomId, socketId });
20
20
  try {
21
21
  const handler = this.routes[event];
22
22
  if (!handler) {
@@ -86,10 +86,10 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
86
86
  console.log("connecting to " + WS_URL);
87
87
  const socket = io(WS_URL);
88
88
  socket.on("connect", () => {
89
- // console.log('connected to server')
89
+ console.log("connected to server");
90
90
  });
91
91
  socket.on("disconnect", () => {
92
- // console.log('disconnected from server')
92
+ console.log("disconnected from server");
93
93
  });
94
94
  socket.emit("joinRoom", { id: roomId, window: "cucumber_client/bvt_recorder" });
95
95
  const recorder = new BVTRecorder({
@@ -1,18 +1,8 @@
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
1
  class NetworkMonitor {
11
2
  constructor() {
12
3
  this.networkId = 0;
13
- /** @type {Map<string, NetworkEvent>} */
14
- this.requestIdMap = new Map();
15
4
  this.networkEvents = new Map();
5
+ this.requestIdMap = new Map();
16
6
  /** @type {Map<import('playwright').Page, {responseListener: Function, requestFailedListener: Function}>} */
17
7
  this.pageListeners = new Map();
18
8
  }
@@ -22,6 +12,7 @@ class NetworkMonitor {
22
12
  * @param {import('playwright').Page} page
23
13
  */
24
14
  addNetworkEventListener(page) {
15
+
25
16
  // Create the listener functions
26
17
  const responseListener = async (response) => {
27
18
  const request = response.request();
@@ -210,6 +201,7 @@ class NetworkMonitor {
210
201
  // Try to get response body safely (only for successful responses)
211
202
  if (response && networkEvent.status === "completed") {
212
203
  try {
204
+
213
205
  const isBinary =
214
206
  !response.headers()["content-type"]?.includes("application/json") &&
215
207
  !response.headers()["content-type"]?.includes("text");
@@ -237,6 +229,7 @@ class NetworkMonitor {
237
229
  marshalledEvent.failureReason = request.failure()?.errorText || "Unknown error";
238
230
  }
239
231
 
232
+
240
233
  console.log("Marshalled network event:", marshalledEvent);
241
234
 
242
235
  return marshalledEvent;
@@ -253,18 +246,7 @@ class NetworkMonitor {
253
246
  }
254
247
  }
255
248
 
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
249
 
267
- /**
268
250
  * Get marshalled network events since this ID
269
251
  * @returns {Promise<Object[]>}
270
252
  * @param {number} sinceId
@@ -281,6 +263,7 @@ class NetworkMonitor {
281
263
  * @param {number} endId
282
264
  * @returns {Promise<Object[]>}
283
265
  */
266
+
284
267
  async getMarshalledNetworkEventsInRange(startId, endId) {
285
268
  const events = this.getNetworkEventsInRange(startId, endId);
286
269
  const marshalledEvents = await Promise.all(events.map((event) => this.marshallNetworkEvent(event)));
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "fs";
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
2
2
  import path from "path";
3
3
  import fs from "fs";
4
4
  import { generatePageName } from "../code_gen/playwright_codeget.js";
@@ -9,9 +9,11 @@ import {
9
9
  getUtilsCodePage,
10
10
  loadStepDefinitions,
11
11
  saveRecording,
12
+ saveRoutes,
12
13
  } from "./step_utils.js";
13
14
  import { escapeString, getExamplesContent } from "./update_feature.js";
14
15
  import { locateDefinitionPath } from "../cucumber/steps_definitions.js";
16
+ import { tmpdir } from "os";
15
17
 
16
18
  // let copiedCodeToTemp = false;
17
19
  async function withAbort(fn, signal) {
@@ -228,28 +230,57 @@ export class BVTStepRunner {
228
230
  if (!codePage) {
229
231
  codePage = getUtilsCodePage(this.projectDir);
230
232
  }
231
- }
232
- const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags });
233
- // console.log({ feature_file_path, step_text: step.text });
234
-
235
- const stepExecController = new AbortController();
236
- this.#currentStepController = stepExecController;
237
- const { result, info } = await withAbort(async () => {
238
- return await this.executeStepRemote(
239
- {
240
- feature_file_path,
241
- tempFolderPath,
242
- stepText: step.text,
243
- scenario: "Temp Scenario",
244
- },
245
- options
246
- );
247
- }, stepExecController.signal).finally(() => {
248
- // rm temp folder
249
- if (fs.existsSync(tempFolderPath)) {
250
- fs.rmSync(tempFolderPath, { recursive: true });
233
+ } else {
234
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
235
+ if (process.env.TEMP_RUN === "true") {
236
+ console.log("Save routes in temp folder for running:", routesPath);
237
+ if (existsSync(routesPath)) {
238
+ console.log("Removing existing temp_routes_folder:", routesPath);
239
+ rmSync(routesPath, { recursive: true });
240
+ }
241
+ mkdirSync(routesPath, { recursive: true });
242
+ console.log("Created temp_routes_folder:", routesPath);
243
+ saveRoutes({ step, folderPath: routesPath });
244
+ } else {
245
+ console.log("Saving routes in project directory:", this.projectDir);
246
+ if (existsSync(routesPath)) {
247
+ // remove the folder
248
+ try {
249
+ rmSync(routesPath, { recursive: true });
250
+ console.log("Removed temp_routes_folder:", routesPath);
251
+ } catch (error) {
252
+ console.error("Error removing temp_routes folder", error);
253
+ }
254
+ }
255
+ routesPath = path.join(this.projectDir, "data", "routes");
256
+ console.log("Saving routes to:", routesPath);
257
+ if (!existsSync(routesPath)) {
258
+ mkdirSync(routesPath, { recursive: true });
259
+ }
260
+ saveRoutes({ step, folderPath: routesPath });
251
261
  }
252
- });
253
- return { result, info };
262
+ const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags });
263
+ // console.log({ feature_file_path, step_text: step.text });
264
+
265
+ const stepExecController = new AbortController();
266
+ this.#currentStepController = stepExecController;
267
+ const { result, info } = await withAbort(async () => {
268
+ return await stepsDefinitions.executeStepRemote(
269
+ {
270
+ feature_file_path,
271
+ tempFolderPath,
272
+ stepText: step.text,
273
+ scenario: "Temp Scenario",
274
+ },
275
+ options
276
+ );
277
+ }, stepExecController.signal).finally(() => {
278
+ // rm temp folder
279
+ if (fs.existsSync(tempFolderPath)) {
280
+ fs.rmSync(tempFolderPath, { recursive: true });
281
+ }
282
+ });
283
+ return { result, info };
284
+ }
254
285
  }
255
286
  }
@@ -71,6 +71,7 @@ function makeStepTextUnique(step, stepsDefinitions) {
71
71
  }
72
72
 
73
73
  export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
74
+
74
75
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
75
76
 
76
77
  if (process.env.TEMP_RUN) {
@@ -105,10 +106,39 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
105
106
  codePage = getCodePage(stepDef.file);
106
107
  } else {
107
108
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
109
+
108
110
  if (isUtilStep) {
109
111
  return;
110
112
  }
111
113
  }
114
+
115
+ if (process.env.TEMP_RUN === "true") {
116
+ console.log("Save routes in temp folder for running:", routesPath);
117
+ if (existsSync(routesPath)) {
118
+ console.log("Removing existing temp_routes_folder:", routesPath);
119
+ rmSync(routesPath, { recursive: true });
120
+ }
121
+ mkdirSync(routesPath, { recursive: true });
122
+ console.log("Created temp_routes_folder:", routesPath);
123
+ saveRoutes({ step, folderPath: routesPath });
124
+ } else {
125
+ console.log("Saving routes in project directory:", projectDir);
126
+ if (existsSync(routesPath)) {
127
+ // remove the folder
128
+ try {
129
+ rmSync(routesPath, { recursive: true });
130
+ console.log("Removed temp_routes_folder:", routesPath);
131
+ } catch (error) {
132
+ console.error("Error removing temp_routes folder", error);
133
+ }
134
+ }
135
+ routesPath = path.join(projectDir, "data", "routes");
136
+ console.log("Saving routes to:", routesPath);
137
+ if (!existsSync(routesPath)) {
138
+ mkdirSync(routesPath, { recursive: true });
139
+ }
140
+ saveRoutes({ step, folderPath: routesPath });
141
+ }
112
142
 
113
143
  cucumberStep.text = step.text;
114
144
  const recording = new Recording();
@@ -385,11 +415,13 @@ export function saveRoutes({ step, folderPath }) {
385
415
  const cucumberStep = getCucumberStep({ step });
386
416
  const template = cucumberStep.getTemplate();
387
417
  const stepNameHash = createHash("sha256").update(template).digest("hex");
418
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
419
+
388
420
  const routeItemsWithFilters = routeItems.map((routeItem) => {
389
421
  const oldFilters = routeItem.filters;
390
422
  const queryParamsObject = {};
391
423
  oldFilters.queryParams.forEach((queryParam) => {
392
- queryParamsObject[queryParam.key] = queryParam.value;
424
+ queryParamsObject[queryParam.paramKey] = queryParam.paramValue;
393
425
  });
394
426
  const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
395
427
  return {
@@ -399,10 +431,13 @@ export function saveRoutes({ step, folderPath }) {
399
431
  });
400
432
 
401
433
  const routesFilePath = path.join(folderPath, stepNameHash + ".json");
434
+ console.log("Routes file path:", routesFilePath);
402
435
  const routesData = {
403
436
  template,
404
437
  routes: routeItemsWithFilters,
405
438
  };
439
+ console.log("Routes data to save:", routesData);
440
+
406
441
  if (!existsSync(folderPath)) {
407
442
  mkdirSync(folderPath, { recursive: true });
408
443
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1317-dev",
3
+ "version": "1.0.1319-dev",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  "@cucumber/tag-expressions": "^6.1.1",
33
33
  "@dev-blinq/cucumber-js": "1.0.178-dev",
34
34
  "@faker-js/faker": "^8.1.0",
35
- "automation_model": "1.0.776-dev",
35
+ "automation_model": "1.0.777-dev",
36
36
  "axios": "^1.7.4",
37
37
  "chokidar": "^3.6.0",
38
38
  "create-require": "^1.1.1",