@dev-blinq/cucumber_client 1.0.1216-dev → 1.0.1216-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 (40) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +220 -0
  2. package/bin/assets/preload/recorderv3.js +74 -4
  3. package/bin/assets/preload/unique_locators.js +24 -3
  4. package/bin/assets/scripts/aria_snapshot.js +235 -0
  5. package/bin/assets/scripts/dom_attr.js +372 -0
  6. package/bin/assets/scripts/dom_element.js +0 -0
  7. package/bin/assets/scripts/dom_parent.js +185 -0
  8. package/bin/assets/scripts/event_utils.js +105 -0
  9. package/bin/assets/scripts/pw.js +7886 -0
  10. package/bin/assets/scripts/recorder.js +1147 -0
  11. package/bin/assets/scripts/snapshot_capturer.js +155 -0
  12. package/bin/assets/scripts/unique_locators.js +841 -0
  13. package/bin/assets/scripts/yaml.js +4770 -0
  14. package/bin/assets/templates/_hooks_template.txt +37 -0
  15. package/bin/assets/templates/page_template.txt +2 -16
  16. package/bin/assets/templates/utils_template.txt +48 -63
  17. package/bin/client/apiTest/apiTest.js +6 -0
  18. package/bin/client/cli_helpers.js +11 -13
  19. package/bin/client/code_cleanup/utils.js +42 -14
  20. package/bin/client/code_gen/code_inversion.js +92 -11
  21. package/bin/client/code_gen/index.js +3 -0
  22. package/bin/client/code_gen/page_reflection.js +37 -20
  23. package/bin/client/code_gen/playwright_codeget.js +170 -33
  24. package/bin/client/cucumber/feature.js +85 -27
  25. package/bin/client/cucumber/steps_definitions.js +85 -77
  26. package/bin/client/local_agent.js +7 -3
  27. package/bin/client/project.js +7 -1
  28. package/bin/client/recorderv3/bvt_recorder.js +278 -79
  29. package/bin/client/recorderv3/implemented_steps.js +75 -14
  30. package/bin/client/recorderv3/index.js +49 -7
  31. package/bin/client/recorderv3/network.js +299 -0
  32. package/bin/client/recorderv3/step_runner.js +181 -12
  33. package/bin/client/recorderv3/step_utils.js +157 -6
  34. package/bin/client/recorderv3/update_feature.js +58 -30
  35. package/bin/client/recording.js +7 -0
  36. package/bin/client/run_cucumber.js +15 -2
  37. package/bin/client/scenario_report.js +18 -6
  38. package/bin/client/test_scenario.js +0 -1
  39. package/bin/index.js +1 -0
  40. package/package.json +15 -8
@@ -82,6 +82,12 @@ class Project {
82
82
  const utilsContent = readFileSync(utilsPath, "utf8");
83
83
  const utilsFileName = this.stepsFolder + "/utils.mjs";
84
84
  writeFileSync(utilsFileName, utilsContent, "utf8");
85
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
86
+ if (existsSync(hooksTemplateFilePath)) {
87
+ const hooksFilePath = path.join(this.stepsFolder, "_hooks.mjs");
88
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
89
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
90
+ }
85
91
  } catch (e) {
86
92
  logger.error("Error loading utils_template");
87
93
  }
@@ -104,7 +110,7 @@ class Project {
104
110
  this.pages.push(page);
105
111
  }
106
112
  }
107
- return envFile;
113
+ return this.environment.name;
108
114
  }
109
115
  getPagesNames() {
110
116
  let result = [];
@@ -3,8 +3,7 @@ import { closeContext, initContext, _getDataFile, resetTestData } from "automati
3
3
  import { existsSync, readdirSync, readFileSync, rmSync } from "fs";
4
4
  import path from "path";
5
5
  import url from "url";
6
- import pkg from "../../min/injectedScript.min.cjs";
7
- import { getImplementedSteps, getStepsAndCommandsForScenario } from "./implemented_steps.js";
6
+ import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
8
7
  import { NamesService } from "./services.js";
9
8
  import { BVTStepRunner } from "./step_runner.js";
10
9
  import { readFile, writeFile } from "fs/promises";
@@ -16,41 +15,21 @@ import chokidar from "chokidar";
16
15
  import logger from "../../logger.js";
17
16
  import { unEscapeNonPrintables } from "../cucumber/utils.js";
18
17
  import { findAvailablePort } from "../utils/index.js";
18
+ import { Step } from "../cucumber/feature.js";
19
+
19
20
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
20
- const { source: injectedScriptSource } = pkg;
21
+
21
22
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
22
- export function getInitScript(config) {
23
- const popupHandlers = config?.popupHandlers ?? [];
24
-
25
- const disableHighlight = config?.disableHighlight ?? false;
26
-
27
- const disableMultipleLocators = config?.disableMultipleLocators ?? false;
28
-
29
- const popupScript = `
30
- window.__bvt_Recorder.setPopupHandlers(${JSON.stringify(popupHandlers)});
31
- window.__bvt_Recorder.setDisableHighlight(${JSON.stringify(disableHighlight)});
32
- window.__bvt_Recorder.setImproviseLocators(${JSON.stringify(!disableMultipleLocators)});`;
33
- return (
34
- [
35
- path.join(__dirname, "..", "..", "assets", "preload", "accessibility.js"),
36
- path.join(__dirname, "..", "..", "assets", "preload", "dom-utils.js"),
37
- path.join(__dirname, "..", "..", "assets", "preload", "generateSelector.js"),
38
- path.join(__dirname, "..", "..", "assets", "preload", "locators.js"),
39
- path.join(__dirname, "..", "..", "assets", "preload", "unique_locators.js"),
40
- // path.join(__dirname, "..", "..", "assets", "preload", "pw_locators.js"),
41
- path.join(__dirname, "..", "..", "assets", "preload", "climb.js"),
42
- path.join(__dirname, "..", "..", "assets", "preload", "text-locator.js"),
43
- path.join(__dirname, "..", "..", "assets", "preload", "toolbar.js"),
44
- path.join(__dirname, "..", "..", "assets", "preload", "recording-tool.js"),
45
- path.join(__dirname, "..", "..", "assets", "preload", "find_context.js"),
46
- path.join(__dirname, "..", "..", "assets", "preload", "recorderv3.js"),
47
- path.join(__dirname, "..", "..", "assets", "preload", "findElementText.js"),
48
- path.join(__dirname, "..", "..", "assets", "preload", "css_gen.js"),
49
- path.join(__dirname, "..", "..", "assets", "preload", "yaml.js"),
50
- ]
51
- .map((filePath) => readFileSync(filePath, "utf8"))
52
- .join("\n") + popupScript
23
+ export function getInitScript(config, options) {
24
+ const preScript = `
25
+ window.__bvt_Recorder_config = ${JSON.stringify(config ?? null)};
26
+ window.__PW_options = ${JSON.stringify(options ?? null)};
27
+ `;
28
+ const recorderScript = readFileSync(
29
+ path.join(__dirname, "..", "..", "assets", "bundled_scripts", "recorder.js"),
30
+ "utf8"
53
31
  );
32
+ return preScript + recorderScript;
54
33
  }
55
34
 
56
35
  async function evaluate(frame, script) {
@@ -71,7 +50,7 @@ async function findNestedFrameSelector(frame, obj) {
71
50
  const frameElement = await frame.frameElement();
72
51
  if (!frameElement) return;
73
52
  const selectors = await parent.evaluate((element) => {
74
- return window.getPWLocators(element);
53
+ return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
75
54
  }, frameElement);
76
55
  return findNestedFrameSelector(parent, { children: obj, selectors });
77
56
  } catch (e) {
@@ -198,6 +177,7 @@ export class BVTRecorder {
198
177
  this.logger = logger;
199
178
  this.screenshotMap = new Map();
200
179
  this.snapshotMap = new Map();
180
+ this.scenariosStepsMap = new Map();
201
181
  this.namesService = new NamesService({
202
182
  screenshotMap: this.screenshotMap,
203
183
  TOKEN: this.TOKEN,
@@ -206,12 +186,37 @@ export class BVTRecorder {
206
186
  });
207
187
  this.stepRunner = new BVTStepRunner({
208
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
+ },
209
214
  });
210
215
  this.pageSet = new Set();
211
-
216
+ this.pageMetaDataSet = new Set();
212
217
  this.lastKnownUrlPath = "";
213
218
  // TODO: what is world?
214
- this.world = { attach: () => {} };
219
+ this.world = { attach: () => { } };
215
220
  this.shouldTakeScreenshot = true;
216
221
  this.watcher = null;
217
222
  }
@@ -224,6 +229,10 @@ export class BVTRecorder {
224
229
  onStepDetails: "BVTRecorder.onStepDetails",
225
230
  getTestData: "BVTRecorder.getTestData",
226
231
  onGoto: "BVTRecorder.onGoto",
232
+ cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
233
+ cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
234
+ cmdExecutionError: "BVTRecorder.cmdExecutionError",
235
+ interceptResults: "BVTRecorder.interceptResults",
227
236
  };
228
237
  bindings = {
229
238
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
@@ -299,22 +308,15 @@ export class BVTRecorder {
299
308
  return result;
300
309
  }
301
310
  getInitScripts(config) {
302
- return getInitScript(config);
303
- // const scripts = []
304
- // scripts.push(...this.getPWScript());
305
- // scripts.push(...this.getRecorderScripts());
306
-
307
- // const popupHandlers = config?.popupHandlers ?? [];
308
- // const disableHighlight = config?.disableHighlight ?? false;
309
- // const disableMultipleLocators = config?.disableMultipleLocators ?? false;
310
-
311
- // const inlineScript = `
312
- // window.__bvt_Recorder.setPopupHandlers(${JSON.stringify(popupHandlers)});
313
- // window.__bvt_Recorder.setDisableHighlight(${JSON.stringify(disableHighlight)});
314
- // window.__bvt_Recorder.setImproviseLocators(${JSON.stringify(!disableMultipleLocators)});
315
- // `
316
- // scripts.push(inlineScript);
317
- // return scripts;
311
+ return getInitScript(config, {
312
+ sdkLanguage: "javascript",
313
+ testIdAttributeName: "blinq-test-id",
314
+ stableRafCount: 0,
315
+ browserName: this.browser?.browserType().name(),
316
+ inputFileRoleTextbox: false,
317
+ customEngines: [],
318
+ isUnderTest: true,
319
+ });
318
320
  }
319
321
 
320
322
  async _initBrowser({ url }) {
@@ -322,7 +324,7 @@ export class BVTRecorder {
322
324
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
323
325
 
324
326
  this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
325
- this.world = { attach: () => {} };
327
+ this.world = { attach: () => { } };
326
328
 
327
329
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
328
330
  let ai_config = {};
@@ -333,8 +335,9 @@ export class BVTRecorder {
333
335
  console.error("Error reading ai_config.json", error);
334
336
  }
335
337
  }
338
+ this.config = ai_config;
336
339
  const initScripts = {
337
- recorderCjs: injectedScriptSource,
340
+ // recorderCjs: injectedScriptSource,
338
341
  scripts: [
339
342
  this.getInitScripts(ai_config),
340
343
  `\ndelete Object.getPrototypeOf(navigator).webdriver;${process.env.WINDOW_DEBUGGER ? "window.debug=true;\n" : ""}`,
@@ -349,6 +352,7 @@ export class BVTRecorder {
349
352
  const context = bvtContext.playContext;
350
353
  this.context = context;
351
354
  this.web = bvtContext.stable || bvtContext.web;
355
+ this.web.tryAllStrategies = true;
352
356
  this.page = bvtContext.page;
353
357
 
354
358
  this.pageSet.add(this.page);
@@ -461,6 +465,75 @@ export class BVTRecorder {
461
465
  }
462
466
  });
463
467
  }
468
+
469
+ hasHistoryReplacementAtIndex(previousEntries, currentEntries, index) {
470
+ if (!previousEntries || !currentEntries) return false;
471
+ if (index >= previousEntries.length || index >= currentEntries.length) return false;
472
+
473
+ const prevEntry = previousEntries[index];
474
+ // console.log("prevEntry", prevEntry);
475
+ const currEntry = currentEntries[index];
476
+ // console.log("currEntry", currEntry);
477
+
478
+ // Check if the entry at this index has been replaced
479
+ return prevEntry.id !== currEntry.id;
480
+ }
481
+
482
+ // Even simpler approach for your specific case
483
+ analyzeTransitionType(entries, currentIndex, currentEntry) {
484
+ // console.log("Analyzing transition type");
485
+ // console.log("===========================");
486
+ // console.log("Current Index:", currentIndex);
487
+ // console.log("Current Entry:", currentEntry);
488
+ // console.log("Current Entries:", entries);
489
+ // console.log("Current entries length:", entries.length);
490
+ // console.log("===========================");
491
+ // console.log("Previous Index:", this.previousIndex);
492
+ // // console.log("Previous Entry:", this.previousEntries[this.previousIndex]);
493
+ // console.log("Previous Entries:", this.previousEntries);
494
+ // console.log("Previous entries length:", this.previousHistoryLength);
495
+
496
+ if (this.previousIndex === null || this.previousHistoryLength === null || !this.previousEntries) {
497
+ return {
498
+ action: "initial",
499
+ };
500
+ }
501
+
502
+ const indexDiff = currentIndex - this.previousIndex;
503
+ const lengthDiff = entries.length - this.previousHistoryLength;
504
+
505
+ // Backward navigation
506
+ if (indexDiff < 0) {
507
+ return { action: "back" };
508
+ }
509
+
510
+ // Forward navigation
511
+ if (indexDiff > 0 && lengthDiff === 0) {
512
+ // Check if the entry at current index is the same as before
513
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
514
+
515
+ if (entryReplaced) {
516
+ return { action: "navigate" }; // New navigation that replaced forward history
517
+ } else {
518
+ return { action: "forward" }; // True forward navigation
519
+ }
520
+ }
521
+
522
+ // New navigation (history grew)
523
+ if (lengthDiff > 0) {
524
+ return { action: "navigate" };
525
+ }
526
+
527
+ // Same position, same length
528
+ if (lengthDiff <= 0) {
529
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
530
+
531
+ return entryReplaced ? { action: "navigate" } : { action: "reload" };
532
+ }
533
+
534
+ return { action: "unknown" };
535
+ }
536
+
464
537
  async getCurrentTransition() {
465
538
  if (this?.web?.browser?._name !== "chromium") {
466
539
  return;
@@ -469,29 +542,70 @@ export class BVTRecorder {
469
542
 
470
543
  try {
471
544
  const result = await client.send("Page.getNavigationHistory");
472
- // console.log("result", result);
545
+ // console.log("Navigation History:", result);
473
546
  const entries = result.entries;
474
547
  const currentIndex = result.currentIndex;
475
548
 
476
549
  // ignore if currentIndex is not the last entry
477
- if (currentIndex !== entries.length - 1) return;
550
+ // if (currentIndex !== entries.length - 1) return;
478
551
 
479
552
  const currentEntry = entries[currentIndex];
480
- return currentEntry;
553
+ const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
554
+ this.previousIndex = currentIndex;
555
+ this.previousHistoryLength = entries.length;
556
+ this.previousUrl = currentEntry.url;
557
+ this.previousEntries = [...entries]; // Store a copy of current entries
558
+
559
+ return {
560
+ currentEntry,
561
+ navigationAction: transitionInfo.action,
562
+ };
481
563
  } catch (error) {
482
- console.error("Error in getTransistionType event");
564
+ console.error("Error in getTransistionType event", error);
483
565
  } finally {
484
566
  await client.detach();
485
567
  }
486
568
  }
487
- userInitiatedTranistionTypes = ["typed", "address_bar"];
569
+ userInitiatedTransitionTypes = ["typed", "address_bar"];
488
570
  async handlePageTransition() {
489
571
  const transition = await this.getCurrentTransition();
490
572
  if (!transition) return;
491
- // console.log("transitionType", transition.transitionType);
492
- if (this.userInitiatedTranistionTypes.includes(transition.transitionType)) {
493
- // console.log("User initiated transition");
494
- this.sendEvent(this.events.onGoto, { url: transition.userTypedURL });
573
+
574
+ const { currentEntry, navigationAction } = transition;
575
+
576
+ switch (navigationAction) {
577
+ case "initial":
578
+ // console.log("Initial navigation, no action taken");
579
+ return;
580
+ case "navigate":
581
+ // console.log("transitionType", transition.transitionType);
582
+ // console.log("sending onGoto event", { url: currentEntry.url,
583
+ // type: "navigate", });
584
+ if (this.userInitiatedTransitionTypes.includes(currentEntry.transitionType)) {
585
+ const env = JSON.parse(readFileSync(this.envName), "utf8");
586
+ const baseUrl = env.baseUrl;
587
+ let url = currentEntry.userTypedURL;
588
+ if (baseUrl && url.startsWith(baseUrl)) {
589
+ url = url.replace(baseUrl, "{{env.baseUrl}}");
590
+ }
591
+ // console.log("User initiated transition");
592
+ this.sendEvent(this.events.onGoto, { url, type: "navigate" });
593
+ }
594
+ return;
595
+ case "back":
596
+ // console.log("User navigated back");
597
+ // console.log("sending onGoto event", {
598
+ // type: "back",
599
+ // });
600
+ this.sendEvent(this.events.onGoto, { type: "back" });
601
+ return;
602
+ case "forward":
603
+ // console.log("User navigated forward"); console.log("sending onGoto event", { type: "forward", });
604
+ this.sendEvent(this.events.onGoto, { type: "forward" });
605
+ return;
606
+ default:
607
+ this.sendEvent(this.events.onGoto, { type: "unknown" });
608
+ return;
495
609
  }
496
610
  }
497
611
 
@@ -618,8 +732,13 @@ export class BVTRecorder {
618
732
  }
619
733
  async closeBrowser() {
620
734
  delete process.env.TEMP_RUN;
621
- await this.watcher.close().then(() => {});
735
+ await this.watcher.close().then(() => { });
622
736
  this.watcher = null;
737
+ this.previousIndex = null;
738
+ this.previousHistoryLength = null;
739
+ this.previousUrl = null;
740
+ this.previousEntries = null;
741
+
623
742
  await closeContext();
624
743
  this.pageSet.clear();
625
744
  }
@@ -671,6 +790,7 @@ export class BVTRecorder {
671
790
  }
672
791
 
673
792
  async startRecordingInput() {
793
+ console.log("startRecordingInput");
674
794
  await this.setMode("recordingInput");
675
795
  }
676
796
  async stopRecordingInput() {
@@ -694,6 +814,7 @@ export class BVTRecorder {
694
814
  }
695
815
 
696
816
  async abortExecution() {
817
+ this.bvtContext.web.abortedExecution = true;
697
818
  await this.stepRunner.abortExecution();
698
819
  }
699
820
  async dealyedRevertMode() {
@@ -702,34 +823,47 @@ export class BVTRecorder {
702
823
  }, 100);
703
824
  this.timerId = timerId;
704
825
  }
705
- async runStep({ step, parametersMap }, options) {
826
+ async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
827
+ const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
706
828
  const _env = {
707
829
  TOKEN: this.TOKEN,
708
830
  TEMP_RUN: true,
709
831
  REPORT_FOLDER: this.bvtContext.reportFolder,
710
832
  BLINQ_ENV: this.envName,
833
+ STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
834
+ CURRENT_STEP_ID: step.id,
711
835
  };
712
836
 
713
837
  this.bvtContext.navigate = true;
838
+ this.bvtContext.loadedRoutes = null;
839
+ this.bvtContext.web.abortedExecution = false;
714
840
  for (const [key, value] of Object.entries(_env)) {
715
841
  process.env[key] = value;
716
842
  }
843
+
717
844
  if (this.timerId) {
718
845
  clearTimeout(this.timerId);
719
846
  this.timerId = null;
720
847
  }
721
848
  await this.setMode("running");
849
+
722
850
  try {
723
- await this.stepRunner.runStep(
851
+ const { result, info } = await this.stepRunner.runStep(
724
852
  {
725
853
  step,
726
854
  parametersMap,
727
855
  envPath: this.envName,
856
+ tags,
857
+ config: this.config,
728
858
  },
729
859
  this.bvtContext,
730
- options
860
+ {
861
+ skipAfter,
862
+ skipBefore,
863
+ }
731
864
  );
732
865
  await this.revertMode();
866
+ return { info };
733
867
  } catch (error) {
734
868
  await this.revertMode();
735
869
  throw error;
@@ -738,24 +872,45 @@ export class BVTRecorder {
738
872
  delete process.env[key];
739
873
  }
740
874
  this.bvtContext.navigate = false;
741
- }
742
- }
743
- async runScenario({ steps, parametersMap }) {
744
- for (const step of steps) {
745
- await this.runStep({ step, parametersMap });
875
+ this.bvtContext.web.abortedExecution = false;
746
876
  }
747
877
  }
748
878
  async saveScenario({ scenario, featureName, override, isSingleStep }) {
749
879
  await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
750
880
  if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
751
- await this.cleanup();
881
+ await this.cleanup({ tags: scenario.tags });
752
882
  }
753
883
  async getImplementedSteps() {
754
- return getImplementedSteps(this.projectDir);
884
+ const stepsAndScenarios = await getImplementedSteps(this.projectDir);
885
+ const implementedSteps = stepsAndScenarios.implementedSteps;
886
+ const scenarios = stepsAndScenarios.scenarios;
887
+ for (const scenario of scenarios) {
888
+ this.scenariosStepsMap.set(scenario.name, scenario.steps);
889
+ delete scenario.steps;
890
+ }
891
+ return {
892
+ implementedSteps,
893
+ scenarios,
894
+ };
755
895
  }
756
896
  async getStepsAndCommandsForScenario({ name, featureName }) {
757
- return getStepsAndCommandsForScenario({ name, featureName, projectDir: this.projectDir });
897
+ const steps = this.scenariosStepsMap.get(name) || [];
898
+ for (const step of steps) {
899
+ if (step.isImplemented) {
900
+ step.commands = this.getCommandsForImplementedStep({ stepName: step.text });
901
+ } else {
902
+ step.commands = [];
903
+ }
904
+ }
905
+ return steps;
906
+ // return getStepsAndCommandsForScenario({
907
+ // name,
908
+ // featureName,
909
+ // projectDir: this.projectDir,
910
+ // map: this.scenariosStepsMap,
911
+ // });
758
912
  }
913
+
759
914
  async generateStepName({ commands, stepsNames, parameters, map }) {
760
915
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
761
916
  }
@@ -838,9 +993,9 @@ export class BVTRecorder {
838
993
  }
839
994
  }
840
995
 
841
- async discardTestData() {
996
+ async discardTestData({ tags }) {
842
997
  resetTestData(this.envName, this.world);
843
- await this.cleanup();
998
+ await this.cleanup({ tags });
844
999
  }
845
1000
  async addToTestData(obj) {
846
1001
  if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
@@ -879,6 +1034,7 @@ export class BVTRecorder {
879
1034
  const stepParams = parseStepTextParameters(stepName);
880
1035
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
881
1036
  }
1037
+
882
1038
  loadExistingScenario({ featureName, scenarioName }) {
883
1039
  const step_definitions = loadStepDefinitions(this.projectDir);
884
1040
  const featureFilePath = path.join(this.projectDir, "features", featureName);
@@ -907,6 +1063,7 @@ export class BVTRecorder {
907
1063
  ..._s,
908
1064
  keyword: step.keyword.trim(),
909
1065
  };
1066
+ parseRouteFiles(this.projectDir, _step);
910
1067
  steps.push(_step);
911
1068
  }
912
1069
  return {
@@ -946,7 +1103,7 @@ export class BVTRecorder {
946
1103
  }
947
1104
  return result;
948
1105
  }
949
- async cleanup() {
1106
+ async cleanup({ tags }) {
950
1107
  const noopStep = {
951
1108
  text: "Noop",
952
1109
  isImplemented: true,
@@ -960,6 +1117,7 @@ export class BVTRecorder {
960
1117
  {
961
1118
  step: noopStep,
962
1119
  parametersMap: {},
1120
+ tags: tags || [],
963
1121
  },
964
1122
  {
965
1123
  skipAfter: false,
@@ -998,6 +1156,47 @@ export class BVTRecorder {
998
1156
  return false;
999
1157
  }
1000
1158
  }
1159
+ async initExecution({ tags = [] }) {
1160
+ // run before hooks
1161
+ const noopStep = {
1162
+ text: "Noop",
1163
+ isImplemented: true,
1164
+ };
1165
+ await this.runStep(
1166
+ {
1167
+ step: noopStep,
1168
+ parametersMap: {},
1169
+ tags,
1170
+ },
1171
+ {
1172
+ skipBefore: false,
1173
+ skipAfter: true,
1174
+ }
1175
+ );
1176
+ }
1177
+ async cleanupExecution({ tags = [] }) {
1178
+ // run after hooks
1179
+ const noopStep = {
1180
+ text: "Noop",
1181
+ isImplemented: true,
1182
+ };
1183
+ await this.runStep(
1184
+ {
1185
+ step: noopStep,
1186
+ parametersMap: {},
1187
+ tags,
1188
+ },
1189
+ {
1190
+ skipBefore: true,
1191
+ skipAfter: false,
1192
+ }
1193
+ );
1194
+ }
1195
+ async resetExecution({ tags = [] }) {
1196
+ // run after hooks followed by before hooks
1197
+ await this.cleanupExecution({ tags });
1198
+ await this.initExecution({ tags });
1199
+ }
1001
1200
  }
1002
1201
 
1003
1202
  const parseFeatureFile = (featureFilePath) => {