@dev-blinq/cucumber_client 1.0.1263-dev → 1.0.1263-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 +159 -14068
  2. package/bin/assets/preload/recorderv3.js +3 -1
  3. package/bin/assets/scripts/dom_parent.js +26 -0
  4. package/bin/assets/scripts/recorder.js +8 -4
  5. package/bin/assets/scripts/unique_locators.js +847 -547
  6. package/bin/assets/templates/_hooks_template.txt +37 -0
  7. package/bin/assets/templates/page_template.txt +2 -16
  8. package/bin/assets/templates/utils_template.txt +44 -71
  9. package/bin/client/apiTest/apiTest.js +6 -0
  10. package/bin/client/cli_helpers.js +11 -13
  11. package/bin/client/code_cleanup/utils.js +5 -1
  12. package/bin/client/code_gen/code_inversion.js +61 -4
  13. package/bin/client/code_gen/page_reflection.js +838 -899
  14. package/bin/client/code_gen/playwright_codeget.js +52 -13
  15. package/bin/client/cucumber/feature.js +89 -27
  16. package/bin/client/cucumber/project_to_document.js +1 -1
  17. package/bin/client/cucumber/steps_definitions.js +84 -76
  18. package/bin/client/cucumber_selector.js +17 -1
  19. package/bin/client/local_agent.js +3 -3
  20. package/bin/client/project.js +186 -196
  21. package/bin/client/recorderv3/bvt_recorder.js +290 -80
  22. package/bin/client/recorderv3/implemented_steps.js +74 -16
  23. package/bin/client/recorderv3/index.js +47 -25
  24. package/bin/client/recorderv3/network.js +299 -0
  25. package/bin/client/recorderv3/services.js +4 -16
  26. package/bin/client/recorderv3/step_runner.js +331 -67
  27. package/bin/client/recorderv3/step_utils.js +152 -5
  28. package/bin/client/recorderv3/update_feature.js +32 -30
  29. package/bin/client/recording.js +3 -0
  30. package/bin/client/run_cucumber.js +5 -1
  31. package/bin/client/scenario_report.js +0 -5
  32. package/bin/client/test_scenario.js +0 -1
  33. package/bin/client/utils/socket_logger.js +132 -0
  34. package/bin/index.js +1 -0
  35. package/bin/logger.js +3 -2
  36. package/bin/min/consoleApi.min.cjs +2 -3
  37. package/bin/min/injectedScript.min.cjs +16 -16
  38. package/package.json +24 -14
@@ -3,7 +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 { getImplementedSteps, getStepsAndCommandsForScenario } from "./implemented_steps.js";
6
+ import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
7
7
  import { NamesService } from "./services.js";
8
8
  import { BVTStepRunner } from "./step_runner.js";
9
9
  import { readFile, writeFile } from "fs/promises";
@@ -12,10 +12,9 @@ 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
-
17
+ import socketLogger from "../utils/socket_logger.js";
19
18
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
20
19
 
21
20
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -24,11 +23,11 @@ export function getInitScript(config, options) {
24
23
  window.__bvt_Recorder_config = ${JSON.stringify(config ?? null)};
25
24
  window.__PW_options = ${JSON.stringify(options ?? null)};
26
25
  `;
27
- const recorderScript = readFileSync(path.join(__dirname, "..", "..", "assets", "bundled_scripts", "recorder.js"), "utf8")
28
- return (
29
- preScript +
30
- recorderScript
26
+ const recorderScript = readFileSync(
27
+ path.join(__dirname, "..", "..", "assets", "bundled_scripts", "recorder.js"),
28
+ "utf8"
31
29
  );
30
+ return preScript + recorderScript;
32
31
  }
33
32
 
34
33
  async function evaluate(frame, script) {
@@ -44,15 +43,15 @@ async function evaluate(frame, script) {
44
43
  async function findNestedFrameSelector(frame, obj) {
45
44
  try {
46
45
  const parent = frame.parentFrame();
47
- if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
48
46
  if (!parent) return { children: obj };
49
47
  const frameElement = await frame.frameElement();
50
48
  if (!frameElement) return;
51
49
  const selectors = await parent.evaluate((element) => {
52
- return window.getPWLocators(element);
50
+ return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
53
51
  }, frameElement);
54
52
  return findNestedFrameSelector(parent, { children: obj, selectors });
55
53
  } catch (e) {
54
+ socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
56
55
  console.error(e);
57
56
  }
58
57
  }
@@ -149,6 +148,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
149
148
  };
150
149
  }
151
150
  default: {
151
+ socketLogger.error(`Action not supported: ${action.name}`);
152
152
  console.log("action not supported", action);
153
153
  throw new Error("action not supported");
154
154
  }
@@ -160,6 +160,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
160
160
  * @property {string} projectDir
161
161
  * @property {string} TOKEN
162
162
  * @property {(name:string, data:any)=> void} sendEvent
163
+ * @property {Object} logger
163
164
  */
164
165
  export class BVTRecorder {
165
166
  #currentURL = "";
@@ -173,7 +174,6 @@ export class BVTRecorder {
173
174
  */
174
175
  constructor(initialState) {
175
176
  Object.assign(this, initialState);
176
- this.logger = logger;
177
177
  this.screenshotMap = new Map();
178
178
  this.snapshotMap = new Map();
179
179
  this.scenariosStepsMap = new Map();
@@ -183,14 +183,10 @@ export class BVTRecorder {
183
183
  projectDir: this.projectDir,
184
184
  logger: this.logger,
185
185
  });
186
- this.stepRunner = new BVTStepRunner({
187
- projectDir: this.projectDir,
188
- });
189
186
  this.pageSet = new Set();
190
-
187
+ this.pageMetaDataSet = new Set();
191
188
  this.lastKnownUrlPath = "";
192
- // TODO: what is world?
193
- this.world = { attach: () => { } };
189
+ this.world = { attach: () => {} };
194
190
  this.shouldTakeScreenshot = true;
195
191
  this.watcher = null;
196
192
  }
@@ -203,12 +199,16 @@ export class BVTRecorder {
203
199
  onStepDetails: "BVTRecorder.onStepDetails",
204
200
  getTestData: "BVTRecorder.getTestData",
205
201
  onGoto: "BVTRecorder.onGoto",
202
+ cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
203
+ cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
204
+ cmdExecutionError: "BVTRecorder.cmdExecutionError",
205
+ interceptResults: "BVTRecorder.interceptResults",
206
206
  };
207
207
  bindings = {
208
208
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
209
209
  this.#activeFrame = frame;
210
210
  const nestFrmLoc = await findNestedFrameSelector(frame);
211
- console.log(`Time taken for action: ${event.statistics.time}`);
211
+ this.logger.info(`Time taken for action: ${event.statistics.time}`);
212
212
  await this.onAction({ ...event, nestFrmLoc });
213
213
  },
214
214
  __bvt_getMode: async () => {
@@ -227,8 +227,7 @@ export class BVTRecorder {
227
227
  await this.onClosePopup();
228
228
  },
229
229
  __bvt_log: async (src, message) => {
230
- // this.logger.info(message);
231
- console.log(`Inside Browser: ${message}`);
230
+ this.logger.info(`Inside Browser: ${message}`);
232
231
  },
233
232
  __bvt_getObject: (_src, obj) => {
234
233
  this.processObject(obj);
@@ -293,8 +292,8 @@ export class BVTRecorder {
293
292
  this.#remoteDebuggerPort = await findAvailablePort();
294
293
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
295
294
 
296
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
297
- this.world = { attach: () => { } };
295
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
296
+ this.world = { attach: () => {} };
298
297
 
299
298
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
300
299
  let ai_config = {};
@@ -302,9 +301,10 @@ export class BVTRecorder {
302
301
  try {
303
302
  ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
304
303
  } catch (error) {
305
- console.error("Error reading ai_config.json", error);
304
+ this.logger.error("Error reading ai_config.json", error);
306
305
  }
307
306
  }
307
+ this.config = ai_config;
308
308
  const initScripts = {
309
309
  // recorderCjs: injectedScriptSource,
310
310
  scripts: [
@@ -313,16 +313,37 @@ export class BVTRecorder {
313
313
  ],
314
314
  };
315
315
 
316
- let startTime = Date.now();
317
316
  const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
318
- let stopTime = Date.now();
319
- this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
320
317
  this.bvtContext = bvtContext;
318
+ this.stepRunner = new BVTStepRunner({
319
+ projectDir: this.projectDir,
320
+ sendExecutionStatus: (data) => {
321
+ if (data && data.type) {
322
+ switch (data.type) {
323
+ case "cmdExecutionStart":
324
+ this.sendEvent(this.events.cmdExecutionStart, data);
325
+ break;
326
+ case "cmdExecutionSuccess":
327
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
328
+ break;
329
+ case "cmdExecutionError":
330
+ this.sendEvent(this.events.cmdExecutionError, data);
331
+ break;
332
+ case "interceptResults":
333
+ this.sendEvent(this.events.interceptResults, data);
334
+ break;
335
+ default:
336
+ break;
337
+ }
338
+ }
339
+ },
340
+ bvtContext: this.bvtContext,
341
+ });
321
342
  const context = bvtContext.playContext;
322
343
  this.context = context;
323
344
  this.web = bvtContext.stable || bvtContext.web;
345
+ this.web.tryAllStrategies = true;
324
346
  this.page = bvtContext.page;
325
-
326
347
  this.pageSet.add(this.page);
327
348
  this.lastKnownUrlPath = this._updateUrlPath();
328
349
  const browser = await this.context.browser();
@@ -350,13 +371,15 @@ export class BVTRecorder {
350
371
  }
351
372
  return;
352
373
  } catch (error) {
353
- console.error("Error evaluting in context:", error);
374
+ // console.error("Error evaluting in context:", error);
375
+ this.logger.error("Error evaluating in context:", error);
354
376
  }
355
377
  }
356
378
  }
357
379
 
358
380
  getMode() {
359
- console.log("getMode", this.#mode);
381
+ // console.log("getMode", this.#mode);
382
+ this.logger.info("Current mode:", this.#mode);
360
383
  return this.#mode;
361
384
  }
362
385
 
@@ -398,6 +421,8 @@ export class BVTRecorder {
398
421
  this.sendEvent(this.events.onBrowserClose);
399
422
  }
400
423
  } catch (error) {
424
+ this.logger.error("Error in page close event");
425
+ this.logger.error(error);
401
426
  console.error("Error in page close event");
402
427
  console.error(error);
403
428
  }
@@ -408,8 +433,10 @@ export class BVTRecorder {
408
433
  if (frame !== page.mainFrame()) return;
409
434
  this.handlePageTransition();
410
435
  } catch (error) {
436
+ this.logger.error("Error in handlePageTransition event");
437
+ this.logger.error(error);
411
438
  console.error("Error in handlePageTransition event");
412
- // console.error(error);
439
+ console.error(error);
413
440
  }
414
441
  try {
415
442
  if (frame !== this.#activeFrame) return;
@@ -428,11 +455,82 @@ export class BVTRecorder {
428
455
  // await this._setRecordingMode(frame);
429
456
  // await this._initPage(page);
430
457
  } catch (error) {
458
+ this.logger.error("Error in frame navigate event");
459
+ this.logger.error(error);
431
460
  console.error("Error in frame navigate event");
432
461
  // console.error(error);
433
462
  }
434
463
  });
435
464
  }
465
+
466
+ hasHistoryReplacementAtIndex(previousEntries, currentEntries, index) {
467
+ if (!previousEntries || !currentEntries) return false;
468
+ if (index >= previousEntries.length || index >= currentEntries.length) return false;
469
+
470
+ const prevEntry = previousEntries[index];
471
+ // console.log("prevEntry", prevEntry);
472
+ const currEntry = currentEntries[index];
473
+ // console.log("currEntry", currEntry);
474
+
475
+ // Check if the entry at this index has been replaced
476
+ return prevEntry.id !== currEntry.id;
477
+ }
478
+
479
+ // Even simpler approach for your specific case
480
+ analyzeTransitionType(entries, currentIndex, currentEntry) {
481
+ // console.log("Analyzing transition type");
482
+ // console.log("===========================");
483
+ // console.log("Current Index:", currentIndex);
484
+ // console.log("Current Entry:", currentEntry);
485
+ // console.log("Current Entries:", entries);
486
+ // console.log("Current entries length:", entries.length);
487
+ // console.log("===========================");
488
+ // console.log("Previous Index:", this.previousIndex);
489
+ // // console.log("Previous Entry:", this.previousEntries[this.previousIndex]);
490
+ // console.log("Previous Entries:", this.previousEntries);
491
+ // console.log("Previous entries length:", this.previousHistoryLength);
492
+
493
+ if (this.previousIndex === null || this.previousHistoryLength === null || !this.previousEntries) {
494
+ return {
495
+ action: "initial",
496
+ };
497
+ }
498
+
499
+ const indexDiff = currentIndex - this.previousIndex;
500
+ const lengthDiff = entries.length - this.previousHistoryLength;
501
+
502
+ // Backward navigation
503
+ if (indexDiff < 0) {
504
+ return { action: "back" };
505
+ }
506
+
507
+ // Forward navigation
508
+ if (indexDiff > 0 && lengthDiff === 0) {
509
+ // Check if the entry at current index is the same as before
510
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
511
+
512
+ if (entryReplaced) {
513
+ return { action: "navigate" }; // New navigation that replaced forward history
514
+ } else {
515
+ return { action: "forward" }; // True forward navigation
516
+ }
517
+ }
518
+
519
+ // New navigation (history grew)
520
+ if (lengthDiff > 0) {
521
+ return { action: "navigate" };
522
+ }
523
+
524
+ // Same position, same length
525
+ if (lengthDiff <= 0) {
526
+ const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
527
+
528
+ return entryReplaced ? { action: "navigate" } : { action: "reload" };
529
+ }
530
+
531
+ return { action: "unknown" };
532
+ }
533
+
436
534
  async getCurrentTransition() {
437
535
  if (this?.web?.browser?._name !== "chromium") {
438
536
  return;
@@ -441,35 +539,68 @@ export class BVTRecorder {
441
539
 
442
540
  try {
443
541
  const result = await client.send("Page.getNavigationHistory");
444
- // console.log("result", result);
445
542
  const entries = result.entries;
446
543
  const currentIndex = result.currentIndex;
447
544
 
448
- // ignore if currentIndex is not the last entry
449
- if (currentIndex !== entries.length - 1) return;
450
-
451
545
  const currentEntry = entries[currentIndex];
452
- return currentEntry;
546
+ const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
547
+ this.previousIndex = currentIndex;
548
+ this.previousHistoryLength = entries.length;
549
+ this.previousUrl = currentEntry.url;
550
+ this.previousEntries = [...entries]; // Store a copy of current entries
551
+
552
+ return {
553
+ currentEntry,
554
+ navigationAction: transitionInfo.action,
555
+ };
453
556
  } catch (error) {
454
- console.error("Error in getTransistionType event");
557
+ this.logger.error("Error in getCurrentTransition event");
558
+ this.logger.error(error);
559
+ console.error("Error in getTransistionType event", error);
455
560
  } finally {
456
561
  await client.detach();
457
562
  }
458
563
  }
459
- userInitiatedTranistionTypes = ["typed", "address_bar"];
564
+ userInitiatedTransitionTypes = ["typed", "address_bar"];
460
565
  async handlePageTransition() {
461
566
  const transition = await this.getCurrentTransition();
462
567
  if (!transition) return;
463
- // console.log("transitionType", transition.transitionType);
464
- if (this.userInitiatedTranistionTypes.includes(transition.transitionType)) {
465
- const env = JSON.parse(readFileSync(this.envName), "utf8");
466
- const baseUrl = env.baseUrl;
467
- let url = transition.userTypedURL;
468
- if (baseUrl && url.startsWith(baseUrl)) {
469
- url = url.replace(baseUrl, "{{env.baseUrl}}");
470
- }
471
- // console.log("User initiated transition");
472
- this.sendEvent(this.events.onGoto, { url });
568
+
569
+ const { currentEntry, navigationAction } = transition;
570
+
571
+ switch (navigationAction) {
572
+ case "initial":
573
+ // console.log("Initial navigation, no action taken");
574
+ return;
575
+ case "navigate":
576
+ // console.log("transitionType", transition.transitionType);
577
+ // console.log("sending onGoto event", { url: currentEntry.url,
578
+ // type: "navigate", });
579
+ if (this.userInitiatedTransitionTypes.includes(currentEntry.transitionType)) {
580
+ const env = JSON.parse(readFileSync(this.envName), "utf8");
581
+ const baseUrl = env.baseUrl;
582
+ let url = currentEntry.userTypedURL;
583
+ if (baseUrl && url.startsWith(baseUrl)) {
584
+ url = url.replace(baseUrl, "{{env.baseUrl}}");
585
+ }
586
+ // console.log("User initiated transition");
587
+ this.sendEvent(this.events.onGoto, { url, type: "navigate" });
588
+ }
589
+ return;
590
+ case "back":
591
+ // console.log("User navigated back");
592
+ // console.log("sending onGoto event", {
593
+ // type: "back",
594
+ // });
595
+ this.sendEvent(this.events.onGoto, { type: "back" });
596
+ return;
597
+ case "forward":
598
+ // console.log("User navigated forward"); console.log("sending onGoto event", { type: "forward", });
599
+ this.sendEvent(this.events.onGoto, { type: "forward" });
600
+ return;
601
+ default:
602
+ this.sendEvent(this.events.onGoto, { type: "unknown" });
603
+ return;
473
604
  }
474
605
  }
475
606
 
@@ -493,6 +624,8 @@ export class BVTRecorder {
493
624
  // add listener for frame navigation on new tab
494
625
  this._addFrameNavigateListener(page);
495
626
  } catch (error) {
627
+ this.logger.error("Error in page event");
628
+ this.logger.error(error);
496
629
  console.error("Error in page event");
497
630
  console.error(error);
498
631
  }
@@ -534,6 +667,7 @@ export class BVTRecorder {
534
667
  const { data } = await client.send("Page.captureScreenshot", { format: "png" });
535
668
  return data;
536
669
  } catch (error) {
670
+ this.logger.error("Error in taking browser screenshot");
537
671
  console.error("Error in taking browser screenshot", error);
538
672
  } finally {
539
673
  await client.detach();
@@ -596,8 +730,13 @@ export class BVTRecorder {
596
730
  }
597
731
  async closeBrowser() {
598
732
  delete process.env.TEMP_RUN;
599
- await this.watcher.close().then(() => { });
733
+ await this.watcher.close().then(() => {});
600
734
  this.watcher = null;
735
+ this.previousIndex = null;
736
+ this.previousHistoryLength = null;
737
+ this.previousUrl = null;
738
+ this.previousEntries = null;
739
+
601
740
  await closeContext();
602
741
  this.pageSet.clear();
603
742
  }
@@ -674,40 +813,66 @@ export class BVTRecorder {
674
813
  async abortExecution() {
675
814
  await this.stepRunner.abortExecution();
676
815
  }
816
+
817
+ async pauseExecution({ cmdId }) {
818
+ await this.stepRunner.pauseExecution(cmdId);
819
+ }
820
+
821
+ async resumeExecution({ cmdId }) {
822
+ await this.stepRunner.resumeExecution(cmdId);
823
+ }
824
+
677
825
  async dealyedRevertMode() {
678
826
  const timerId = setTimeout(async () => {
679
827
  await this.revertMode();
680
828
  }, 100);
681
829
  this.timerId = timerId;
682
830
  }
683
- async runStep({ step, parametersMap }, options) {
831
+ async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
832
+ const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
684
833
  const _env = {
685
834
  TOKEN: this.TOKEN,
686
835
  TEMP_RUN: true,
687
836
  REPORT_FOLDER: this.bvtContext.reportFolder,
688
837
  BLINQ_ENV: this.envName,
838
+ //STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
839
+ DEBUG: "blinq:route",
689
840
  };
690
841
 
691
842
  this.bvtContext.navigate = true;
843
+ this.bvtContext.loadedRoutes = null;
844
+ if (listenNetwork) {
845
+ process.env.STORE_DETAILED_NETWORK_DATA = "true";
846
+ } else {
847
+ process.env.STORE_DETAILED_NETWORK_DATA = "false";
848
+ }
692
849
  for (const [key, value] of Object.entries(_env)) {
693
850
  process.env[key] = value;
694
851
  }
852
+
695
853
  if (this.timerId) {
696
854
  clearTimeout(this.timerId);
697
855
  this.timerId = null;
698
856
  }
699
857
  await this.setMode("running");
858
+
700
859
  try {
701
- await this.stepRunner.runStep(
860
+ const { result, info } = await this.stepRunner.runStep(
702
861
  {
703
862
  step,
704
863
  parametersMap,
705
864
  envPath: this.envName,
865
+ tags,
866
+ config: this.config,
706
867
  },
707
868
  this.bvtContext,
708
- options
869
+ {
870
+ skipAfter,
871
+ skipBefore,
872
+ }
709
873
  );
710
874
  await this.revertMode();
875
+ return { info };
711
876
  } catch (error) {
712
877
  await this.revertMode();
713
878
  throw error;
@@ -718,15 +883,10 @@ export class BVTRecorder {
718
883
  this.bvtContext.navigate = false;
719
884
  }
720
885
  }
721
- async runScenario({ steps, parametersMap }) {
722
- for (const step of steps) {
723
- await this.runStep({ step, parametersMap });
724
- }
725
- }
726
886
  async saveScenario({ scenario, featureName, override, isSingleStep }) {
727
887
  await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
728
888
  if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
729
- await this.cleanup();
889
+ await this.cleanup({ tags: scenario.tags });
730
890
  }
731
891
  async getImplementedSteps() {
732
892
  const stepsAndScenarios = await getImplementedSteps(this.projectDir);
@@ -750,6 +910,7 @@ export class BVTRecorder {
750
910
  step.commands = [];
751
911
  }
752
912
  }
913
+ return steps;
753
914
  // return getStepsAndCommandsForScenario({
754
915
  // name,
755
916
  // featureName,
@@ -757,6 +918,7 @@ export class BVTRecorder {
757
918
  // map: this.scenariosStepsMap,
758
919
  // });
759
920
  }
921
+
760
922
  async generateStepName({ commands, stepsNames, parameters, map }) {
761
923
  return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
762
924
  }
@@ -808,10 +970,11 @@ export class BVTRecorder {
808
970
  if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
809
971
  try {
810
972
  const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
811
- this.logger.info("Test data", testData);
973
+ // this.logger.info("Test data", testData);
812
974
  this.sendEvent(this.events.getTestData, testData);
813
975
  } catch (e) {
814
- this.logger.error("Error reading test data file", e);
976
+ // this.logger.error("Error reading test data file", e);
977
+ console.log("Error reading test data file", e);
815
978
  }
816
979
  }
817
980
 
@@ -820,10 +983,12 @@ export class BVTRecorder {
820
983
  this.watcher.on("all", async (event, path) => {
821
984
  try {
822
985
  const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
823
- this.logger.info("Test data", testData);
986
+ // this.logger.info("Test data", testData);
987
+ console.log("Test data changed", testData);
824
988
  this.sendEvent(this.events.getTestData, testData);
825
989
  } catch (e) {
826
- this.logger.error("Error reading test data file", e);
990
+ // this.logger.error("Error reading test data file", e);
991
+ console.log("Error reading test data file", e);
827
992
  }
828
993
  });
829
994
  }
@@ -839,9 +1004,9 @@ export class BVTRecorder {
839
1004
  }
840
1005
  }
841
1006
 
842
- async discardTestData() {
1007
+ async discardTestData({ tags }) {
843
1008
  resetTestData(this.envName, this.world);
844
- await this.cleanup();
1009
+ await this.cleanup({ tags });
845
1010
  }
846
1011
  async addToTestData(obj) {
847
1012
  if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
@@ -856,7 +1021,7 @@ export class BVTRecorder {
856
1021
  .filter((file) => file.endsWith(".feature"))
857
1022
  .map((file) => path.join(this.projectDir, "features", file));
858
1023
  try {
859
- const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
1024
+ const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
860
1025
  const output = {};
861
1026
  parsedFiles.forEach((file) => {
862
1027
  if (!file.feature) return;
@@ -880,10 +1045,11 @@ export class BVTRecorder {
880
1045
  const stepParams = parseStepTextParameters(stepName);
881
1046
  return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
882
1047
  }
1048
+
883
1049
  loadExistingScenario({ featureName, scenarioName }) {
884
1050
  const step_definitions = loadStepDefinitions(this.projectDir);
885
1051
  const featureFilePath = path.join(this.projectDir, "features", featureName);
886
- const gherkinDoc = parseFeatureFile(featureFilePath);
1052
+ const gherkinDoc = this.parseFeatureFile(featureFilePath);
887
1053
  const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
888
1054
 
889
1055
  const steps = [];
@@ -908,6 +1074,7 @@ export class BVTRecorder {
908
1074
  ..._s,
909
1075
  keyword: step.keyword.trim(),
910
1076
  };
1077
+ parseRouteFiles(this.projectDir, _step);
911
1078
  steps.push(_step);
912
1079
  }
913
1080
  return {
@@ -947,7 +1114,7 @@ export class BVTRecorder {
947
1114
  }
948
1115
  return result;
949
1116
  }
950
- async cleanup() {
1117
+ async cleanup({ tags }) {
951
1118
  const noopStep = {
952
1119
  text: "Noop",
953
1120
  isImplemented: true,
@@ -961,6 +1128,7 @@ export class BVTRecorder {
961
1128
  {
962
1129
  step: noopStep,
963
1130
  parametersMap: {},
1131
+ tags: tags || [],
964
1132
  },
965
1133
  {
966
1134
  skipAfter: false,
@@ -999,20 +1167,62 @@ export class BVTRecorder {
999
1167
  return false;
1000
1168
  }
1001
1169
  }
1002
- }
1170
+ async initExecution({ tags = [] }) {
1171
+ // run before hooks
1172
+ const noopStep = {
1173
+ text: "Noop",
1174
+ isImplemented: true,
1175
+ };
1176
+ await this.runStep(
1177
+ {
1178
+ step: noopStep,
1179
+ parametersMap: {},
1180
+ tags,
1181
+ },
1182
+ {
1183
+ skipBefore: false,
1184
+ skipAfter: true,
1185
+ }
1186
+ );
1187
+ }
1188
+ async cleanupExecution({ tags = [] }) {
1189
+ // run after hooks
1190
+ const noopStep = {
1191
+ text: "Noop",
1192
+ isImplemented: true,
1193
+ };
1194
+ await this.runStep(
1195
+ {
1196
+ step: noopStep,
1197
+ parametersMap: {},
1198
+ tags,
1199
+ },
1200
+ {
1201
+ skipBefore: true,
1202
+ skipAfter: false,
1203
+ }
1204
+ );
1205
+ }
1206
+ async resetExecution({ tags = [] }) {
1207
+ // run after hooks followed by before hooks
1208
+ await this.cleanupExecution({ tags });
1209
+ await this.initExecution({ tags });
1210
+ }
1003
1211
 
1004
- const parseFeatureFile = (featureFilePath) => {
1005
- try {
1006
- let id = 0;
1007
- const uuidFn = () => (++id).toString(16);
1008
- const builder = new AstBuilder(uuidFn);
1009
- const matcher = new GherkinClassicTokenMatcher();
1010
- const parser = new Parser(builder, matcher);
1011
- const source = readFileSync(featureFilePath, "utf8");
1012
- const gherkinDocument = parser.parse(source);
1013
- return gherkinDocument;
1014
- } catch (e) {
1015
- console.log(e);
1212
+ parseFeatureFile(featureFilePath) {
1213
+ try {
1214
+ let id = 0;
1215
+ const uuidFn = () => (++id).toString(16);
1216
+ const builder = new AstBuilder(uuidFn);
1217
+ const matcher = new GherkinClassicTokenMatcher();
1218
+ const parser = new Parser(builder, matcher);
1219
+ const source = readFileSync(featureFilePath, "utf8");
1220
+ const gherkinDocument = parser.parse(source);
1221
+ return gherkinDocument;
1222
+ } catch (e) {
1223
+ this.logger.error(`Error parsing feature file: ${featureFilePath}`);
1224
+ console.log(e);
1225
+ }
1226
+ return {};
1016
1227
  }
1017
- return {};
1018
- };
1228
+ }