@dev-blinq/cucumber_client 1.0.1349-dev → 1.0.1349-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 (53) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +107 -107
  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 +13 -23
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +941 -815
  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 +2 -45
  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 +107 -2
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +25 -11
  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 +6 -3
  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 +202 -89
  35. package/bin/client/recorderv3/implemented_steps.js +22 -12
  36. package/bin/client/recorderv3/index.js +59 -54
  37. package/bin/client/recorderv3/network.js +22 -5
  38. package/bin/client/recorderv3/scriptTest.js +1 -1
  39. package/bin/client/recorderv3/services.js +4 -16
  40. package/bin/client/recorderv3/step_runner.js +318 -209
  41. package/bin/client/recorderv3/step_utils.js +475 -16
  42. package/bin/client/recorderv3/update_feature.js +32 -30
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/run_cucumber.js +1 -1
  45. package/bin/client/scenario_report.js +0 -5
  46. package/bin/client/test_scenario.js +0 -1
  47. package/bin/client/upload-service.js +3 -2
  48. package/bin/client/utils/socket_logger.js +132 -0
  49. package/bin/index.js +2 -0
  50. package/bin/logger.js +3 -2
  51. package/bin/min/consoleApi.min.cjs +2 -3
  52. package/bin/min/injectedScript.min.cjs +16 -16
  53. package/package.json +21 -12
@@ -12,11 +12,12 @@ 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 { Step } from "../cucumber/feature.js";
19
-
17
+ import socketLogger from "../utils/socket_logger.js";
18
+ import { tmpdir } from "os";
19
+ import { faker } from "@faker-js/faker/locale/en_US";
20
+ import { chromium } from "playwright-core";
20
21
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
21
22
 
22
23
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -45,15 +46,15 @@ async function evaluate(frame, script) {
45
46
  async function findNestedFrameSelector(frame, obj) {
46
47
  try {
47
48
  const parent = frame.parentFrame();
48
- if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
49
49
  if (!parent) return { children: obj };
50
50
  const frameElement = await frame.frameElement();
51
51
  if (!frameElement) return;
52
52
  const selectors = await parent.evaluate((element) => {
53
- return window.getPWLocators(element);
53
+ return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
54
54
  }, frameElement);
55
55
  return findNestedFrameSelector(parent, { children: obj, selectors });
56
56
  } catch (e) {
57
+ socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
57
58
  console.error(e);
58
59
  }
59
60
  }
@@ -150,6 +151,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
150
151
  };
151
152
  }
152
153
  default: {
154
+ socketLogger.error(`Action not supported: ${action.name}`);
153
155
  console.log("action not supported", action);
154
156
  throw new Error("action not supported");
155
157
  }
@@ -161,6 +163,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
161
163
  * @property {string} projectDir
162
164
  * @property {string} TOKEN
163
165
  * @property {(name:string, data:any)=> void} sendEvent
166
+ * @property {Object} logger
164
167
  */
165
168
  export class BVTRecorder {
166
169
  #currentURL = "";
@@ -174,7 +177,6 @@ export class BVTRecorder {
174
177
  */
175
178
  constructor(initialState) {
176
179
  Object.assign(this, initialState);
177
- this.logger = logger;
178
180
  this.screenshotMap = new Map();
179
181
  this.snapshotMap = new Map();
180
182
  this.scenariosStepsMap = new Map();
@@ -184,40 +186,16 @@ export class BVTRecorder {
184
186
  projectDir: this.projectDir,
185
187
  logger: this.logger,
186
188
  });
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);
194
- this.sendEvent(this.events.cmdExecutionStart, data);
195
- break;
196
- case "cmdExecutionSuccess":
197
- console.log("Sending cmdExecutionSuccess event for cmdId:", data);
198
- this.sendEvent(this.events.cmdExecutionSuccess, data);
199
- break;
200
- case "cmdExecutionError":
201
- console.log("Sending cmdExecutionError event for cmdId:", data);
202
- this.sendEvent(this.events.cmdExecutionError, data);
203
- break;
204
- case "interceptResults":
205
- console.log("Sending interceptResults event");
206
- this.sendEvent(this.events.interceptResults, data);
207
- break;
208
- default:
209
- console.warn("Unknown command execution status type:", data.type);
210
- break;
211
- }
212
- }
213
- },
214
- });
215
189
  this.pageSet = new Set();
190
+ this.pageMetaDataSet = new Set();
216
191
  this.lastKnownUrlPath = "";
217
- // TODO: what is world?
218
192
  this.world = { attach: () => {} };
219
193
  this.shouldTakeScreenshot = true;
220
194
  this.watcher = null;
195
+ this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
196
+ if (existsSync(this.networkEventsFolder)) {
197
+ rmSync(this.networkEventsFolder, { recursive: true, force: true });
198
+ }
221
199
  }
222
200
  events = {
223
201
  onFrameNavigate: "BVTRecorder.onFrameNavigate",
@@ -232,12 +210,14 @@ export class BVTRecorder {
232
210
  cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
233
211
  cmdExecutionError: "BVTRecorder.cmdExecutionError",
234
212
  interceptResults: "BVTRecorder.interceptResults",
213
+ onDebugURLChange: "BVTRecorder.onDebugURLChange",
214
+ updateCommand: "BVTRecorder.updateCommand",
235
215
  };
236
216
  bindings = {
237
217
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
238
218
  this.#activeFrame = frame;
239
219
  const nestFrmLoc = await findNestedFrameSelector(frame);
240
- console.log(`Time taken for action: ${event.statistics.time}`);
220
+ this.logger.info(`Time taken for action: ${event.statistics.time}`);
241
221
  await this.onAction({ ...event, nestFrmLoc });
242
222
  },
243
223
  __bvt_getMode: async () => {
@@ -256,8 +236,7 @@ export class BVTRecorder {
256
236
  await this.onClosePopup();
257
237
  },
258
238
  __bvt_log: async (src, message) => {
259
- // this.logger.info(message);
260
- console.log(`Inside Browser: ${message}`);
239
+ this.logger.info(`Inside Browser: ${message}`);
261
240
  },
262
241
  __bvt_getObject: (_src, obj) => {
263
242
  this.processObject(obj);
@@ -269,7 +248,6 @@ export class BVTRecorder {
269
248
  const locator = await this.web.page.locator(selector);
270
249
  const snapshot = await locator.ariaSnapshot();
271
250
  return snapshot;
272
- // Triggering workflow
273
251
  };
274
252
 
275
253
  processObject = async ({ type, action, value }) => {
@@ -320,10 +298,11 @@ export class BVTRecorder {
320
298
  }
321
299
 
322
300
  async _initBrowser({ url }) {
301
+ socketLogger.info("Only present in 1.0.1293-stage");
323
302
  this.#remoteDebuggerPort = await findAvailablePort();
324
303
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
325
304
 
326
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
305
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
327
306
  this.world = { attach: () => {} };
328
307
 
329
308
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
@@ -332,9 +311,10 @@ export class BVTRecorder {
332
311
  try {
333
312
  ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
334
313
  } catch (error) {
335
- console.error("Error reading ai_config.json", error);
314
+ this.logger.error("Error reading ai_config.json", error);
336
315
  }
337
316
  }
317
+ this.config = ai_config;
338
318
  const initScripts = {
339
319
  // recorderCjs: injectedScriptSource,
340
320
  scripts: [
@@ -343,16 +323,37 @@ export class BVTRecorder {
343
323
  ],
344
324
  };
345
325
 
346
- let startTime = Date.now();
347
326
  const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
348
- let stopTime = Date.now();
349
- this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
350
327
  this.bvtContext = bvtContext;
328
+ this.stepRunner = new BVTStepRunner({
329
+ projectDir: this.projectDir,
330
+ sendExecutionStatus: (data) => {
331
+ if (data && data.type) {
332
+ switch (data.type) {
333
+ case "cmdExecutionStart":
334
+ this.sendEvent(this.events.cmdExecutionStart, data);
335
+ break;
336
+ case "cmdExecutionSuccess":
337
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
338
+ break;
339
+ case "cmdExecutionError":
340
+ this.sendEvent(this.events.cmdExecutionError, data);
341
+ break;
342
+ case "interceptResults":
343
+ this.sendEvent(this.events.interceptResults, data);
344
+ break;
345
+ default:
346
+ break;
347
+ }
348
+ }
349
+ },
350
+ bvtContext: this.bvtContext,
351
+ });
351
352
  const context = bvtContext.playContext;
352
353
  this.context = context;
353
354
  this.web = bvtContext.stable || bvtContext.web;
355
+ this.web.tryAllStrategies = true;
354
356
  this.page = bvtContext.page;
355
-
356
357
  this.pageSet.add(this.page);
357
358
  this.lastKnownUrlPath = this._updateUrlPath();
358
359
  const browser = await this.context.browser();
@@ -366,6 +367,14 @@ export class BVTRecorder {
366
367
  this.web.onRestoreSaveState = (url) => {
367
368
  this._initBrowser({ url });
368
369
  };
370
+
371
+ // create a second browser for locator generation
372
+ this.backgroundBrowser = await chromium.launch({
373
+ headless: true,
374
+ });
375
+ this.backgroundContext = await this.backgroundBrowser.newContext({});
376
+ await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
377
+ await this.backgroundContext.newPage();
369
378
  }
370
379
  async onClosePopup() {
371
380
  // console.log("close popups");
@@ -380,13 +389,15 @@ export class BVTRecorder {
380
389
  }
381
390
  return;
382
391
  } catch (error) {
383
- console.error("Error evaluting in context:", error);
392
+ // console.error("Error evaluting in context:", error);
393
+ this.logger.error("Error evaluating in context:", error);
384
394
  }
385
395
  }
386
396
  }
387
397
 
388
398
  getMode() {
389
- console.log("getMode", this.#mode);
399
+ // console.log("getMode", this.#mode);
400
+ this.logger.info("Current mode:", this.#mode);
390
401
  return this.#mode;
391
402
  }
392
403
 
@@ -428,6 +439,8 @@ export class BVTRecorder {
428
439
  this.sendEvent(this.events.onBrowserClose);
429
440
  }
430
441
  } catch (error) {
442
+ this.logger.error("Error in page close event");
443
+ this.logger.error(error);
431
444
  console.error("Error in page close event");
432
445
  console.error(error);
433
446
  }
@@ -438,8 +451,10 @@ export class BVTRecorder {
438
451
  if (frame !== page.mainFrame()) return;
439
452
  this.handlePageTransition();
440
453
  } catch (error) {
454
+ this.logger.error("Error in handlePageTransition event");
455
+ this.logger.error(error);
441
456
  console.error("Error in handlePageTransition event");
442
- // console.error(error);
457
+ console.error(error);
443
458
  }
444
459
  try {
445
460
  if (frame !== this.#activeFrame) return;
@@ -458,6 +473,8 @@ export class BVTRecorder {
458
473
  // await this._setRecordingMode(frame);
459
474
  // await this._initPage(page);
460
475
  } catch (error) {
476
+ this.logger.error("Error in frame navigate event");
477
+ this.logger.error(error);
461
478
  console.error("Error in frame navigate event");
462
479
  // console.error(error);
463
480
  }
@@ -540,13 +557,9 @@ export class BVTRecorder {
540
557
 
541
558
  try {
542
559
  const result = await client.send("Page.getNavigationHistory");
543
- // console.log("Navigation History:", result);
544
560
  const entries = result.entries;
545
561
  const currentIndex = result.currentIndex;
546
562
 
547
- // ignore if currentIndex is not the last entry
548
- // if (currentIndex !== entries.length - 1) return;
549
-
550
563
  const currentEntry = entries[currentIndex];
551
564
  const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
552
565
  this.previousIndex = currentIndex;
@@ -559,6 +572,8 @@ export class BVTRecorder {
559
572
  navigationAction: transitionInfo.action,
560
573
  };
561
574
  } catch (error) {
575
+ this.logger.error("Error in getCurrentTransition event");
576
+ this.logger.error(error);
562
577
  console.error("Error in getTransistionType event", error);
563
578
  } finally {
564
579
  await client.detach();
@@ -623,9 +638,12 @@ export class BVTRecorder {
623
638
  this.pageSet.add(page);
624
639
 
625
640
  await page.waitForLoadState("domcontentloaded");
641
+
626
642
  // add listener for frame navigation on new tab
627
643
  this._addFrameNavigateListener(page);
628
644
  } catch (error) {
645
+ this.logger.error("Error in page event");
646
+ this.logger.error(error);
629
647
  console.error("Error in page event");
630
648
  console.error(error);
631
649
  }
@@ -667,6 +685,7 @@ export class BVTRecorder {
667
685
  const { data } = await client.send("Page.captureScreenshot", { format: "png" });
668
686
  return data;
669
687
  } catch (error) {
688
+ this.logger.error("Error in taking browser screenshot");
670
689
  console.error("Error in taking browser screenshot", error);
671
690
  } finally {
672
691
  await client.detach();
@@ -682,6 +701,52 @@ export class BVTRecorder {
682
701
  console.error("Error in saving screenshot: ", error);
683
702
  }
684
703
  }
704
+ async generateLocators(event) {
705
+ const snapshotDetails = event.snapshotDetails;
706
+ if (!snapshotDetails) {
707
+ throw new Error("No snapshot details found");
708
+ }
709
+ const mode = event.mode;
710
+ const inputID = event.element.inputID;
711
+
712
+ const { id, contextId, doc } = snapshotDetails;
713
+ // const selector = `[data-blinq-id="${id}"]`;
714
+ const newPage = await this.backgroundContext.newPage();
715
+ await newPage.setContent(doc);
716
+ const locatorsObj = await newPage.evaluate(
717
+ ([id, contextId, mode]) => {
718
+ const recorder = window.__bvt_Recorder;
719
+ const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
720
+ const el = document.querySelector(`[data-blinq-id="${id}"]`);
721
+ if (contextElement) {
722
+ const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
723
+ return result;
724
+ }
725
+ const isRecordingText = mode === "recordingText";
726
+ return recorder.locatorGenerator.getElementLocators(el, {
727
+ excludeText: isRecordingText,
728
+ });
729
+ },
730
+ [id, contextId, mode]
731
+ );
732
+
733
+ console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
734
+ await newPage.close();
735
+ if (event.nestFrmLoc?.children) {
736
+ locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
737
+ }
738
+
739
+ this.sendEvent(this.events.updateCommand, {
740
+ locators: {
741
+ locators: locatorsObj.locators,
742
+ nestFrmLoc: locatorsObj.nestFrmLoc,
743
+ iframe_src: !event.frame.isTop ? event.frame.url : undefined,
744
+ },
745
+ allStrategyLocators: locatorsObj.allStrategyLocators,
746
+ inputID,
747
+ });
748
+ // const
749
+ }
685
750
  async onAction(event) {
686
751
  this._updateUrlPath();
687
752
  // const locators = this.overlayLocators(event);
@@ -695,26 +760,26 @@ export class BVTRecorder {
695
760
  event.mode === "recordingHover",
696
761
  event.mode === "multiInspecting"
697
762
  ),
698
- locators: {
699
- locators: event.locators,
700
- iframe_src: !event.frame.isTop ? event.frame.url : undefined,
701
- },
702
- allStrategyLocators: event.allStrategyLocators,
763
+ // locators: {
764
+ // locators: event.locators,
765
+ // iframe_src: !event.frame.isTop ? event.frame.url : undefined,
766
+ // },
767
+ // allStrategyLocators: event.allStrategyLocators,
703
768
  url: event.frame.url,
704
769
  title: event.frame.title,
705
770
  extract: {},
706
771
  lastKnownUrlPath: this.lastKnownUrlPath,
707
772
  };
708
- if (event.nestFrmLoc?.children) {
709
- cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
710
- }
773
+ // if (event.nestFrmLoc?.children) {
774
+ // cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
775
+ // }
711
776
  // this.logger.info({ event });
712
777
  if (this.shouldTakeScreenshot) {
713
778
  await this.storeScreenshot(event);
714
779
  }
715
-
716
780
  this.sendEvent(this.events.onNewCommand, cmdEvent);
717
781
  this._updateUrlPath();
782
+ await this.generateLocators(event);
718
783
  }
719
784
  _updateUrlPath() {
720
785
  try {
@@ -788,7 +853,6 @@ export class BVTRecorder {
788
853
  }
789
854
 
790
855
  async startRecordingInput() {
791
- console.log("startRecordingInput");
792
856
  await this.setMode("recordingInput");
793
857
  }
794
858
  async stopRecordingInput() {
@@ -812,9 +876,17 @@ export class BVTRecorder {
812
876
  }
813
877
 
814
878
  async abortExecution() {
815
- this.bvtContext.web.abortedExecution = true;
816
879
  await this.stepRunner.abortExecution();
817
880
  }
881
+
882
+ async pauseExecution({ cmdId }) {
883
+ await this.stepRunner.pauseExecution(cmdId);
884
+ }
885
+
886
+ async resumeExecution({ cmdId }) {
887
+ await this.stepRunner.resumeExecution(cmdId);
888
+ }
889
+
818
890
  async dealyedRevertMode() {
819
891
  const timerId = setTimeout(async () => {
820
892
  await this.revertMode();
@@ -828,13 +900,15 @@ export class BVTRecorder {
828
900
  TEMP_RUN: true,
829
901
  REPORT_FOLDER: this.bvtContext.reportFolder,
830
902
  BLINQ_ENV: this.envName,
831
- STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
832
- CURRENT_STEP_ID: step.id,
833
903
  };
834
904
 
835
905
  this.bvtContext.navigate = true;
836
906
  this.bvtContext.loadedRoutes = null;
837
- this.bvtContext.web.abortedExecution = false;
907
+ if (listenNetwork) {
908
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
909
+ } else {
910
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
911
+ }
838
912
  for (const [key, value] of Object.entries(_env)) {
839
913
  process.env[key] = value;
840
914
  }
@@ -852,6 +926,7 @@ export class BVTRecorder {
852
926
  parametersMap,
853
927
  envPath: this.envName,
854
928
  tags,
929
+ config: this.config,
855
930
  },
856
931
  this.bvtContext,
857
932
  {
@@ -906,6 +981,7 @@ export class BVTRecorder {
906
981
  // map: this.scenariosStepsMap,
907
982
  // });
908
983
  }
984
+
909
985
  async generateStepName({ commands, stepsNames, parameters, map }) {
910
986
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
911
987
  }
@@ -957,10 +1033,11 @@ export class BVTRecorder {
957
1033
  if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
958
1034
  try {
959
1035
  const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
960
- this.logger.info("Test data", testData);
1036
+ // this.logger.info("Test data", testData);
961
1037
  this.sendEvent(this.events.getTestData, testData);
962
1038
  } catch (e) {
963
- this.logger.error("Error reading test data file", e);
1039
+ // this.logger.error("Error reading test data file", e);
1040
+ console.log("Error reading test data file", e);
964
1041
  }
965
1042
  }
966
1043
 
@@ -969,10 +1046,12 @@ export class BVTRecorder {
969
1046
  this.watcher.on("all", async (event, path) => {
970
1047
  try {
971
1048
  const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
972
- this.logger.info("Test data", testData);
1049
+ // this.logger.info("Test data", testData);
1050
+ console.log("Test data changed", testData);
973
1051
  this.sendEvent(this.events.getTestData, testData);
974
1052
  } catch (e) {
975
- this.logger.error("Error reading test data file", e);
1053
+ // this.logger.error("Error reading test data file", e);
1054
+ console.log("Error reading test data file", e);
976
1055
  }
977
1056
  });
978
1057
  }
@@ -1005,7 +1084,7 @@ export class BVTRecorder {
1005
1084
  .filter((file) => file.endsWith(".feature"))
1006
1085
  .map((file) => path.join(this.projectDir, "features", file));
1007
1086
  try {
1008
- const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
1087
+ const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
1009
1088
  const output = {};
1010
1089
  parsedFiles.forEach((file) => {
1011
1090
  if (!file.feature) return;
@@ -1033,7 +1112,7 @@ export class BVTRecorder {
1033
1112
  loadExistingScenario({ featureName, scenarioName }) {
1034
1113
  const step_definitions = loadStepDefinitions(this.projectDir);
1035
1114
  const featureFilePath = path.join(this.projectDir, "features", featureName);
1036
- const gherkinDoc = parseFeatureFile(featureFilePath);
1115
+ const gherkinDoc = this.parseFeatureFile(featureFilePath);
1037
1116
  const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
1038
1117
 
1039
1118
  const steps = [];
@@ -1189,23 +1268,57 @@ export class BVTRecorder {
1189
1268
  }
1190
1269
  async resetExecution({ tags = [] }) {
1191
1270
  // run after hooks followed by before hooks
1192
- await this.runAfterHooks({ tags });
1193
- await this.runBeforeHooks({ tags });
1271
+ await this.cleanupExecution({ tags });
1272
+ await this.initExecution({ tags });
1194
1273
  }
1195
- }
1196
1274
 
1197
- const parseFeatureFile = (featureFilePath) => {
1198
- try {
1199
- let id = 0;
1200
- const uuidFn = () => (++id).toString(16);
1201
- const builder = new AstBuilder(uuidFn);
1202
- const matcher = new GherkinClassicTokenMatcher();
1203
- const parser = new Parser(builder, matcher);
1204
- const source = readFileSync(featureFilePath, "utf8");
1205
- const gherkinDocument = parser.parse(source);
1206
- return gherkinDocument;
1207
- } catch (e) {
1208
- console.log(e);
1275
+ parseFeatureFile(featureFilePath) {
1276
+ try {
1277
+ let id = 0;
1278
+ const uuidFn = () => (++id).toString(16);
1279
+ const builder = new AstBuilder(uuidFn);
1280
+ const matcher = new GherkinClassicTokenMatcher();
1281
+ const parser = new Parser(builder, matcher);
1282
+ const source = readFileSync(featureFilePath, "utf8");
1283
+ const gherkinDocument = parser.parse(source);
1284
+ return gherkinDocument;
1285
+ } catch (e) {
1286
+ this.logger.error(`Error parsing feature file: ${featureFilePath}`);
1287
+ console.log(e);
1288
+ }
1289
+ return {};
1209
1290
  }
1210
- return {};
1211
- };
1291
+
1292
+ stopRecordingNetwork(input) {
1293
+ if (this.bvtContext) {
1294
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
1295
+ }
1296
+ }
1297
+
1298
+ async fakeParams(params) {
1299
+ const newFakeParams = {};
1300
+ Object.keys(params).forEach((key) => {
1301
+ if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
1302
+ newFakeParams[key] = params[key];
1303
+ return;
1304
+ }
1305
+
1306
+ try {
1307
+ const value = params[key].substring(2, params[key].length - 2).trim();
1308
+ const faking = value.split("(")[0].split(".");
1309
+ let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
1310
+ argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
1311
+ let fakeFunc = faker;
1312
+ faking.forEach((f) => {
1313
+ fakeFunc = fakeFunc[f];
1314
+ });
1315
+ const newValue = fakeFunc(argument);
1316
+ newFakeParams[key] = newValue;
1317
+ } catch (error) {
1318
+ newFakeParams[key] = params[key];
1319
+ }
1320
+ });
1321
+
1322
+ return newFakeParams;
1323
+ }
1324
+ }
@@ -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++}`;
@@ -61,6 +60,11 @@ export function parseRouteFiles(projectDir, step) {
61
60
  const routeFolder = path.join(projectDir, "data", "routes");
62
61
  const templateRouteMap = new Map();
63
62
 
63
+ if (!existsSync(routeFolder)) {
64
+ step.routeItems = null;
65
+ return;
66
+ }
67
+
64
68
  // Go over all the files in the route folder and parse them
65
69
  const routeFiles = readdirSync(routeFolder).filter((file) => file.endsWith(".json"));
66
70
  for (const file of routeFiles) {
@@ -112,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
112
116
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
113
117
  //console.log({ utilsContent });
114
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
+ }
115
125
  } catch (error) {
116
126
  foundErrors.push({ error });
117
127
  }
@@ -284,17 +294,17 @@ export const getImplementedSteps = async (projectDir) => {
284
294
  for (const tag of scenario.tags) {
285
295
  delete tag.location;
286
296
  }
287
- // for (const scenario of scenarios) {
288
- // for (const step of scenario.steps) {
289
- // if (step.templateIndex === undefined) {
290
- // const cleanStepName = stepsDefinitions._stepNameToTemplate(step.text);
291
- // const index = implementedSteps.findIndex((istep) => {
292
- // return cleanStepName === istep.pattern;
293
- // });
294
- // step.templateIndex = index;
295
- // }
296
- // }
297
- // }
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
+ }
298
308
  }
299
309
  if (foundErrors.length > 0) {
300
310
  console.log("foundErrors", foundErrors);