@dev-blinq/cucumber_client 1.0.1315-dev → 1.0.1315-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 (51) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +108 -108
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/recorderv3.js +3 -1
  4. package/bin/assets/preload/toolbar.js +27 -29
  5. package/bin/assets/preload/unique_locators.js +1 -1
  6. package/bin/assets/preload/yaml.js +288 -275
  7. package/bin/assets/scripts/aria_snapshot.js +223 -220
  8. package/bin/assets/scripts/dom_attr.js +329 -329
  9. package/bin/assets/scripts/dom_parent.js +169 -174
  10. package/bin/assets/scripts/event_utils.js +94 -94
  11. package/bin/assets/scripts/pw.js +2050 -1949
  12. package/bin/assets/scripts/recorder.js +5 -17
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +156 -48
  15. package/bin/assets/scripts/yaml.js +796 -783
  16. package/bin/assets/templates/_hooks_template.txt +41 -0
  17. package/bin/assets/templates/utils_template.txt +1 -44
  18. package/bin/client/apiTest/apiTest.js +6 -0
  19. package/bin/client/cli_helpers.js +11 -13
  20. package/bin/client/code_cleanup/utils.js +5 -1
  21. package/bin/client/code_gen/api_codegen.js +2 -2
  22. package/bin/client/code_gen/code_inversion.js +53 -4
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +32 -17
  25. package/bin/client/cucumber/feature.js +89 -27
  26. package/bin/client/cucumber/feature_data.js +2 -2
  27. package/bin/client/cucumber/project_to_document.js +9 -3
  28. package/bin/client/cucumber/steps_definitions.js +90 -87
  29. package/bin/client/cucumber_selector.js +17 -1
  30. package/bin/client/local_agent.js +6 -5
  31. package/bin/client/parse_feature_file.js +23 -26
  32. package/bin/client/playground/projects/env.json +2 -2
  33. package/bin/client/project.js +186 -196
  34. package/bin/client/recorderv3/bvt_recorder.js +159 -76
  35. package/bin/client/recorderv3/implemented_steps.js +74 -16
  36. package/bin/client/recorderv3/index.js +65 -54
  37. package/bin/client/recorderv3/scriptTest.js +1 -1
  38. package/bin/client/recorderv3/services.js +4 -16
  39. package/bin/client/recorderv3/step_runner.js +313 -170
  40. package/bin/client/recorderv3/step_utils.js +508 -4
  41. package/bin/client/recorderv3/update_feature.js +32 -30
  42. package/bin/client/run_cucumber.js +5 -1
  43. package/bin/client/scenario_report.js +0 -5
  44. package/bin/client/test_scenario.js +0 -1
  45. package/bin/client/upload-service.js +2 -2
  46. package/bin/client/utils/socket_logger.js +132 -0
  47. package/bin/index.js +1 -0
  48. package/bin/logger.js +3 -2
  49. package/bin/min/consoleApi.min.cjs +2 -3
  50. package/bin/min/injectedScript.min.cjs +16 -16
  51. package/package.json +20 -11
@@ -1,9 +1,9 @@
1
1
  // define the jsdoc type for the input
2
2
  import { closeContext, initContext, _getDataFile, resetTestData } from "automation_model";
3
- import { existsSync, mkdir, mkdirSync, readdirSync, readFileSync, rmSync } from "fs";
3
+ import { existsSync, readdirSync, readFileSync, rmSync } from "fs";
4
4
  import path from "path";
5
5
  import url from "url";
6
- import { getImplementedSteps, getStepsAndCommandsForScenario } from "./implemented_steps.js";
6
+ import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
7
7
  import { NamesService } from "./services.js";
8
8
  import { BVTStepRunner } from "./step_runner.js";
9
9
  import { readFile, writeFile } from "fs/promises";
@@ -12,11 +12,10 @@ import { updateFeatureFile } from "./update_feature.js";
12
12
  import { parseStepTextParameters } from "../cucumber/utils.js";
13
13
  import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
14
14
  import chokidar from "chokidar";
15
- import logger from "../../logger.js";
16
15
  import { unEscapeNonPrintables } from "../cucumber/utils.js";
17
16
  import { findAvailablePort } from "../utils/index.js";
18
- import NetworkMonitor from "./network.js";
19
-
17
+ import socketLogger from "../utils/socket_logger.js";
18
+ import { tmpdir } from "os";
20
19
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
21
20
 
22
21
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -45,15 +44,15 @@ async function evaluate(frame, script) {
45
44
  async function findNestedFrameSelector(frame, obj) {
46
45
  try {
47
46
  const parent = frame.parentFrame();
48
- if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
49
47
  if (!parent) return { children: obj };
50
48
  const frameElement = await frame.frameElement();
51
49
  if (!frameElement) return;
52
50
  const selectors = await parent.evaluate((element) => {
53
- return window.getPWLocators(element);
51
+ return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
54
52
  }, frameElement);
55
53
  return findNestedFrameSelector(parent, { children: obj, selectors });
56
54
  } catch (e) {
55
+ socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
57
56
  console.error(e);
58
57
  }
59
58
  }
@@ -150,6 +149,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
150
149
  };
151
150
  }
152
151
  default: {
152
+ socketLogger.error(`Action not supported: ${action.name}`);
153
153
  console.log("action not supported", action);
154
154
  throw new Error("action not supported");
155
155
  }
@@ -161,6 +161,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
161
161
  * @property {string} projectDir
162
162
  * @property {string} TOKEN
163
163
  * @property {(name:string, data:any)=> void} sendEvent
164
+ * @property {Object} logger
164
165
  */
165
166
  export class BVTRecorder {
166
167
  #currentURL = "";
@@ -174,7 +175,6 @@ export class BVTRecorder {
174
175
  */
175
176
  constructor(initialState) {
176
177
  Object.assign(this, initialState);
177
- this.logger = logger;
178
178
  this.screenshotMap = new Map();
179
179
  this.snapshotMap = new Map();
180
180
  this.scenariosStepsMap = new Map();
@@ -184,37 +184,16 @@ export class BVTRecorder {
184
184
  projectDir: this.projectDir,
185
185
  logger: this.logger,
186
186
  });
187
- this.stepRunner = new BVTStepRunner({
188
- projectDir: this.projectDir,
189
- sendExecutionStatus: (data) => {
190
- if (data && data.type) {
191
- switch (data.type) {
192
- case "cmdExecutionStart":
193
- console.log("Sending cmdExecutionStart event for cmdId:", data.cmdId);
194
- this.sendEvent(this.events.cmdExecutionStart, data.cmdId);
195
- break;
196
- case "cmdExecutionSuccess":
197
- console.log("Sending cmdExecutionSuccess event for cmdId:", data.cmdId);
198
- this.sendEvent(this.events.cmdExecutionSuccess, data.cmdId);
199
- break;
200
- case "cmdExecutionFailure":
201
- console.log("Sending cmdExecutionFailure event for cmdId:", data.cmdId);
202
- this.sendEvent(this.events.cmdExecutionFailure, data.cmdId);
203
- break;
204
- default:
205
- console.warn("Unknown command execution status type:", data.type);
206
- break;
207
- }
208
- }
209
- },
210
- });
211
187
  this.pageSet = new Set();
212
- this.networkMonitor = new NetworkMonitor();
188
+ this.pageMetaDataSet = new Set();
213
189
  this.lastKnownUrlPath = "";
214
- // TODO: what is world?
215
190
  this.world = { attach: () => {} };
216
191
  this.shouldTakeScreenshot = true;
217
192
  this.watcher = null;
193
+ this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
194
+ if (existsSync(this.networkEventsFolder)) {
195
+ rmSync(this.networkEventsFolder, { recursive: true, force: true });
196
+ }
218
197
  }
219
198
  events = {
220
199
  onFrameNavigate: "BVTRecorder.onFrameNavigate",
@@ -227,13 +206,14 @@ export class BVTRecorder {
227
206
  onGoto: "BVTRecorder.onGoto",
228
207
  cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
229
208
  cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
230
- cmdExecutionFailure: "BVTRecorder.cmdExecutionFailure",
209
+ cmdExecutionError: "BVTRecorder.cmdExecutionError",
210
+ interceptResults: "BVTRecorder.interceptResults",
231
211
  };
232
212
  bindings = {
233
213
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
234
214
  this.#activeFrame = frame;
235
215
  const nestFrmLoc = await findNestedFrameSelector(frame);
236
- console.log(`Time taken for action: ${event.statistics.time}`);
216
+ this.logger.info(`Time taken for action: ${event.statistics.time}`);
237
217
  await this.onAction({ ...event, nestFrmLoc });
238
218
  },
239
219
  __bvt_getMode: async () => {
@@ -252,8 +232,7 @@ export class BVTRecorder {
252
232
  await this.onClosePopup();
253
233
  },
254
234
  __bvt_log: async (src, message) => {
255
- // this.logger.info(message);
256
- console.log(`Inside Browser: ${message}`);
235
+ this.logger.info(`Inside Browser: ${message}`);
257
236
  },
258
237
  __bvt_getObject: (_src, obj) => {
259
238
  this.processObject(obj);
@@ -265,7 +244,6 @@ export class BVTRecorder {
265
244
  const locator = await this.web.page.locator(selector);
266
245
  const snapshot = await locator.ariaSnapshot();
267
246
  return snapshot;
268
- // Triggering workflow
269
247
  };
270
248
 
271
249
  processObject = async ({ type, action, value }) => {
@@ -316,10 +294,11 @@ export class BVTRecorder {
316
294
  }
317
295
 
318
296
  async _initBrowser({ url }) {
297
+ socketLogger.info("Only present in 1.0.1293-stage");
319
298
  this.#remoteDebuggerPort = await findAvailablePort();
320
299
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
321
300
 
322
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
301
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
323
302
  this.world = { attach: () => {} };
324
303
 
325
304
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
@@ -328,9 +307,10 @@ export class BVTRecorder {
328
307
  try {
329
308
  ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
330
309
  } catch (error) {
331
- console.error("Error reading ai_config.json", error);
310
+ this.logger.error("Error reading ai_config.json", error);
332
311
  }
333
312
  }
313
+ this.config = ai_config;
334
314
  const initScripts = {
335
315
  // recorderCjs: injectedScriptSource,
336
316
  scripts: [
@@ -339,16 +319,37 @@ export class BVTRecorder {
339
319
  ],
340
320
  };
341
321
 
342
- let startTime = Date.now();
343
322
  const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
344
- let stopTime = Date.now();
345
- this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
346
323
  this.bvtContext = bvtContext;
324
+ this.stepRunner = new BVTStepRunner({
325
+ projectDir: this.projectDir,
326
+ sendExecutionStatus: (data) => {
327
+ if (data && data.type) {
328
+ switch (data.type) {
329
+ case "cmdExecutionStart":
330
+ this.sendEvent(this.events.cmdExecutionStart, data);
331
+ break;
332
+ case "cmdExecutionSuccess":
333
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
334
+ break;
335
+ case "cmdExecutionError":
336
+ this.sendEvent(this.events.cmdExecutionError, data);
337
+ break;
338
+ case "interceptResults":
339
+ this.sendEvent(this.events.interceptResults, data);
340
+ break;
341
+ default:
342
+ break;
343
+ }
344
+ }
345
+ },
346
+ bvtContext: this.bvtContext,
347
+ });
347
348
  const context = bvtContext.playContext;
348
349
  this.context = context;
349
350
  this.web = bvtContext.stable || bvtContext.web;
351
+ this.web.tryAllStrategies = true;
350
352
  this.page = bvtContext.page;
351
-
352
353
  this.pageSet.add(this.page);
353
354
  this.lastKnownUrlPath = this._updateUrlPath();
354
355
  const browser = await this.context.browser();
@@ -376,13 +377,15 @@ export class BVTRecorder {
376
377
  }
377
378
  return;
378
379
  } catch (error) {
379
- console.error("Error evaluting in context:", error);
380
+ // console.error("Error evaluting in context:", error);
381
+ this.logger.error("Error evaluating in context:", error);
380
382
  }
381
383
  }
382
384
  }
383
385
 
384
386
  getMode() {
385
- console.log("getMode", this.#mode);
387
+ // console.log("getMode", this.#mode);
388
+ this.logger.info("Current mode:", this.#mode);
386
389
  return this.#mode;
387
390
  }
388
391
 
@@ -424,6 +427,8 @@ export class BVTRecorder {
424
427
  this.sendEvent(this.events.onBrowserClose);
425
428
  }
426
429
  } catch (error) {
430
+ this.logger.error("Error in page close event");
431
+ this.logger.error(error);
427
432
  console.error("Error in page close event");
428
433
  console.error(error);
429
434
  }
@@ -434,8 +439,10 @@ export class BVTRecorder {
434
439
  if (frame !== page.mainFrame()) return;
435
440
  this.handlePageTransition();
436
441
  } catch (error) {
442
+ this.logger.error("Error in handlePageTransition event");
443
+ this.logger.error(error);
437
444
  console.error("Error in handlePageTransition event");
438
- // console.error(error);
445
+ console.error(error);
439
446
  }
440
447
  try {
441
448
  if (frame !== this.#activeFrame) return;
@@ -454,6 +461,8 @@ export class BVTRecorder {
454
461
  // await this._setRecordingMode(frame);
455
462
  // await this._initPage(page);
456
463
  } catch (error) {
464
+ this.logger.error("Error in frame navigate event");
465
+ this.logger.error(error);
457
466
  console.error("Error in frame navigate event");
458
467
  // console.error(error);
459
468
  }
@@ -536,13 +545,9 @@ export class BVTRecorder {
536
545
 
537
546
  try {
538
547
  const result = await client.send("Page.getNavigationHistory");
539
- // console.log("Navigation History:", result);
540
548
  const entries = result.entries;
541
549
  const currentIndex = result.currentIndex;
542
550
 
543
- // ignore if currentIndex is not the last entry
544
- // if (currentIndex !== entries.length - 1) return;
545
-
546
551
  const currentEntry = entries[currentIndex];
547
552
  const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
548
553
  this.previousIndex = currentIndex;
@@ -555,6 +560,8 @@ export class BVTRecorder {
555
560
  navigationAction: transitionInfo.action,
556
561
  };
557
562
  } catch (error) {
563
+ this.logger.error("Error in getCurrentTransition event");
564
+ this.logger.error(error);
558
565
  console.error("Error in getTransistionType event", error);
559
566
  } finally {
560
567
  await client.detach();
@@ -623,6 +630,8 @@ export class BVTRecorder {
623
630
  // add listener for frame navigation on new tab
624
631
  this._addFrameNavigateListener(page);
625
632
  } catch (error) {
633
+ this.logger.error("Error in page event");
634
+ this.logger.error(error);
626
635
  console.error("Error in page event");
627
636
  console.error(error);
628
637
  }
@@ -664,6 +673,7 @@ export class BVTRecorder {
664
673
  const { data } = await client.send("Page.captureScreenshot", { format: "png" });
665
674
  return data;
666
675
  } catch (error) {
676
+ this.logger.error("Error in taking browser screenshot");
667
677
  console.error("Error in taking browser screenshot", error);
668
678
  } finally {
669
679
  await client.detach();
@@ -809,6 +819,15 @@ export class BVTRecorder {
809
819
  async abortExecution() {
810
820
  await this.stepRunner.abortExecution();
811
821
  }
822
+
823
+ async pauseExecution({ cmdId }) {
824
+ await this.stepRunner.pauseExecution(cmdId);
825
+ }
826
+
827
+ async resumeExecution({ cmdId }) {
828
+ await this.stepRunner.resumeExecution(cmdId);
829
+ }
830
+
812
831
  async dealyedRevertMode() {
813
832
  const timerId = setTimeout(async () => {
814
833
  await this.revertMode();
@@ -816,16 +835,21 @@ export class BVTRecorder {
816
835
  this.timerId = timerId;
817
836
  }
818
837
  async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
838
+ const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
819
839
  const _env = {
820
840
  TOKEN: this.TOKEN,
821
841
  TEMP_RUN: true,
822
842
  REPORT_FOLDER: this.bvtContext.reportFolder,
823
843
  BLINQ_ENV: this.envName,
824
- STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
825
- CURRENT_STEP_ID: step.id,
826
844
  };
827
845
 
828
846
  this.bvtContext.navigate = true;
847
+ this.bvtContext.loadedRoutes = null;
848
+ if (listenNetwork) {
849
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
850
+ } else {
851
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
852
+ }
829
853
  for (const [key, value] of Object.entries(_env)) {
830
854
  process.env[key] = value;
831
855
  }
@@ -843,9 +867,13 @@ export class BVTRecorder {
843
867
  parametersMap,
844
868
  envPath: this.envName,
845
869
  tags,
870
+ config: this.config,
846
871
  },
847
872
  this.bvtContext,
848
- options ? { ...options, skipBefore: !isFirstStep } : { skipBefore: !isFirstStep }
873
+ {
874
+ skipAfter,
875
+ skipBefore,
876
+ }
849
877
  );
850
878
  await this.revertMode();
851
879
  return { info };
@@ -886,6 +914,7 @@ export class BVTRecorder {
886
914
  step.commands = [];
887
915
  }
888
916
  }
917
+ return steps;
889
918
  // return getStepsAndCommandsForScenario({
890
919
  // name,
891
920
  // featureName,
@@ -893,6 +922,7 @@ export class BVTRecorder {
893
922
  // map: this.scenariosStepsMap,
894
923
  // });
895
924
  }
925
+
896
926
  async generateStepName({ commands, stepsNames, parameters, map }) {
897
927
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
898
928
  }
@@ -944,10 +974,11 @@ export class BVTRecorder {
944
974
  if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
945
975
  try {
946
976
  const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
947
- this.logger.info("Test data", testData);
977
+ // this.logger.info("Test data", testData);
948
978
  this.sendEvent(this.events.getTestData, testData);
949
979
  } catch (e) {
950
- this.logger.error("Error reading test data file", e);
980
+ // this.logger.error("Error reading test data file", e);
981
+ console.log("Error reading test data file", e);
951
982
  }
952
983
  }
953
984
 
@@ -956,10 +987,12 @@ export class BVTRecorder {
956
987
  this.watcher.on("all", async (event, path) => {
957
988
  try {
958
989
  const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
959
- this.logger.info("Test data", testData);
990
+ // this.logger.info("Test data", testData);
991
+ console.log("Test data changed", testData);
960
992
  this.sendEvent(this.events.getTestData, testData);
961
993
  } catch (e) {
962
- this.logger.error("Error reading test data file", e);
994
+ // this.logger.error("Error reading test data file", e);
995
+ console.log("Error reading test data file", e);
963
996
  }
964
997
  });
965
998
  }
@@ -992,7 +1025,7 @@ export class BVTRecorder {
992
1025
  .filter((file) => file.endsWith(".feature"))
993
1026
  .map((file) => path.join(this.projectDir, "features", file));
994
1027
  try {
995
- const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
1028
+ const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
996
1029
  const output = {};
997
1030
  parsedFiles.forEach((file) => {
998
1031
  if (!file.feature) return;
@@ -1016,10 +1049,11 @@ export class BVTRecorder {
1016
1049
  const stepParams = parseStepTextParameters(stepName);
1017
1050
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
1018
1051
  }
1052
+
1019
1053
  loadExistingScenario({ featureName, scenarioName }) {
1020
1054
  const step_definitions = loadStepDefinitions(this.projectDir);
1021
1055
  const featureFilePath = path.join(this.projectDir, "features", featureName);
1022
- const gherkinDoc = parseFeatureFile(featureFilePath);
1056
+ const gherkinDoc = this.parseFeatureFile(featureFilePath);
1023
1057
  const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
1024
1058
 
1025
1059
  const steps = [];
@@ -1044,6 +1078,7 @@ export class BVTRecorder {
1044
1078
  ..._s,
1045
1079
  keyword: step.keyword.trim(),
1046
1080
  };
1081
+ parseRouteFiles(this.projectDir, _step);
1047
1082
  steps.push(_step);
1048
1083
  }
1049
1084
  return {
@@ -1136,20 +1171,68 @@ export class BVTRecorder {
1136
1171
  return false;
1137
1172
  }
1138
1173
  }
1139
- }
1174
+ async initExecution({ tags = [] }) {
1175
+ // run before hooks
1176
+ const noopStep = {
1177
+ text: "Noop",
1178
+ isImplemented: true,
1179
+ };
1180
+ await this.runStep(
1181
+ {
1182
+ step: noopStep,
1183
+ parametersMap: {},
1184
+ tags,
1185
+ },
1186
+ {
1187
+ skipBefore: false,
1188
+ skipAfter: true,
1189
+ }
1190
+ );
1191
+ }
1192
+ async cleanupExecution({ tags = [] }) {
1193
+ // run after hooks
1194
+ const noopStep = {
1195
+ text: "Noop",
1196
+ isImplemented: true,
1197
+ };
1198
+ await this.runStep(
1199
+ {
1200
+ step: noopStep,
1201
+ parametersMap: {},
1202
+ tags,
1203
+ },
1204
+ {
1205
+ skipBefore: true,
1206
+ skipAfter: false,
1207
+ }
1208
+ );
1209
+ }
1210
+ async resetExecution({ tags = [] }) {
1211
+ // run after hooks followed by before hooks
1212
+ await this.cleanupExecution({ tags });
1213
+ await this.initExecution({ tags });
1214
+ }
1140
1215
 
1141
- const parseFeatureFile = (featureFilePath) => {
1142
- try {
1143
- let id = 0;
1144
- const uuidFn = () => (++id).toString(16);
1145
- const builder = new AstBuilder(uuidFn);
1146
- const matcher = new GherkinClassicTokenMatcher();
1147
- const parser = new Parser(builder, matcher);
1148
- const source = readFileSync(featureFilePath, "utf8");
1149
- const gherkinDocument = parser.parse(source);
1150
- return gherkinDocument;
1151
- } catch (e) {
1152
- console.log(e);
1216
+ parseFeatureFile(featureFilePath) {
1217
+ try {
1218
+ let id = 0;
1219
+ const uuidFn = () => (++id).toString(16);
1220
+ const builder = new AstBuilder(uuidFn);
1221
+ const matcher = new GherkinClassicTokenMatcher();
1222
+ const parser = new Parser(builder, matcher);
1223
+ const source = readFileSync(featureFilePath, "utf8");
1224
+ const gherkinDocument = parser.parse(source);
1225
+ return gherkinDocument;
1226
+ } catch (e) {
1227
+ this.logger.error(`Error parsing feature file: ${featureFilePath}`);
1228
+ console.log(e);
1229
+ }
1230
+ return {};
1153
1231
  }
1154
- return {};
1155
- };
1232
+
1233
+ stopRecordingNetwork(input) {
1234
+ if (this.bvtContext) {
1235
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
1236
+ }
1237
+ }
1238
+ }
@@ -1,9 +1,9 @@
1
1
  import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
2
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
3
3
  import path from "path";
4
4
  import url from "url";
5
5
  import { findFilesWithExtension, StepsDefinitions } from "../cucumber/steps_definitions.js";
6
- import { Feature } from "../cucumber/feature.js";
6
+ import { Feature, Step } from "../cucumber/feature.js";
7
7
  import { CodePage } from "../code_gen/page_reflection.js";
8
8
  import { getCommandsForImplementedStep, loadStepDefinitions } from "./step_utils.js";
9
9
  import { parseStepTextParameters } from "../cucumber/utils.js";
@@ -13,7 +13,6 @@ const uuidFn = () => (++id).toString(16);
13
13
  const builder = new AstBuilder(uuidFn);
14
14
  const matcher = new GherkinClassicTokenMatcher();
15
15
  const parser = new Parser(builder, matcher);
16
-
17
16
  let i = 0;
18
17
  const getImplId = () => {
19
18
  return `I-${i++}`;
@@ -57,6 +56,53 @@ function memorySizeOf(obj) {
57
56
  return sizeOf(obj);
58
57
  }
59
58
 
59
+ export function parseRouteFiles(projectDir, step) {
60
+ const routeFolder = path.join(projectDir, "data", "routes");
61
+ const templateRouteMap = new Map();
62
+
63
+ if (!existsSync(routeFolder)) {
64
+ step.routeItems = null;
65
+ return;
66
+ }
67
+
68
+ // Go over all the files in the route folder and parse them
69
+ const routeFiles = readdirSync(routeFolder).filter((file) => file.endsWith(".json"));
70
+ for (const file of routeFiles) {
71
+ const filePath = path.join(routeFolder, file);
72
+ const routeData = JSON.parse(readFileSync(filePath, "utf8"));
73
+ if (routeData && routeData.template) {
74
+ const template = routeData.template;
75
+ const routes = routeData.routes;
76
+
77
+ templateRouteMap.set(template, routes);
78
+ }
79
+ }
80
+
81
+ if (!existsSync(routeFolder)) {
82
+ return null;
83
+ } else if (step && step.text) {
84
+ // Convert the step text to cucumber template
85
+ const cucumberStep = new Step();
86
+ cucumberStep.text = step.text;
87
+ const template = cucumberStep.getTemplate();
88
+ if (templateRouteMap.has(template)) {
89
+ const routeItems = templateRouteMap.get(template);
90
+ routeItems.forEach((item) => {
91
+ const filters = item.filters || {};
92
+ const queryParams = filters?.queryParams || {};
93
+ const queryParamsArray = Object.keys(queryParams).map((key) => ({
94
+ key: key,
95
+ value: queryParams[key],
96
+ }));
97
+ filters.queryParams = queryParamsArray || [];
98
+ });
99
+ step.routeItems = routeItems;
100
+ } else {
101
+ step.routeItems = null;
102
+ }
103
+ }
104
+ }
105
+
60
106
  export const getImplementedSteps = async (projectDir) => {
61
107
  const foundErrors = [];
62
108
  try {
@@ -70,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
70
116
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
71
117
  //console.log({ utilsContent });
72
118
  writeFileSync(utilsFilePath, utilsContent, "utf8");
119
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
120
+ if (existsSync(hooksTemplateFilePath)) {
121
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
122
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
123
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
124
+ }
73
125
  } catch (error) {
74
126
  foundErrors.push({ error });
75
127
  }
@@ -168,7 +220,10 @@ export const getImplementedSteps = async (projectDir) => {
168
220
  }
169
221
  stepLineSet.add(stepLine);
170
222
  step.templateIndex = implementedSteps.length;
171
- implementedSteps.push({
223
+
224
+ parseRouteFiles(projectDir, step);
225
+
226
+ const implementedStep = {
172
227
  keyword: step.keyword.trim(),
173
228
  keywordAlias: step.keywordAlias?.trim(),
174
229
  text: updateStepText(template.pattern, step.parameters),
@@ -179,7 +234,10 @@ export const getImplementedSteps = async (projectDir) => {
179
234
  templateIndex: step.templateIndex,
180
235
  pattern: template.pattern,
181
236
  paths: template.paths,
182
- });
237
+ routeItems: step.routeItems,
238
+ };
239
+
240
+ implementedSteps.push(implementedStep);
183
241
  }
184
242
  }
185
243
 
@@ -236,17 +294,17 @@ export const getImplementedSteps = async (projectDir) => {
236
294
  for (const tag of scenario.tags) {
237
295
  delete tag.location;
238
296
  }
239
- // for (const scenario of scenarios) {
240
- // for (const step of scenario.steps) {
241
- // if (step.templateIndex === undefined) {
242
- // const cleanStepName = stepsDefinitions._stepNameToTemplate(step.text);
243
- // const index = implementedSteps.findIndex((istep) => {
244
- // return cleanStepName === istep.pattern;
245
- // });
246
- // step.templateIndex = index;
247
- // }
248
- // }
249
- // }
297
+ for (const scenario of scenarios) {
298
+ for (const step of scenario.steps) {
299
+ if (step.templateIndex === undefined) {
300
+ const cleanStepName = stepsDefinitions._stepNameToTemplate(step.text);
301
+ const index = implementedSteps.findIndex((istep) => {
302
+ return cleanStepName === istep.pattern;
303
+ });
304
+ step.templateIndex = index;
305
+ }
306
+ }
307
+ }
250
308
  }
251
309
  if (foundErrors.length > 0) {
252
310
  console.log("foundErrors", foundErrors);