@dev-blinq/cucumber_client 1.0.1203-dev → 1.0.1203-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 (38) 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/page_template.txt +2 -16
  15. package/bin/assets/templates/utils_template.txt +59 -7
  16. package/bin/client/cli_helpers.js +11 -13
  17. package/bin/client/code_cleanup/utils.js +42 -14
  18. package/bin/client/code_gen/code_inversion.js +48 -11
  19. package/bin/client/code_gen/index.js +3 -0
  20. package/bin/client/code_gen/page_reflection.js +37 -20
  21. package/bin/client/code_gen/playwright_codeget.js +170 -33
  22. package/bin/client/cucumber/feature.js +85 -27
  23. package/bin/client/cucumber/steps_definitions.js +109 -83
  24. package/bin/client/local_agent.js +6 -2
  25. package/bin/client/project.js +6 -2
  26. package/bin/client/recorderv3/bvt_recorder.js +276 -79
  27. package/bin/client/recorderv3/implemented_steps.js +69 -14
  28. package/bin/client/recorderv3/index.js +49 -7
  29. package/bin/client/recorderv3/network.js +299 -0
  30. package/bin/client/recorderv3/step_runner.js +184 -13
  31. package/bin/client/recorderv3/step_utils.js +155 -8
  32. package/bin/client/recorderv3/update_feature.js +58 -30
  33. package/bin/client/recording.js +7 -0
  34. package/bin/client/run_cucumber.js +16 -2
  35. package/bin/client/scenario_report.js +18 -6
  36. package/bin/client/test_scenario.js +0 -1
  37. package/bin/index.js +1 -0
  38. package/package.json +15 -8
@@ -655,6 +655,7 @@ class LocalAgent {
655
655
 
656
656
  let page = agent.project.getPage(agent.pageName, true);
657
657
  const generateCodeResult = generateCode(recording, page, userData, agent.project.rootFolder, methodName);
658
+
658
659
  if (generateCodeResult.simple === true) {
659
660
  agent.sendDone("generateStepCode", null);
660
661
  break;
@@ -707,6 +708,9 @@ class LocalAgent {
707
708
  cucumberStep: agent.cucumberStep,
708
709
  },
709
710
  });
711
+ if (generateCodeResult.locatorsMetadata) {
712
+ page.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
713
+ }
710
714
  await page.save();
711
715
  agent.scenarioReport.updateLastStep({
712
716
  recording: command.data.recording,
@@ -967,7 +971,7 @@ class LocalAgent {
967
971
  async createNewStepLocal(
968
972
  featureName,
969
973
  cucumberStep,
970
- feature,
974
+ comments,
971
975
  userData,
972
976
  firstStep,
973
977
  previousTasks,
@@ -1083,7 +1087,7 @@ class LocalAgent {
1083
1087
  previousTasks,
1084
1088
  scenarioDocument,
1085
1089
  scenarioStepIndex,
1086
- comments: feature.comments,
1090
+ comments,
1087
1091
  featureFileText,
1088
1092
  recover,
1089
1093
  dumpConfig: this.dumpConfig,
@@ -4,6 +4,7 @@ import { CodePage } from "./code_gen/page_reflection.js";
4
4
  import logger from "../logger.js";
5
5
  import path from "path";
6
6
  import { fileURLToPath } from "url";
7
+ import { findFilesWithExtension } from "./cucumber/steps_definitions.js";
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = path.dirname(__filename);
9
10
 
@@ -85,7 +86,10 @@ class Project {
85
86
  logger.error("Error loading utils_template");
86
87
  }
87
88
 
88
- let files = readdirSync(this.stepsFolder);
89
+ const files = findFilesWithExtension(this.stepsFolder, "mjs");
90
+
91
+ //let files = readdirSync(this.stepsFolder);
92
+
89
93
  for (let i = 0; i < files.length; i++) {
90
94
  let file = files[i];
91
95
  if (file.startsWith("_")) {
@@ -100,7 +104,7 @@ class Project {
100
104
  this.pages.push(page);
101
105
  }
102
106
  }
103
- return envFile;
107
+ return this.environment.name;
104
108
  }
105
109
  getPagesNames() {
106
110
  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,36 @@ 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
-
212
216
  this.lastKnownUrlPath = "";
213
217
  // TODO: what is world?
214
- this.world = { attach: () => {} };
218
+ this.world = { attach: () => { } };
215
219
  this.shouldTakeScreenshot = true;
216
220
  this.watcher = null;
217
221
  }
@@ -224,6 +228,10 @@ export class BVTRecorder {
224
228
  onStepDetails: "BVTRecorder.onStepDetails",
225
229
  getTestData: "BVTRecorder.getTestData",
226
230
  onGoto: "BVTRecorder.onGoto",
231
+ cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
232
+ cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
233
+ cmdExecutionError: "BVTRecorder.cmdExecutionError",
234
+ interceptResults: "BVTRecorder.interceptResults",
227
235
  };
228
236
  bindings = {
229
237
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
@@ -299,22 +307,15 @@ export class BVTRecorder {
299
307
  return result;
300
308
  }
301
309
  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;
310
+ return getInitScript(config, {
311
+ sdkLanguage: "javascript",
312
+ testIdAttributeName: "blinq-test-id",
313
+ stableRafCount: 0,
314
+ browserName: this.browser?.browserType().name(),
315
+ inputFileRoleTextbox: false,
316
+ customEngines: [],
317
+ isUnderTest: true,
318
+ });
318
319
  }
319
320
 
320
321
  async _initBrowser({ url }) {
@@ -322,7 +323,7 @@ export class BVTRecorder {
322
323
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
323
324
 
324
325
  this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
325
- this.world = { attach: () => {} };
326
+ this.world = { attach: () => { } };
326
327
 
327
328
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
328
329
  let ai_config = {};
@@ -333,8 +334,9 @@ export class BVTRecorder {
333
334
  console.error("Error reading ai_config.json", error);
334
335
  }
335
336
  }
337
+ this.config = ai_config;
336
338
  const initScripts = {
337
- recorderCjs: injectedScriptSource,
339
+ // recorderCjs: injectedScriptSource,
338
340
  scripts: [
339
341
  this.getInitScripts(ai_config),
340
342
  `\ndelete Object.getPrototypeOf(navigator).webdriver;${process.env.WINDOW_DEBUGGER ? "window.debug=true;\n" : ""}`,
@@ -349,6 +351,7 @@ export class BVTRecorder {
349
351
  const context = bvtContext.playContext;
350
352
  this.context = context;
351
353
  this.web = bvtContext.stable || bvtContext.web;
354
+ this.web.tryAllStrategies = true;
352
355
  this.page = bvtContext.page;
353
356
 
354
357
  this.pageSet.add(this.page);
@@ -461,6 +464,75 @@ export class BVTRecorder {
461
464
  }
462
465
  });
463
466
  }
467
+
468
+ hasHistoryReplacementAtIndex(previousEntries, currentEntries, index) {
469
+ if (!previousEntries || !currentEntries) return false;
470
+ if (index >= previousEntries.length || index >= currentEntries.length) return false;
471
+
472
+ const prevEntry = previousEntries[index];
473
+ // console.log("prevEntry", prevEntry);
474
+ const currEntry = currentEntries[index];
475
+ // console.log("currEntry", currEntry);
476
+
477
+ // Check if the entry at this index has been replaced
478
+ return prevEntry.id !== currEntry.id;
479
+ }
480
+
481
+ // Even simpler approach for your specific case
482
+ analyzeTransitionType(entries, currentIndex, currentEntry) {
483
+ // console.log("Analyzing transition type");
484
+ // console.log("===========================");
485
+ // console.log("Current Index:", currentIndex);
486
+ // console.log("Current Entry:", currentEntry);
487
+ // console.log("Current Entries:", entries);
488
+ // console.log("Current entries length:", entries.length);
489
+ // console.log("===========================");
490
+ // console.log("Previous Index:", this.previousIndex);
491
+ // // console.log("Previous Entry:", this.previousEntries[this.previousIndex]);
492
+ // console.log("Previous Entries:", this.previousEntries);
493
+ // console.log("Previous entries length:", this.previousHistoryLength);
494
+
495
+ if (this.previousIndex === null || this.previousHistoryLength === null || !this.previousEntries) {
496
+ return {
497
+ action: "initial",
498
+ };
499
+ }
500
+
501
+ const indexDiff = currentIndex - this.previousIndex;
502
+ const lengthDiff = entries.length - this.previousHistoryLength;
503
+
504
+ // Backward navigation
505
+ if (indexDiff < 0) {
506
+ return { action: "back" };
507
+ }
508
+
509
+ // Forward navigation
510
+ if (indexDiff > 0 && lengthDiff === 0) {
511
+ // Check if the entry at current index is the same as before
512
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
513
+
514
+ if (entryReplaced) {
515
+ return { action: "navigate" }; // New navigation that replaced forward history
516
+ } else {
517
+ return { action: "forward" }; // True forward navigation
518
+ }
519
+ }
520
+
521
+ // New navigation (history grew)
522
+ if (lengthDiff > 0) {
523
+ return { action: "navigate" };
524
+ }
525
+
526
+ // Same position, same length
527
+ if (lengthDiff <= 0) {
528
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
529
+
530
+ return entryReplaced ? { action: "navigate" } : { action: "reload" };
531
+ }
532
+
533
+ return { action: "unknown" };
534
+ }
535
+
464
536
  async getCurrentTransition() {
465
537
  if (this?.web?.browser?._name !== "chromium") {
466
538
  return;
@@ -469,29 +541,70 @@ export class BVTRecorder {
469
541
 
470
542
  try {
471
543
  const result = await client.send("Page.getNavigationHistory");
472
- // console.log("result", result);
544
+ // console.log("Navigation History:", result);
473
545
  const entries = result.entries;
474
546
  const currentIndex = result.currentIndex;
475
547
 
476
548
  // ignore if currentIndex is not the last entry
477
- if (currentIndex !== entries.length - 1) return;
549
+ // if (currentIndex !== entries.length - 1) return;
478
550
 
479
551
  const currentEntry = entries[currentIndex];
480
- return currentEntry;
552
+ const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
553
+ this.previousIndex = currentIndex;
554
+ this.previousHistoryLength = entries.length;
555
+ this.previousUrl = currentEntry.url;
556
+ this.previousEntries = [...entries]; // Store a copy of current entries
557
+
558
+ return {
559
+ currentEntry,
560
+ navigationAction: transitionInfo.action,
561
+ };
481
562
  } catch (error) {
482
- console.error("Error in getTransistionType event");
563
+ console.error("Error in getTransistionType event", error);
483
564
  } finally {
484
565
  await client.detach();
485
566
  }
486
567
  }
487
- userInitiatedTranistionTypes = ["typed", "address_bar"];
568
+ userInitiatedTransitionTypes = ["typed", "address_bar"];
488
569
  async handlePageTransition() {
489
570
  const transition = await this.getCurrentTransition();
490
571
  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 });
572
+
573
+ const { currentEntry, navigationAction } = transition;
574
+
575
+ switch (navigationAction) {
576
+ case "initial":
577
+ // console.log("Initial navigation, no action taken");
578
+ return;
579
+ case "navigate":
580
+ // console.log("transitionType", transition.transitionType);
581
+ // console.log("sending onGoto event", { url: currentEntry.url,
582
+ // type: "navigate", });
583
+ if (this.userInitiatedTransitionTypes.includes(currentEntry.transitionType)) {
584
+ const env = JSON.parse(readFileSync(this.envName), "utf8");
585
+ const baseUrl = env.baseUrl;
586
+ let url = currentEntry.userTypedURL;
587
+ if (baseUrl && url.startsWith(baseUrl)) {
588
+ url = url.replace(baseUrl, "{{env.baseUrl}}");
589
+ }
590
+ // console.log("User initiated transition");
591
+ this.sendEvent(this.events.onGoto, { url, type: "navigate" });
592
+ }
593
+ return;
594
+ case "back":
595
+ // console.log("User navigated back");
596
+ // console.log("sending onGoto event", {
597
+ // type: "back",
598
+ // });
599
+ this.sendEvent(this.events.onGoto, { type: "back" });
600
+ return;
601
+ case "forward":
602
+ // console.log("User navigated forward"); console.log("sending onGoto event", { type: "forward", });
603
+ this.sendEvent(this.events.onGoto, { type: "forward" });
604
+ return;
605
+ default:
606
+ this.sendEvent(this.events.onGoto, { type: "unknown" });
607
+ return;
495
608
  }
496
609
  }
497
610
 
@@ -618,8 +731,13 @@ export class BVTRecorder {
618
731
  }
619
732
  async closeBrowser() {
620
733
  delete process.env.TEMP_RUN;
621
- await this.watcher.close().then(() => {});
734
+ await this.watcher.close().then(() => { });
622
735
  this.watcher = null;
736
+ this.previousIndex = null;
737
+ this.previousHistoryLength = null;
738
+ this.previousUrl = null;
739
+ this.previousEntries = null;
740
+
623
741
  await closeContext();
624
742
  this.pageSet.clear();
625
743
  }
@@ -671,6 +789,7 @@ export class BVTRecorder {
671
789
  }
672
790
 
673
791
  async startRecordingInput() {
792
+ console.log("startRecordingInput");
674
793
  await this.setMode("recordingInput");
675
794
  }
676
795
  async stopRecordingInput() {
@@ -694,6 +813,7 @@ export class BVTRecorder {
694
813
  }
695
814
 
696
815
  async abortExecution() {
816
+ this.bvtContext.web.abortedExecution = true;
697
817
  await this.stepRunner.abortExecution();
698
818
  }
699
819
  async dealyedRevertMode() {
@@ -702,34 +822,47 @@ export class BVTRecorder {
702
822
  }, 100);
703
823
  this.timerId = timerId;
704
824
  }
705
- async runStep({ step, parametersMap }, options) {
825
+ async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
826
+ const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
706
827
  const _env = {
707
828
  TOKEN: this.TOKEN,
708
829
  TEMP_RUN: true,
709
830
  REPORT_FOLDER: this.bvtContext.reportFolder,
710
831
  BLINQ_ENV: this.envName,
832
+ STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
833
+ CURRENT_STEP_ID: step.id,
711
834
  };
712
835
 
713
836
  this.bvtContext.navigate = true;
837
+ this.bvtContext.loadedRoutes = null;
838
+ this.bvtContext.web.abortedExecution = false;
714
839
  for (const [key, value] of Object.entries(_env)) {
715
840
  process.env[key] = value;
716
841
  }
842
+
717
843
  if (this.timerId) {
718
844
  clearTimeout(this.timerId);
719
845
  this.timerId = null;
720
846
  }
721
847
  await this.setMode("running");
848
+
722
849
  try {
723
- await this.stepRunner.runStep(
850
+ const { result, info } = await this.stepRunner.runStep(
724
851
  {
725
852
  step,
726
853
  parametersMap,
727
854
  envPath: this.envName,
855
+ tags,
856
+ config: this.config,
728
857
  },
729
858
  this.bvtContext,
730
- options
859
+ {
860
+ skipAfter,
861
+ skipBefore,
862
+ }
731
863
  );
732
864
  await this.revertMode();
865
+ return { info };
733
866
  } catch (error) {
734
867
  await this.revertMode();
735
868
  throw error;
@@ -740,22 +873,42 @@ export class BVTRecorder {
740
873
  this.bvtContext.navigate = false;
741
874
  }
742
875
  }
743
- async runScenario({ steps, parametersMap }) {
744
- for (const step of steps) {
745
- await this.runStep({ step, parametersMap });
746
- }
747
- }
748
876
  async saveScenario({ scenario, featureName, override, isSingleStep }) {
749
877
  await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
750
878
  if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
751
- await this.cleanup();
879
+ await this.cleanup({ tags: scenario.tags });
752
880
  }
753
881
  async getImplementedSteps() {
754
- return getImplementedSteps(this.projectDir);
882
+ const stepsAndScenarios = await getImplementedSteps(this.projectDir);
883
+ const implementedSteps = stepsAndScenarios.implementedSteps;
884
+ const scenarios = stepsAndScenarios.scenarios;
885
+ for (const scenario of scenarios) {
886
+ this.scenariosStepsMap.set(scenario.name, scenario.steps);
887
+ delete scenario.steps;
888
+ }
889
+ return {
890
+ implementedSteps,
891
+ scenarios,
892
+ };
755
893
  }
756
894
  async getStepsAndCommandsForScenario({ name, featureName }) {
757
- return getStepsAndCommandsForScenario({ name, featureName, projectDir: this.projectDir });
895
+ const steps = this.scenariosStepsMap.get(name) || [];
896
+ for (const step of steps) {
897
+ if (step.isImplemented) {
898
+ step.commands = this.getCommandsForImplementedStep({ stepName: step.text });
899
+ } else {
900
+ step.commands = [];
901
+ }
902
+ }
903
+ return steps;
904
+ // return getStepsAndCommandsForScenario({
905
+ // name,
906
+ // featureName,
907
+ // projectDir: this.projectDir,
908
+ // map: this.scenariosStepsMap,
909
+ // });
758
910
  }
911
+
759
912
  async generateStepName({ commands, stepsNames, parameters, map }) {
760
913
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
761
914
  }
@@ -838,9 +991,9 @@ export class BVTRecorder {
838
991
  }
839
992
  }
840
993
 
841
- async discardTestData() {
994
+ async discardTestData({ tags }) {
842
995
  resetTestData(this.envName, this.world);
843
- await this.cleanup();
996
+ await this.cleanup({ tags });
844
997
  }
845
998
  async addToTestData(obj) {
846
999
  if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
@@ -879,6 +1032,7 @@ export class BVTRecorder {
879
1032
  const stepParams = parseStepTextParameters(stepName);
880
1033
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
881
1034
  }
1035
+
882
1036
  loadExistingScenario({ featureName, scenarioName }) {
883
1037
  const step_definitions = loadStepDefinitions(this.projectDir);
884
1038
  const featureFilePath = path.join(this.projectDir, "features", featureName);
@@ -907,6 +1061,7 @@ export class BVTRecorder {
907
1061
  ..._s,
908
1062
  keyword: step.keyword.trim(),
909
1063
  };
1064
+ parseRouteFiles(this.projectDir, _step);
910
1065
  steps.push(_step);
911
1066
  }
912
1067
  return {
@@ -946,7 +1101,7 @@ export class BVTRecorder {
946
1101
  }
947
1102
  return result;
948
1103
  }
949
- async cleanup() {
1104
+ async cleanup({ tags }) {
950
1105
  const noopStep = {
951
1106
  text: "Noop",
952
1107
  isImplemented: true,
@@ -960,6 +1115,7 @@ export class BVTRecorder {
960
1115
  {
961
1116
  step: noopStep,
962
1117
  parametersMap: {},
1118
+ tags: tags || [],
963
1119
  },
964
1120
  {
965
1121
  skipAfter: false,
@@ -998,6 +1154,47 @@ export class BVTRecorder {
998
1154
  return false;
999
1155
  }
1000
1156
  }
1157
+ async initExecution({ tags = [] }) {
1158
+ // run before hooks
1159
+ const noopStep = {
1160
+ text: "Noop",
1161
+ isImplemented: true,
1162
+ };
1163
+ await this.runStep(
1164
+ {
1165
+ step: noopStep,
1166
+ parametersMap: {},
1167
+ tags,
1168
+ },
1169
+ {
1170
+ skipBefore: false,
1171
+ skipAfter: true,
1172
+ }
1173
+ );
1174
+ }
1175
+ async cleanupExecution({ tags = [] }) {
1176
+ // run after hooks
1177
+ const noopStep = {
1178
+ text: "Noop",
1179
+ isImplemented: true,
1180
+ };
1181
+ await this.runStep(
1182
+ {
1183
+ step: noopStep,
1184
+ parametersMap: {},
1185
+ tags,
1186
+ },
1187
+ {
1188
+ skipBefore: true,
1189
+ skipAfter: false,
1190
+ }
1191
+ );
1192
+ }
1193
+ async resetExecution({ tags = [] }) {
1194
+ // run after hooks followed by before hooks
1195
+ await this.cleanupExecution({ tags });
1196
+ await this.initExecution({ tags });
1197
+ }
1001
1198
  }
1002
1199
 
1003
1200
  const parseFeatureFile = (featureFilePath) => {