@dev-blinq/cucumber_client 1.0.1361-dev → 1.0.1361-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 (48) 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/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +13 -23
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +163 -44
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +41 -0
  16. package/bin/assets/templates/utils_template.txt +2 -45
  17. package/bin/client/apiTest/apiTest.js +6 -0
  18. package/bin/client/code_cleanup/utils.js +5 -1
  19. package/bin/client/code_gen/api_codegen.js +2 -2
  20. package/bin/client/code_gen/code_inversion.js +107 -2
  21. package/bin/client/code_gen/function_signature.js +4 -0
  22. package/bin/client/code_gen/page_reflection.js +846 -906
  23. package/bin/client/code_gen/playwright_codeget.js +25 -11
  24. package/bin/client/cucumber/feature.js +4 -0
  25. package/bin/client/cucumber/feature_data.js +2 -2
  26. package/bin/client/cucumber/project_to_document.js +9 -3
  27. package/bin/client/cucumber/steps_definitions.js +6 -3
  28. package/bin/client/cucumber_selector.js +17 -1
  29. package/bin/client/local_agent.js +4 -3
  30. package/bin/client/parse_feature_file.js +23 -26
  31. package/bin/client/playground/projects/env.json +2 -2
  32. package/bin/client/project.js +186 -196
  33. package/bin/client/recorderv3/bvt_recorder.js +213 -90
  34. package/bin/client/recorderv3/implemented_steps.js +8 -0
  35. package/bin/client/recorderv3/index.js +59 -54
  36. package/bin/client/recorderv3/scriptTest.js +1 -1
  37. package/bin/client/recorderv3/services.js +4 -16
  38. package/bin/client/recorderv3/step_runner.js +315 -205
  39. package/bin/client/recorderv3/step_utils.js +475 -16
  40. package/bin/client/recorderv3/update_feature.js +9 -5
  41. package/bin/client/recording.js +1 -0
  42. package/bin/client/upload-service.js +3 -2
  43. package/bin/client/utils/socket_logger.js +132 -0
  44. package/bin/index.js +1 -0
  45. package/bin/logger.js +3 -2
  46. package/bin/min/consoleApi.min.cjs +2 -3
  47. package/bin/min/injectedScript.min.cjs +16 -16
  48. package/package.json +20 -10
@@ -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,7 +46,6 @@ 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;
@@ -54,6 +54,7 @@ async function findNestedFrameSelector(frame, obj) {
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,17 +151,30 @@ 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
  }
156
158
  }
157
159
  };
160
+ const diffPaths = (currentPath, newPath) => {
161
+ const currentDomain = new URL(currentPath).hostname;
162
+ const newDomain = new URL(newPath).hostname;
163
+ if (currentDomain !== newDomain) {
164
+ return true;
165
+ } else {
166
+ const currentRoute = new URL(currentPath).pathname;
167
+ const newRoute = new URL(newPath).pathname;
168
+ return currentRoute !== newRoute;
169
+ }
170
+ };
158
171
  /**
159
172
  * @typedef {Object} BVTRecorderInput
160
173
  * @property {string} envName
161
174
  * @property {string} projectDir
162
175
  * @property {string} TOKEN
163
176
  * @property {(name:string, data:any)=> void} sendEvent
177
+ * @property {Object} logger
164
178
  */
165
179
  export class BVTRecorder {
166
180
  #currentURL = "";
@@ -174,7 +188,6 @@ export class BVTRecorder {
174
188
  */
175
189
  constructor(initialState) {
176
190
  Object.assign(this, initialState);
177
- this.logger = logger;
178
191
  this.screenshotMap = new Map();
179
192
  this.snapshotMap = new Map();
180
193
  this.scenariosStepsMap = new Map();
@@ -184,40 +197,16 @@ export class BVTRecorder {
184
197
  projectDir: this.projectDir,
185
198
  logger: this.logger,
186
199
  });
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
200
  this.pageSet = new Set();
201
+ this.pageMetaDataSet = new Set();
216
202
  this.lastKnownUrlPath = "";
217
- // TODO: what is world?
218
203
  this.world = { attach: () => {} };
219
204
  this.shouldTakeScreenshot = true;
220
205
  this.watcher = null;
206
+ this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
207
+ if (existsSync(this.networkEventsFolder)) {
208
+ rmSync(this.networkEventsFolder, { recursive: true, force: true });
209
+ }
221
210
  }
222
211
  events = {
223
212
  onFrameNavigate: "BVTRecorder.onFrameNavigate",
@@ -232,12 +221,14 @@ export class BVTRecorder {
232
221
  cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
233
222
  cmdExecutionError: "BVTRecorder.cmdExecutionError",
234
223
  interceptResults: "BVTRecorder.interceptResults",
224
+ onDebugURLChange: "BVTRecorder.onDebugURLChange",
225
+ updateCommand: "BVTRecorder.updateCommand",
235
226
  };
236
227
  bindings = {
237
228
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
238
229
  this.#activeFrame = frame;
239
230
  const nestFrmLoc = await findNestedFrameSelector(frame);
240
- console.log(`Time taken for action: ${event.statistics.time}`);
231
+ this.logger.info(`Time taken for action: ${event.statistics.time}`);
241
232
  await this.onAction({ ...event, nestFrmLoc });
242
233
  },
243
234
  __bvt_getMode: async () => {
@@ -256,8 +247,7 @@ export class BVTRecorder {
256
247
  await this.onClosePopup();
257
248
  },
258
249
  __bvt_log: async (src, message) => {
259
- // this.logger.info(message);
260
- console.log(`Inside Browser: ${message}`);
250
+ this.logger.info(`Inside Browser: ${message}`);
261
251
  },
262
252
  __bvt_getObject: (_src, obj) => {
263
253
  this.processObject(obj);
@@ -319,10 +309,11 @@ export class BVTRecorder {
319
309
  }
320
310
 
321
311
  async _initBrowser({ url }) {
312
+ socketLogger.info("Only present in 1.0.1293-stage");
322
313
  this.#remoteDebuggerPort = await findAvailablePort();
323
314
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
324
315
 
325
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
316
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
326
317
  this.world = { attach: () => {} };
327
318
 
328
319
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
@@ -331,7 +322,7 @@ export class BVTRecorder {
331
322
  try {
332
323
  ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
333
324
  } catch (error) {
334
- console.error("Error reading ai_config.json", error);
325
+ this.logger.error("Error reading ai_config.json", error);
335
326
  }
336
327
  }
337
328
  this.config = ai_config;
@@ -343,16 +334,37 @@ export class BVTRecorder {
343
334
  ],
344
335
  };
345
336
 
346
- let startTime = Date.now();
347
337
  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
338
  this.bvtContext = bvtContext;
339
+ this.stepRunner = new BVTStepRunner({
340
+ projectDir: this.projectDir,
341
+ sendExecutionStatus: (data) => {
342
+ if (data && data.type) {
343
+ switch (data.type) {
344
+ case "cmdExecutionStart":
345
+ this.sendEvent(this.events.cmdExecutionStart, data);
346
+ break;
347
+ case "cmdExecutionSuccess":
348
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
349
+ break;
350
+ case "cmdExecutionError":
351
+ this.sendEvent(this.events.cmdExecutionError, data);
352
+ break;
353
+ case "interceptResults":
354
+ this.sendEvent(this.events.interceptResults, data);
355
+ break;
356
+ default:
357
+ break;
358
+ }
359
+ }
360
+ },
361
+ bvtContext: this.bvtContext,
362
+ });
351
363
  const context = bvtContext.playContext;
352
364
  this.context = context;
353
365
  this.web = bvtContext.stable || bvtContext.web;
366
+ this.web.tryAllStrategies = true;
354
367
  this.page = bvtContext.page;
355
-
356
368
  this.pageSet.add(this.page);
357
369
  this.lastKnownUrlPath = this._updateUrlPath();
358
370
  const browser = await this.context.browser();
@@ -366,6 +378,14 @@ export class BVTRecorder {
366
378
  this.web.onRestoreSaveState = (url) => {
367
379
  this._initBrowser({ url });
368
380
  };
381
+
382
+ // create a second browser for locator generation
383
+ this.backgroundBrowser = await chromium.launch({
384
+ headless: true,
385
+ });
386
+ this.backgroundContext = await this.backgroundBrowser.newContext({});
387
+ await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
388
+ await this.backgroundContext.newPage();
369
389
  }
370
390
  async onClosePopup() {
371
391
  // console.log("close popups");
@@ -380,13 +400,15 @@ export class BVTRecorder {
380
400
  }
381
401
  return;
382
402
  } catch (error) {
383
- console.error("Error evaluting in context:", error);
403
+ // console.error("Error evaluting in context:", error);
404
+ this.logger.error("Error evaluating in context:", error);
384
405
  }
385
406
  }
386
407
  }
387
408
 
388
409
  getMode() {
389
- console.log("getMode", this.#mode);
410
+ // console.log("getMode", this.#mode);
411
+ this.logger.info("Current mode:", this.#mode);
390
412
  return this.#mode;
391
413
  }
392
414
 
@@ -411,7 +433,7 @@ export class BVTRecorder {
411
433
 
412
434
  // eval init script on current tab
413
435
  // await this._initPage(this.page);
414
- this.#currentURL = new URL(url).pathname;
436
+ this.#currentURL = url;
415
437
 
416
438
  await this.page.dispatchEvent("html", "scroll");
417
439
  await delay(1000);
@@ -428,6 +450,8 @@ export class BVTRecorder {
428
450
  this.sendEvent(this.events.onBrowserClose);
429
451
  }
430
452
  } catch (error) {
453
+ this.logger.error("Error in page close event");
454
+ this.logger.error(error);
431
455
  console.error("Error in page close event");
432
456
  console.error(error);
433
457
  }
@@ -438,8 +462,10 @@ export class BVTRecorder {
438
462
  if (frame !== page.mainFrame()) return;
439
463
  this.handlePageTransition();
440
464
  } catch (error) {
465
+ this.logger.error("Error in handlePageTransition event");
466
+ this.logger.error(error);
441
467
  console.error("Error in handlePageTransition event");
442
- // console.error(error);
468
+ console.error(error);
443
469
  }
444
470
  try {
445
471
  if (frame !== this.#activeFrame) return;
@@ -449,15 +475,18 @@ export class BVTRecorder {
449
475
  element: { inputID: "frame" },
450
476
  });
451
477
 
452
- const newPath = new URL(frame.url()).pathname;
478
+ const newUrl = frame.url();
479
+ const newPath = new URL(newUrl).pathname;
453
480
  const newTitle = await frame.title();
454
- if (newPath !== this.#currentURL) {
481
+ const changed = diffPaths(this.#currentURL, newUrl);
482
+
483
+ if (changed) {
455
484
  this.sendEvent(this.events.onFrameNavigate, { url: newPath, title: newTitle });
456
- this.#currentURL = newPath;
485
+ this.#currentURL = newUrl;
457
486
  }
458
- // await this._setRecordingMode(frame);
459
- // await this._initPage(page);
460
487
  } catch (error) {
488
+ this.logger.error("Error in frame navigate event");
489
+ this.logger.error(error);
461
490
  console.error("Error in frame navigate event");
462
491
  // console.error(error);
463
492
  }
@@ -540,13 +569,9 @@ export class BVTRecorder {
540
569
 
541
570
  try {
542
571
  const result = await client.send("Page.getNavigationHistory");
543
- // console.log("Navigation History:", result);
544
572
  const entries = result.entries;
545
573
  const currentIndex = result.currentIndex;
546
574
 
547
- // ignore if currentIndex is not the last entry
548
- // if (currentIndex !== entries.length - 1) return;
549
-
550
575
  const currentEntry = entries[currentIndex];
551
576
  const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
552
577
  this.previousIndex = currentIndex;
@@ -559,6 +584,8 @@ export class BVTRecorder {
559
584
  navigationAction: transitionInfo.action,
560
585
  };
561
586
  } catch (error) {
587
+ this.logger.error("Error in getCurrentTransition event");
588
+ this.logger.error(error);
562
589
  console.error("Error in getTransistionType event", error);
563
590
  } finally {
564
591
  await client.detach();
@@ -627,6 +654,8 @@ export class BVTRecorder {
627
654
  // add listener for frame navigation on new tab
628
655
  this._addFrameNavigateListener(page);
629
656
  } catch (error) {
657
+ this.logger.error("Error in page event");
658
+ this.logger.error(error);
630
659
  console.error("Error in page event");
631
660
  console.error(error);
632
661
  }
@@ -668,6 +697,7 @@ export class BVTRecorder {
668
697
  const { data } = await client.send("Page.captureScreenshot", { format: "png" });
669
698
  return data;
670
699
  } catch (error) {
700
+ this.logger.error("Error in taking browser screenshot");
671
701
  console.error("Error in taking browser screenshot", error);
672
702
  } finally {
673
703
  await client.detach();
@@ -683,6 +713,52 @@ export class BVTRecorder {
683
713
  console.error("Error in saving screenshot: ", error);
684
714
  }
685
715
  }
716
+ async generateLocators(event) {
717
+ const snapshotDetails = event.snapshotDetails;
718
+ if (!snapshotDetails) {
719
+ throw new Error("No snapshot details found");
720
+ }
721
+ const mode = event.mode;
722
+ const inputID = event.element.inputID;
723
+
724
+ const { id, contextId, doc } = snapshotDetails;
725
+ // const selector = `[data-blinq-id="${id}"]`;
726
+ const newPage = await this.backgroundContext.newPage();
727
+ await newPage.setContent(doc, { waitUntil: "domcontentloaded" });
728
+ const locatorsObj = await newPage.evaluate(
729
+ ([id, contextId, mode]) => {
730
+ const recorder = window.__bvt_Recorder;
731
+ const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
732
+ const el = document.querySelector(`[data-blinq-id="${id}"]`);
733
+ if (contextElement) {
734
+ const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
735
+ return result;
736
+ }
737
+ const isRecordingText = mode === "recordingText";
738
+ return recorder.locatorGenerator.getElementLocators(el, {
739
+ excludeText: isRecordingText,
740
+ });
741
+ },
742
+ [id, contextId, mode]
743
+ );
744
+
745
+ console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
746
+ await newPage.close();
747
+ if (event.nestFrmLoc?.children) {
748
+ locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
749
+ }
750
+
751
+ this.sendEvent(this.events.updateCommand, {
752
+ locators: {
753
+ locators: locatorsObj.locators,
754
+ nestFrmLoc: locatorsObj.nestFrmLoc,
755
+ iframe_src: !event.frame.isTop ? event.frame.url : undefined,
756
+ },
757
+ allStrategyLocators: locatorsObj.allStrategyLocators,
758
+ inputID,
759
+ });
760
+ // const
761
+ }
686
762
  async onAction(event) {
687
763
  this._updateUrlPath();
688
764
  // const locators = this.overlayLocators(event);
@@ -696,25 +772,26 @@ export class BVTRecorder {
696
772
  event.mode === "recordingHover",
697
773
  event.mode === "multiInspecting"
698
774
  ),
699
- locators: {
700
- locators: event.locators,
701
- iframe_src: !event.frame.isTop ? event.frame.url : undefined,
702
- },
703
- allStrategyLocators: event.allStrategyLocators,
775
+ // locators: {
776
+ // locators: event.locators,
777
+ // iframe_src: !event.frame.isTop ? event.frame.url : undefined,
778
+ // },
779
+ // allStrategyLocators: event.allStrategyLocators,
704
780
  url: event.frame.url,
705
781
  title: event.frame.title,
706
782
  extract: {},
707
783
  lastKnownUrlPath: this.lastKnownUrlPath,
708
784
  };
709
- if (event.nestFrmLoc?.children) {
710
- cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
711
- }
785
+ // if (event.nestFrmLoc?.children) {
786
+ // cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
787
+ // }
712
788
  // this.logger.info({ event });
713
789
  if (this.shouldTakeScreenshot) {
714
790
  await this.storeScreenshot(event);
715
791
  }
716
792
  this.sendEvent(this.events.onNewCommand, cmdEvent);
717
793
  this._updateUrlPath();
794
+ await this.generateLocators(event);
718
795
  }
719
796
  _updateUrlPath() {
720
797
  try {
@@ -788,7 +865,6 @@ export class BVTRecorder {
788
865
  }
789
866
 
790
867
  async startRecordingInput() {
791
- console.log("startRecordingInput");
792
868
  await this.setMode("recordingInput");
793
869
  }
794
870
  async stopRecordingInput() {
@@ -812,9 +888,17 @@ export class BVTRecorder {
812
888
  }
813
889
 
814
890
  async abortExecution() {
815
- this.bvtContext.web.abortedExecution = true;
816
891
  await this.stepRunner.abortExecution();
817
892
  }
893
+
894
+ async pauseExecution({ cmdId }) {
895
+ await this.stepRunner.pauseExecution(cmdId);
896
+ }
897
+
898
+ async resumeExecution({ cmdId }) {
899
+ await this.stepRunner.resumeExecution(cmdId);
900
+ }
901
+
818
902
  async dealyedRevertMode() {
819
903
  const timerId = setTimeout(async () => {
820
904
  await this.revertMode();
@@ -828,13 +912,15 @@ export class BVTRecorder {
828
912
  TEMP_RUN: true,
829
913
  REPORT_FOLDER: this.bvtContext.reportFolder,
830
914
  BLINQ_ENV: this.envName,
831
- STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
832
- CURRENT_STEP_ID: step.id,
833
915
  };
834
916
 
835
917
  this.bvtContext.navigate = true;
836
918
  this.bvtContext.loadedRoutes = null;
837
- this.bvtContext.web.abortedExecution = false;
919
+ if (listenNetwork) {
920
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
921
+ } else {
922
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
923
+ }
838
924
  for (const [key, value] of Object.entries(_env)) {
839
925
  process.env[key] = value;
840
926
  }
@@ -959,10 +1045,11 @@ export class BVTRecorder {
959
1045
  if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
960
1046
  try {
961
1047
  const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
962
- this.logger.info("Test data", testData);
1048
+ // this.logger.info("Test data", testData);
963
1049
  this.sendEvent(this.events.getTestData, testData);
964
1050
  } catch (e) {
965
- this.logger.error("Error reading test data file", e);
1051
+ // this.logger.error("Error reading test data file", e);
1052
+ console.log("Error reading test data file", e);
966
1053
  }
967
1054
  }
968
1055
 
@@ -971,10 +1058,12 @@ export class BVTRecorder {
971
1058
  this.watcher.on("all", async (event, path) => {
972
1059
  try {
973
1060
  const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
974
- this.logger.info("Test data", testData);
1061
+ // this.logger.info("Test data", testData);
1062
+ console.log("Test data changed", testData);
975
1063
  this.sendEvent(this.events.getTestData, testData);
976
1064
  } catch (e) {
977
- this.logger.error("Error reading test data file", e);
1065
+ // this.logger.error("Error reading test data file", e);
1066
+ console.log("Error reading test data file", e);
978
1067
  }
979
1068
  });
980
1069
  }
@@ -1007,7 +1096,7 @@ export class BVTRecorder {
1007
1096
  .filter((file) => file.endsWith(".feature"))
1008
1097
  .map((file) => path.join(this.projectDir, "features", file));
1009
1098
  try {
1010
- const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
1099
+ const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
1011
1100
  const output = {};
1012
1101
  parsedFiles.forEach((file) => {
1013
1102
  if (!file.feature) return;
@@ -1035,7 +1124,7 @@ export class BVTRecorder {
1035
1124
  loadExistingScenario({ featureName, scenarioName }) {
1036
1125
  const step_definitions = loadStepDefinitions(this.projectDir);
1037
1126
  const featureFilePath = path.join(this.projectDir, "features", featureName);
1038
- const gherkinDoc = parseFeatureFile(featureFilePath);
1127
+ const gherkinDoc = this.parseFeatureFile(featureFilePath);
1039
1128
  const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
1040
1129
 
1041
1130
  const steps = [];
@@ -1194,20 +1283,54 @@ export class BVTRecorder {
1194
1283
  await this.cleanupExecution({ tags });
1195
1284
  await this.initExecution({ tags });
1196
1285
  }
1197
- }
1198
1286
 
1199
- const parseFeatureFile = (featureFilePath) => {
1200
- try {
1201
- let id = 0;
1202
- const uuidFn = () => (++id).toString(16);
1203
- const builder = new AstBuilder(uuidFn);
1204
- const matcher = new GherkinClassicTokenMatcher();
1205
- const parser = new Parser(builder, matcher);
1206
- const source = readFileSync(featureFilePath, "utf8");
1207
- const gherkinDocument = parser.parse(source);
1208
- return gherkinDocument;
1209
- } catch (e) {
1210
- console.log(e);
1287
+ parseFeatureFile(featureFilePath) {
1288
+ try {
1289
+ let id = 0;
1290
+ const uuidFn = () => (++id).toString(16);
1291
+ const builder = new AstBuilder(uuidFn);
1292
+ const matcher = new GherkinClassicTokenMatcher();
1293
+ const parser = new Parser(builder, matcher);
1294
+ const source = readFileSync(featureFilePath, "utf8");
1295
+ const gherkinDocument = parser.parse(source);
1296
+ return gherkinDocument;
1297
+ } catch (e) {
1298
+ this.logger.error(`Error parsing feature file: ${featureFilePath}`);
1299
+ console.log(e);
1300
+ }
1301
+ return {};
1211
1302
  }
1212
- return {};
1213
- };
1303
+
1304
+ stopRecordingNetwork(input) {
1305
+ if (this.bvtContext) {
1306
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
1307
+ }
1308
+ }
1309
+
1310
+ async fakeParams(params) {
1311
+ const newFakeParams = {};
1312
+ Object.keys(params).forEach((key) => {
1313
+ if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
1314
+ newFakeParams[key] = params[key];
1315
+ return;
1316
+ }
1317
+
1318
+ try {
1319
+ const value = params[key].substring(2, params[key].length - 2).trim();
1320
+ const faking = value.split("(")[0].split(".");
1321
+ let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
1322
+ argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
1323
+ let fakeFunc = faker;
1324
+ faking.forEach((f) => {
1325
+ fakeFunc = fakeFunc[f];
1326
+ });
1327
+ const newValue = fakeFunc(argument);
1328
+ newFakeParams[key] = newValue;
1329
+ } catch (error) {
1330
+ newFakeParams[key] = params[key];
1331
+ }
1332
+ });
1333
+
1334
+ return newFakeParams;
1335
+ }
1336
+ }
@@ -116,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
116
116
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
117
117
  //console.log({ utilsContent });
118
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
+ }
119
125
  } catch (error) {
120
126
  foundErrors.push({ error });
121
127
  }
@@ -229,6 +235,7 @@ export const getImplementedSteps = async (projectDir) => {
229
235
  pattern: template.pattern,
230
236
  paths: template.paths,
231
237
  routeItems: step.routeItems,
238
+ isApiStep: template.source === "api",
232
239
  };
233
240
 
234
241
  implementedSteps.push(implementedStep);
@@ -254,6 +261,7 @@ export const getImplementedSteps = async (projectDir) => {
254
261
  mjsFile: template.mjsFile,
255
262
  pattern: template.pattern,
256
263
  paths: template.paths,
264
+ isApiStep: template.source === "api",
257
265
  };
258
266
  // reconstruct the parameters
259
267
  const parameters = [];