@dev-blinq/cucumber_client 1.0.1220-dev → 1.0.1220-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 +70 -3
  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 +84 -76
  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 +285 -81
  29. package/bin/client/recorderv3/implemented_steps.js +75 -14
  30. package/bin/client/recorderv3/index.js +58 -8
  31. package/bin/client/recorderv3/network.js +299 -0
  32. package/bin/client/recorderv3/step_runner.js +319 -67
  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 +4 -8
  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,17 +177,15 @@ 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,
204
184
  projectDir: this.projectDir,
205
185
  logger: this.logger,
206
186
  });
207
- this.stepRunner = new BVTStepRunner({
208
- projectDir: this.projectDir,
209
- });
210
187
  this.pageSet = new Set();
211
-
188
+ this.pageMetaDataSet = new Set();
212
189
  this.lastKnownUrlPath = "";
213
190
  // TODO: what is world?
214
191
  this.world = { attach: () => {} };
@@ -224,6 +201,10 @@ export class BVTRecorder {
224
201
  onStepDetails: "BVTRecorder.onStepDetails",
225
202
  getTestData: "BVTRecorder.getTestData",
226
203
  onGoto: "BVTRecorder.onGoto",
204
+ cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
205
+ cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
206
+ cmdExecutionError: "BVTRecorder.cmdExecutionError",
207
+ interceptResults: "BVTRecorder.interceptResults",
227
208
  };
228
209
  bindings = {
229
210
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
@@ -299,29 +280,22 @@ export class BVTRecorder {
299
280
  return result;
300
281
  }
301
282
  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;
283
+ return getInitScript(config, {
284
+ sdkLanguage: "javascript",
285
+ testIdAttributeName: "blinq-test-id",
286
+ stableRafCount: 0,
287
+ browserName: this.browser?.browserType().name(),
288
+ inputFileRoleTextbox: false,
289
+ customEngines: [],
290
+ isUnderTest: true,
291
+ });
318
292
  }
319
293
 
320
294
  async _initBrowser({ url }) {
321
295
  this.#remoteDebuggerPort = await findAvailablePort();
322
296
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
323
297
 
324
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
298
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
325
299
  this.world = { attach: () => {} };
326
300
 
327
301
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
@@ -333,8 +307,9 @@ export class BVTRecorder {
333
307
  console.error("Error reading ai_config.json", error);
334
308
  }
335
309
  }
310
+ this.config = ai_config;
336
311
  const initScripts = {
337
- recorderCjs: injectedScriptSource,
312
+ // recorderCjs: injectedScriptSource,
338
313
  scripts: [
339
314
  this.getInitScripts(ai_config),
340
315
  `\ndelete Object.getPrototypeOf(navigator).webdriver;${process.env.WINDOW_DEBUGGER ? "window.debug=true;\n" : ""}`,
@@ -346,11 +321,40 @@ export class BVTRecorder {
346
321
  let stopTime = Date.now();
347
322
  this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
348
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
+ console.log("Sending cmdExecutionStart event for cmdId:", data);
331
+ this.sendEvent(this.events.cmdExecutionStart, data);
332
+ break;
333
+ case "cmdExecutionSuccess":
334
+ console.log("Sending cmdExecutionSuccess event for cmdId:", data);
335
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
336
+ break;
337
+ case "cmdExecutionError":
338
+ console.log("Sending cmdExecutionError event for cmdId:", data);
339
+ this.sendEvent(this.events.cmdExecutionError, data);
340
+ break;
341
+ case "interceptResults":
342
+ console.log("Sending interceptResults event");
343
+ this.sendEvent(this.events.interceptResults, data);
344
+ break;
345
+ default:
346
+ console.warn("Unknown command execution status type:", data.type);
347
+ break;
348
+ }
349
+ }
350
+ },
351
+ bvtContext: this.bvtContext,
352
+ });
349
353
  const context = bvtContext.playContext;
350
354
  this.context = context;
351
355
  this.web = bvtContext.stable || bvtContext.web;
356
+ this.web.tryAllStrategies = true;
352
357
  this.page = bvtContext.page;
353
-
354
358
  this.pageSet.add(this.page);
355
359
  this.lastKnownUrlPath = this._updateUrlPath();
356
360
  const browser = await this.context.browser();
@@ -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
 
@@ -620,6 +734,11 @@ export class BVTRecorder {
620
734
  delete process.env.TEMP_RUN;
621
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
  }
@@ -696,40 +815,61 @@ export class BVTRecorder {
696
815
  async abortExecution() {
697
816
  await this.stepRunner.abortExecution();
698
817
  }
818
+
819
+ async pauseExecution({ cmdId }) {
820
+ await this.stepRunner.pauseExecution(cmdId);
821
+ }
822
+
823
+ async resumeExecution({ cmdId }) {
824
+ await this.stepRunner.resumeExecution(cmdId);
825
+ }
826
+
699
827
  async dealyedRevertMode() {
700
828
  const timerId = setTimeout(async () => {
701
829
  await this.revertMode();
702
830
  }, 100);
703
831
  this.timerId = timerId;
704
832
  }
705
- async runStep({ step, parametersMap }, options) {
833
+ async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
834
+ const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
706
835
  const _env = {
707
836
  TOKEN: this.TOKEN,
708
837
  TEMP_RUN: true,
709
838
  REPORT_FOLDER: this.bvtContext.reportFolder,
710
839
  BLINQ_ENV: this.envName,
840
+ STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
841
+ CURRENT_STEP_ID: step.id,
711
842
  };
712
843
 
713
844
  this.bvtContext.navigate = true;
845
+ this.bvtContext.loadedRoutes = null;
714
846
  for (const [key, value] of Object.entries(_env)) {
715
847
  process.env[key] = value;
716
848
  }
849
+
717
850
  if (this.timerId) {
718
851
  clearTimeout(this.timerId);
719
852
  this.timerId = null;
720
853
  }
721
854
  await this.setMode("running");
855
+
722
856
  try {
723
- await this.stepRunner.runStep(
857
+ const { result, info } = await this.stepRunner.runStep(
724
858
  {
725
859
  step,
726
860
  parametersMap,
727
861
  envPath: this.envName,
862
+ tags,
863
+ config: this.config,
728
864
  },
729
865
  this.bvtContext,
730
- options
866
+ {
867
+ skipAfter,
868
+ skipBefore,
869
+ }
731
870
  );
732
871
  await this.revertMode();
872
+ return { info };
733
873
  } catch (error) {
734
874
  await this.revertMode();
735
875
  throw error;
@@ -740,22 +880,42 @@ export class BVTRecorder {
740
880
  this.bvtContext.navigate = false;
741
881
  }
742
882
  }
743
- async runScenario({ steps, parametersMap }) {
744
- for (const step of steps) {
745
- await this.runStep({ step, parametersMap });
746
- }
747
- }
748
883
  async saveScenario({ scenario, featureName, override, isSingleStep }) {
749
884
  await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
750
885
  if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
751
- await this.cleanup();
886
+ await this.cleanup({ tags: scenario.tags });
752
887
  }
753
888
  async getImplementedSteps() {
754
- return getImplementedSteps(this.projectDir);
889
+ const stepsAndScenarios = await getImplementedSteps(this.projectDir);
890
+ const implementedSteps = stepsAndScenarios.implementedSteps;
891
+ const scenarios = stepsAndScenarios.scenarios;
892
+ for (const scenario of scenarios) {
893
+ this.scenariosStepsMap.set(scenario.name, scenario.steps);
894
+ delete scenario.steps;
895
+ }
896
+ return {
897
+ implementedSteps,
898
+ scenarios,
899
+ };
755
900
  }
756
901
  async getStepsAndCommandsForScenario({ name, featureName }) {
757
- return getStepsAndCommandsForScenario({ name, featureName, projectDir: this.projectDir });
902
+ const steps = this.scenariosStepsMap.get(name) || [];
903
+ for (const step of steps) {
904
+ if (step.isImplemented) {
905
+ step.commands = this.getCommandsForImplementedStep({ stepName: step.text });
906
+ } else {
907
+ step.commands = [];
908
+ }
909
+ }
910
+ return steps;
911
+ // return getStepsAndCommandsForScenario({
912
+ // name,
913
+ // featureName,
914
+ // projectDir: this.projectDir,
915
+ // map: this.scenariosStepsMap,
916
+ // });
758
917
  }
918
+
759
919
  async generateStepName({ commands, stepsNames, parameters, map }) {
760
920
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
761
921
  }
@@ -838,9 +998,9 @@ export class BVTRecorder {
838
998
  }
839
999
  }
840
1000
 
841
- async discardTestData() {
1001
+ async discardTestData({ tags }) {
842
1002
  resetTestData(this.envName, this.world);
843
- await this.cleanup();
1003
+ await this.cleanup({ tags });
844
1004
  }
845
1005
  async addToTestData(obj) {
846
1006
  if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
@@ -879,6 +1039,7 @@ export class BVTRecorder {
879
1039
  const stepParams = parseStepTextParameters(stepName);
880
1040
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
881
1041
  }
1042
+
882
1043
  loadExistingScenario({ featureName, scenarioName }) {
883
1044
  const step_definitions = loadStepDefinitions(this.projectDir);
884
1045
  const featureFilePath = path.join(this.projectDir, "features", featureName);
@@ -907,6 +1068,7 @@ export class BVTRecorder {
907
1068
  ..._s,
908
1069
  keyword: step.keyword.trim(),
909
1070
  };
1071
+ parseRouteFiles(this.projectDir, _step);
910
1072
  steps.push(_step);
911
1073
  }
912
1074
  return {
@@ -946,7 +1108,7 @@ export class BVTRecorder {
946
1108
  }
947
1109
  return result;
948
1110
  }
949
- async cleanup() {
1111
+ async cleanup({ tags }) {
950
1112
  const noopStep = {
951
1113
  text: "Noop",
952
1114
  isImplemented: true,
@@ -960,6 +1122,7 @@ export class BVTRecorder {
960
1122
  {
961
1123
  step: noopStep,
962
1124
  parametersMap: {},
1125
+ tags: tags || [],
963
1126
  },
964
1127
  {
965
1128
  skipAfter: false,
@@ -998,6 +1161,47 @@ export class BVTRecorder {
998
1161
  return false;
999
1162
  }
1000
1163
  }
1164
+ async initExecution({ tags = [] }) {
1165
+ // run before hooks
1166
+ const noopStep = {
1167
+ text: "Noop",
1168
+ isImplemented: true,
1169
+ };
1170
+ await this.runStep(
1171
+ {
1172
+ step: noopStep,
1173
+ parametersMap: {},
1174
+ tags,
1175
+ },
1176
+ {
1177
+ skipBefore: false,
1178
+ skipAfter: true,
1179
+ }
1180
+ );
1181
+ }
1182
+ async cleanupExecution({ tags = [] }) {
1183
+ // run after hooks
1184
+ const noopStep = {
1185
+ text: "Noop",
1186
+ isImplemented: true,
1187
+ };
1188
+ await this.runStep(
1189
+ {
1190
+ step: noopStep,
1191
+ parametersMap: {},
1192
+ tags,
1193
+ },
1194
+ {
1195
+ skipBefore: true,
1196
+ skipAfter: false,
1197
+ }
1198
+ );
1199
+ }
1200
+ async resetExecution({ tags = [] }) {
1201
+ // run after hooks followed by before hooks
1202
+ await this.cleanupExecution({ tags });
1203
+ await this.initExecution({ tags });
1204
+ }
1001
1205
  }
1002
1206
 
1003
1207
  const parseFeatureFile = (featureFilePath) => {