@dev-blinq/cucumber_client 1.0.1378-dev → 1.0.1378-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 (50) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +107 -107
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +13 -23
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +163 -44
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +41 -0
  16. package/bin/assets/templates/utils_template.txt +2 -45
  17. package/bin/client/apiTest/apiTest.js +6 -0
  18. package/bin/client/code_cleanup/utils.js +5 -1
  19. package/bin/client/code_gen/api_codegen.js +2 -2
  20. package/bin/client/code_gen/code_inversion.js +107 -2
  21. package/bin/client/code_gen/function_signature.js +4 -0
  22. package/bin/client/code_gen/page_reflection.js +846 -906
  23. package/bin/client/code_gen/playwright_codeget.js +32 -3
  24. package/bin/client/cucumber/feature.js +4 -0
  25. package/bin/client/cucumber/feature_data.js +2 -2
  26. package/bin/client/cucumber/project_to_document.js +8 -2
  27. package/bin/client/cucumber/steps_definitions.js +6 -3
  28. package/bin/client/cucumber_selector.js +17 -1
  29. package/bin/client/local_agent.js +3 -2
  30. package/bin/client/parse_feature_file.js +23 -26
  31. package/bin/client/playground/projects/env.json +2 -2
  32. package/bin/client/project.js +186 -196
  33. package/bin/client/recorderv3/bvt_init.js +325 -0
  34. package/bin/client/recorderv3/bvt_recorder.js +301 -102
  35. package/bin/client/recorderv3/implemented_steps.js +8 -0
  36. package/bin/client/recorderv3/index.js +4 -303
  37. package/bin/client/recorderv3/scriptTest.js +1 -1
  38. package/bin/client/recorderv3/services.js +430 -154
  39. package/bin/client/recorderv3/step_runner.js +315 -206
  40. package/bin/client/recorderv3/step_utils.js +479 -25
  41. package/bin/client/recorderv3/update_feature.js +9 -5
  42. package/bin/client/recorderv3/wbr_entry.js +61 -0
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/upload-service.js +3 -2
  45. package/bin/client/utils/socket_logger.js +132 -0
  46. package/bin/index.js +4 -1
  47. package/bin/logger.js +3 -2
  48. package/bin/min/consoleApi.min.cjs +2 -3
  49. package/bin/min/injectedScript.min.cjs +16 -16
  50. package/package.json +20 -10
@@ -4,7 +4,7 @@ import { existsSync, readdirSync, readFileSync, rmSync } from "fs";
4
4
  import path from "path";
5
5
  import url from "url";
6
6
  import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
7
- import { NamesService } from "./services.js";
7
+ import { NamesService, PublishService, RemoteBrowserService } from "./services.js";
8
8
  import { BVTStepRunner } from "./step_runner.js";
9
9
  import { readFile, writeFile } from "fs/promises";
10
10
  import { updateStepDefinitions, loadStepDefinitions, getCommandsForImplementedStep } from "./step_utils.js";
@@ -12,11 +12,12 @@ import { updateFeatureFile } from "./update_feature.js";
12
12
  import { parseStepTextParameters } from "../cucumber/utils.js";
13
13
  import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
14
14
  import chokidar from "chokidar";
15
- import logger from "../../logger.js";
16
15
  import { unEscapeNonPrintables } from "../cucumber/utils.js";
17
16
  import { findAvailablePort } from "../utils/index.js";
18
- import { Step } from "../cucumber/feature.js";
19
-
17
+ import socketLogger from "../utils/socket_logger.js";
18
+ import { tmpdir } from "os";
19
+ import { faker } from "@faker-js/faker/locale/en_US";
20
+ import { chromium } from "playwright-core";
20
21
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
21
22
 
22
23
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -45,7 +46,6 @@ async function evaluate(frame, script) {
45
46
  async function findNestedFrameSelector(frame, obj) {
46
47
  try {
47
48
  const parent = frame.parentFrame();
48
- if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
49
49
  if (!parent) return { children: obj };
50
50
  const frameElement = await frame.frameElement();
51
51
  if (!frameElement) return;
@@ -54,6 +54,7 @@ async function findNestedFrameSelector(frame, obj) {
54
54
  }, frameElement);
55
55
  return findNestedFrameSelector(parent, { children: obj, selectors });
56
56
  } catch (e) {
57
+ socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
57
58
  console.error(e);
58
59
  }
59
60
  }
@@ -150,17 +151,30 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
150
151
  };
151
152
  }
152
153
  default: {
154
+ socketLogger.error(`Action not supported: ${action.name}`);
153
155
  console.log("action not supported", action);
154
156
  throw new Error("action not supported");
155
157
  }
156
158
  }
157
159
  };
160
+ const diffPaths = (currentPath, newPath) => {
161
+ const currentDomain = new URL(currentPath).hostname;
162
+ const newDomain = new URL(newPath).hostname;
163
+ if (currentDomain !== newDomain) {
164
+ return true;
165
+ } else {
166
+ const currentRoute = new URL(currentPath).pathname;
167
+ const newRoute = new URL(newPath).pathname;
168
+ return currentRoute !== newRoute;
169
+ }
170
+ };
158
171
  /**
159
172
  * @typedef {Object} BVTRecorderInput
160
173
  * @property {string} envName
161
174
  * @property {string} projectDir
162
175
  * @property {string} TOKEN
163
176
  * @property {(name:string, data:any)=> void} sendEvent
177
+ * @property {Object} logger
164
178
  */
165
179
  export class BVTRecorder {
166
180
  #currentURL = "";
@@ -174,7 +188,6 @@ export class BVTRecorder {
174
188
  */
175
189
  constructor(initialState) {
176
190
  Object.assign(this, initialState);
177
- this.logger = logger;
178
191
  this.screenshotMap = new Map();
179
192
  this.snapshotMap = new Map();
180
193
  this.scenariosStepsMap = new Map();
@@ -184,40 +197,20 @@ export class BVTRecorder {
184
197
  projectDir: this.projectDir,
185
198
  logger: this.logger,
186
199
  });
187
- this.stepRunner = new BVTStepRunner({
188
- projectDir: this.projectDir,
189
- sendExecutionStatus: (data) => {
190
- if (data && data.type) {
191
- switch (data.type) {
192
- case "cmdExecutionStart":
193
- console.log("Sending cmdExecutionStart event for cmdId:", data);
194
- this.sendEvent(this.events.cmdExecutionStart, data);
195
- break;
196
- case "cmdExecutionSuccess":
197
- console.log("Sending cmdExecutionSuccess event for cmdId:", data);
198
- this.sendEvent(this.events.cmdExecutionSuccess, data);
199
- break;
200
- case "cmdExecutionError":
201
- console.log("Sending cmdExecutionError event for cmdId:", data);
202
- this.sendEvent(this.events.cmdExecutionError, data);
203
- break;
204
- case "interceptResults":
205
- console.log("Sending interceptResults event");
206
- this.sendEvent(this.events.interceptResults, data);
207
- break;
208
- default:
209
- console.warn("Unknown command execution status type:", data.type);
210
- break;
211
- }
212
- }
213
- },
214
- });
200
+ this.workspaceService = new PublishService(this.TOKEN);
215
201
  this.pageSet = new Set();
202
+ this.pageMetaDataSet = new Set();
216
203
  this.lastKnownUrlPath = "";
217
- // TODO: what is world?
218
- this.world = { attach: () => {} };
204
+ this.world = { attach: () => { } };
219
205
  this.shouldTakeScreenshot = true;
220
206
  this.watcher = null;
207
+ this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
208
+ this.tempProjectFolder = `${tmpdir()}/bvt_temp_project_${Math.floor(Math.random() * 1000000)}`;
209
+ this.tempSnapshotsFolder = path.join(this.tempProjectFolder, "data/snapshots");
210
+
211
+ if (existsSync(this.networkEventsFolder)) {
212
+ rmSync(this.networkEventsFolder, { recursive: true, force: true });
213
+ }
221
214
  }
222
215
  events = {
223
216
  onFrameNavigate: "BVTRecorder.onFrameNavigate",
@@ -232,12 +225,16 @@ export class BVTRecorder {
232
225
  cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
233
226
  cmdExecutionError: "BVTRecorder.cmdExecutionError",
234
227
  interceptResults: "BVTRecorder.interceptResults",
228
+ onDebugURLChange: "BVTRecorder.onDebugURLChange",
229
+ updateCommand: "BVTRecorder.updateCommand",
230
+ browserStateSync: "BrowserService.stateSync",
231
+ browserStateError: "BrowserService.stateError",
235
232
  };
236
233
  bindings = {
237
234
  __bvt_recordCommand: async ({ frame, page, context }, event) => {
238
235
  this.#activeFrame = frame;
239
236
  const nestFrmLoc = await findNestedFrameSelector(frame);
240
- console.log(`Time taken for action: ${event.statistics.time}`);
237
+ this.logger.info(`Time taken for action: ${event.statistics.time}`);
241
238
  await this.onAction({ ...event, nestFrmLoc });
242
239
  },
243
240
  __bvt_getMode: async () => {
@@ -256,8 +253,7 @@ export class BVTRecorder {
256
253
  await this.onClosePopup();
257
254
  },
258
255
  __bvt_log: async (src, message) => {
259
- // this.logger.info(message);
260
- console.log(`Inside Browser: ${message}`);
256
+ this.logger.info(`Inside Browser: ${message}`);
261
257
  },
262
258
  __bvt_getObject: (_src, obj) => {
263
259
  this.processObject(obj);
@@ -319,11 +315,12 @@ export class BVTRecorder {
319
315
  }
320
316
 
321
317
  async _initBrowser({ url }) {
318
+ socketLogger.info("Only present in 1.0.1293-stage");
322
319
  this.#remoteDebuggerPort = await findAvailablePort();
323
320
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
324
321
 
325
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
326
- this.world = { attach: () => {} };
322
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
323
+ this.world = { attach: () => { } };
327
324
 
328
325
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
329
326
  let ai_config = {};
@@ -331,7 +328,7 @@ export class BVTRecorder {
331
328
  try {
332
329
  ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
333
330
  } catch (error) {
334
- console.error("Error reading ai_config.json", error);
331
+ this.logger.error("Error reading ai_config.json", error);
335
332
  }
336
333
  }
337
334
  this.config = ai_config;
@@ -343,18 +340,47 @@ export class BVTRecorder {
343
340
  ],
344
341
  };
345
342
 
346
- let startTime = Date.now();
347
343
  const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
348
- let stopTime = Date.now();
349
- this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
350
344
  this.bvtContext = bvtContext;
351
- const context = bvtContext.playContext;
352
- this.context = context;
345
+ this.stepRunner = new BVTStepRunner({
346
+ projectDir: this.projectDir,
347
+ sendExecutionStatus: (data) => {
348
+ if (data && data.type) {
349
+ switch (data.type) {
350
+ case "cmdExecutionStart":
351
+ this.sendEvent(this.events.cmdExecutionStart, data);
352
+ break;
353
+ case "cmdExecutionSuccess":
354
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
355
+ break;
356
+ case "cmdExecutionError":
357
+ this.sendEvent(this.events.cmdExecutionError, data);
358
+ break;
359
+ case "interceptResults":
360
+ this.sendEvent(this.events.interceptResults, data);
361
+ break;
362
+ default:
363
+ break;
364
+ }
365
+ }
366
+ },
367
+ bvtContext: this.bvtContext,
368
+ });
369
+ this.context = bvtContext.playContext;
353
370
  this.web = bvtContext.stable || bvtContext.web;
354
371
  this.web.tryAllStrategies = true;
355
372
  this.page = bvtContext.page;
356
-
357
373
  this.pageSet.add(this.page);
374
+ if (process.env.REMOTE_RECORDER === "true") {
375
+ this.browserEmitter = new RemoteBrowserService({
376
+ CDP_CONNECT_URL: `http://localhost:${this.#remoteDebuggerPort}`,
377
+ context: this.context,
378
+ });
379
+ this.browserEmitter.on(this.events.browserStateSync, (state) => {
380
+ this.page = this.browserEmitter.getSelectedPage();
381
+ this.sendEvent(this.events.browserStateSync, state);
382
+ });
383
+ }
358
384
  this.lastKnownUrlPath = this._updateUrlPath();
359
385
  const browser = await this.context.browser();
360
386
  this.browser = browser;
@@ -367,6 +393,14 @@ export class BVTRecorder {
367
393
  this.web.onRestoreSaveState = (url) => {
368
394
  this._initBrowser({ url });
369
395
  };
396
+
397
+ // create a second browser for locator generation
398
+ this.backgroundBrowser = await chromium.launch({
399
+ headless: true,
400
+ });
401
+ this.backgroundContext = await this.backgroundBrowser.newContext({});
402
+ await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
403
+ await this.backgroundContext.newPage();
370
404
  }
371
405
  async onClosePopup() {
372
406
  // console.log("close popups");
@@ -381,13 +415,15 @@ export class BVTRecorder {
381
415
  }
382
416
  return;
383
417
  } catch (error) {
384
- console.error("Error evaluting in context:", error);
418
+ // console.error("Error evaluting in context:", error);
419
+ this.logger.error("Error evaluating in context:", error);
385
420
  }
386
421
  }
387
422
  }
388
423
 
389
424
  getMode() {
390
- console.log("getMode", this.#mode);
425
+ // console.log("getMode", this.#mode);
426
+ this.logger.info("Current mode:", this.#mode);
391
427
  return this.#mode;
392
428
  }
393
429
 
@@ -412,7 +448,7 @@ export class BVTRecorder {
412
448
 
413
449
  // eval init script on current tab
414
450
  // await this._initPage(this.page);
415
- this.#currentURL = new URL(url).pathname;
451
+ this.#currentURL = url;
416
452
 
417
453
  await this.page.dispatchEvent("html", "scroll");
418
454
  await delay(1000);
@@ -429,6 +465,8 @@ export class BVTRecorder {
429
465
  this.sendEvent(this.events.onBrowserClose);
430
466
  }
431
467
  } catch (error) {
468
+ this.logger.error("Error in page close event");
469
+ this.logger.error(error);
432
470
  console.error("Error in page close event");
433
471
  console.error(error);
434
472
  }
@@ -439,8 +477,10 @@ export class BVTRecorder {
439
477
  if (frame !== page.mainFrame()) return;
440
478
  this.handlePageTransition();
441
479
  } catch (error) {
480
+ this.logger.error("Error in handlePageTransition event");
481
+ this.logger.error(error);
442
482
  console.error("Error in handlePageTransition event");
443
- // console.error(error);
483
+ console.error(error);
444
484
  }
445
485
  try {
446
486
  if (frame !== this.#activeFrame) return;
@@ -450,15 +490,18 @@ export class BVTRecorder {
450
490
  element: { inputID: "frame" },
451
491
  });
452
492
 
453
- const newPath = new URL(frame.url()).pathname;
493
+ const newUrl = frame.url();
494
+ const newPath = new URL(newUrl).pathname;
454
495
  const newTitle = await frame.title();
455
- if (newPath !== this.#currentURL) {
496
+ const changed = diffPaths(this.#currentURL, newUrl);
497
+
498
+ if (changed) {
456
499
  this.sendEvent(this.events.onFrameNavigate, { url: newPath, title: newTitle });
457
- this.#currentURL = newPath;
500
+ this.#currentURL = newUrl;
458
501
  }
459
- // await this._setRecordingMode(frame);
460
- // await this._initPage(page);
461
502
  } catch (error) {
503
+ this.logger.error("Error in frame navigate event");
504
+ this.logger.error(error);
462
505
  console.error("Error in frame navigate event");
463
506
  // console.error(error);
464
507
  }
@@ -541,13 +584,9 @@ export class BVTRecorder {
541
584
 
542
585
  try {
543
586
  const result = await client.send("Page.getNavigationHistory");
544
- // console.log("Navigation History:", result);
545
587
  const entries = result.entries;
546
588
  const currentIndex = result.currentIndex;
547
589
 
548
- // ignore if currentIndex is not the last entry
549
- // if (currentIndex !== entries.length - 1) return;
550
-
551
590
  const currentEntry = entries[currentIndex];
552
591
  const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
553
592
  this.previousIndex = currentIndex;
@@ -560,6 +599,8 @@ export class BVTRecorder {
560
599
  navigationAction: transitionInfo.action,
561
600
  };
562
601
  } catch (error) {
602
+ this.logger.error("Error in getCurrentTransition event");
603
+ this.logger.error(error);
563
604
  console.error("Error in getTransistionType event", error);
564
605
  } finally {
565
606
  await client.detach();
@@ -628,6 +669,8 @@ export class BVTRecorder {
628
669
  // add listener for frame navigation on new tab
629
670
  this._addFrameNavigateListener(page);
630
671
  } catch (error) {
672
+ this.logger.error("Error in page event");
673
+ this.logger.error(error);
631
674
  console.error("Error in page event");
632
675
  console.error(error);
633
676
  }
@@ -669,6 +712,7 @@ export class BVTRecorder {
669
712
  const { data } = await client.send("Page.captureScreenshot", { format: "png" });
670
713
  return data;
671
714
  } catch (error) {
715
+ this.logger.error("Error in taking browser screenshot");
672
716
  console.error("Error in taking browser screenshot", error);
673
717
  } finally {
674
718
  await client.detach();
@@ -684,6 +728,52 @@ export class BVTRecorder {
684
728
  console.error("Error in saving screenshot: ", error);
685
729
  }
686
730
  }
731
+ async generateLocators(event) {
732
+ const snapshotDetails = event.snapshotDetails;
733
+ if (!snapshotDetails) {
734
+ throw new Error("No snapshot details found");
735
+ }
736
+ const mode = event.mode;
737
+ const inputID = event.element.inputID;
738
+
739
+ const { id, contextId, doc } = snapshotDetails;
740
+ // const selector = `[data-blinq-id="${id}"]`;
741
+ const newPage = await this.backgroundContext.newPage();
742
+ await newPage.setContent(doc, { waitUntil: "domcontentloaded" });
743
+ const locatorsObj = await newPage.evaluate(
744
+ ([id, contextId, mode]) => {
745
+ const recorder = window.__bvt_Recorder;
746
+ const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
747
+ const el = document.querySelector(`[data-blinq-id="${id}"]`);
748
+ if (contextElement) {
749
+ const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
750
+ return result;
751
+ }
752
+ const isRecordingText = mode === "recordingText";
753
+ return recorder.locatorGenerator.getElementLocators(el, {
754
+ excludeText: isRecordingText,
755
+ });
756
+ },
757
+ [id, contextId, mode]
758
+ );
759
+
760
+ console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
761
+ await newPage.close();
762
+ if (event.nestFrmLoc?.children) {
763
+ locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
764
+ }
765
+
766
+ this.sendEvent(this.events.updateCommand, {
767
+ locators: {
768
+ locators: locatorsObj.locators,
769
+ nestFrmLoc: locatorsObj.nestFrmLoc,
770
+ iframe_src: !event.frame.isTop ? event.frame.url : undefined,
771
+ },
772
+ allStrategyLocators: locatorsObj.allStrategyLocators,
773
+ inputID,
774
+ });
775
+ // const
776
+ }
687
777
  async onAction(event) {
688
778
  this._updateUrlPath();
689
779
  // const locators = this.overlayLocators(event);
@@ -697,25 +787,26 @@ export class BVTRecorder {
697
787
  event.mode === "recordingHover",
698
788
  event.mode === "multiInspecting"
699
789
  ),
700
- locators: {
701
- locators: event.locators,
702
- iframe_src: !event.frame.isTop ? event.frame.url : undefined,
703
- },
704
- allStrategyLocators: event.allStrategyLocators,
790
+ // locators: {
791
+ // locators: event.locators,
792
+ // iframe_src: !event.frame.isTop ? event.frame.url : undefined,
793
+ // },
794
+ // allStrategyLocators: event.allStrategyLocators,
705
795
  url: event.frame.url,
706
796
  title: event.frame.title,
707
797
  extract: {},
708
798
  lastKnownUrlPath: this.lastKnownUrlPath,
709
799
  };
710
- if (event.nestFrmLoc?.children) {
711
- cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
712
- }
800
+ // if (event.nestFrmLoc?.children) {
801
+ // cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
802
+ // }
713
803
  // this.logger.info({ event });
714
804
  if (this.shouldTakeScreenshot) {
715
805
  await this.storeScreenshot(event);
716
806
  }
717
807
  this.sendEvent(this.events.onNewCommand, cmdEvent);
718
808
  this._updateUrlPath();
809
+ await this.generateLocators(event);
719
810
  }
720
811
  _updateUrlPath() {
721
812
  try {
@@ -731,13 +822,12 @@ export class BVTRecorder {
731
822
  }
732
823
  async closeBrowser() {
733
824
  delete process.env.TEMP_RUN;
734
- await this.watcher.close().then(() => {});
825
+ await this.watcher.close().then(() => { });
735
826
  this.watcher = null;
736
827
  this.previousIndex = null;
737
828
  this.previousHistoryLength = null;
738
829
  this.previousUrl = null;
739
830
  this.previousEntries = null;
740
-
741
831
  await closeContext();
742
832
  this.pageSet.clear();
743
833
  }
@@ -789,7 +879,6 @@ export class BVTRecorder {
789
879
  }
790
880
 
791
881
  async startRecordingInput() {
792
- console.log("startRecordingInput");
793
882
  await this.setMode("recordingInput");
794
883
  }
795
884
  async stopRecordingInput() {
@@ -813,9 +902,17 @@ export class BVTRecorder {
813
902
  }
814
903
 
815
904
  async abortExecution() {
816
- this.bvtContext.web.abortedExecution = true;
817
905
  await this.stepRunner.abortExecution();
818
906
  }
907
+
908
+ async pauseExecution({ cmdId }) {
909
+ await this.stepRunner.pauseExecution(cmdId);
910
+ }
911
+
912
+ async resumeExecution({ cmdId }) {
913
+ await this.stepRunner.resumeExecution(cmdId);
914
+ }
915
+
819
916
  async dealyedRevertMode() {
820
917
  const timerId = setTimeout(async () => {
821
918
  await this.revertMode();
@@ -829,13 +926,17 @@ export class BVTRecorder {
829
926
  TEMP_RUN: true,
830
927
  REPORT_FOLDER: this.bvtContext.reportFolder,
831
928
  BLINQ_ENV: this.envName,
832
- STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
833
- CURRENT_STEP_ID: step.id,
929
+ DEBUG: "blinq:route",
930
+ BVT_TEMP_SNAPSHOTS_FOLDER: this.tempSnapshotsFolder,
834
931
  };
835
932
 
836
933
  this.bvtContext.navigate = true;
837
934
  this.bvtContext.loadedRoutes = null;
838
- this.bvtContext.web.abortedExecution = false;
935
+ if (listenNetwork) {
936
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
937
+ } else {
938
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
939
+ }
839
940
  for (const [key, value] of Object.entries(_env)) {
840
941
  process.env[key] = value;
841
942
  }
@@ -871,13 +972,25 @@ export class BVTRecorder {
871
972
  delete process.env[key];
872
973
  }
873
974
  this.bvtContext.navigate = false;
874
- this.bvtContext.web.abortedExecution = false;
875
975
  }
876
976
  }
877
- async saveScenario({ scenario, featureName, override, isSingleStep }) {
878
- await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
879
- if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
880
- await this.cleanup({ tags: scenario.tags });
977
+ async saveScenario({ scenario, featureName, override, isSingleStep, branch, isEditing }) {
978
+ // await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
979
+ // if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
980
+ const res = await this.workspaceService.saveScenario({
981
+ scenario,
982
+ featureName,
983
+ override,
984
+ isSingleStep,
985
+ branch,
986
+ isEditing,
987
+ projectId: path.basename(this.projectDir),
988
+ });
989
+ if (res.success) {
990
+ await this.cleanup({ tags: scenario.tags });
991
+ } else {
992
+ throw new Error(res.message || "Error saving scenario");
993
+ }
881
994
  }
882
995
  async getImplementedSteps() {
883
996
  const stepsAndScenarios = await getImplementedSteps(this.projectDir);
@@ -961,10 +1074,11 @@ export class BVTRecorder {
961
1074
  if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
962
1075
  try {
963
1076
  const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
964
- this.logger.info("Test data", testData);
1077
+ // this.logger.info("Test data", testData);
965
1078
  this.sendEvent(this.events.getTestData, testData);
966
1079
  } catch (e) {
967
- this.logger.error("Error reading test data file", e);
1080
+ // this.logger.error("Error reading test data file", e);
1081
+ console.log("Error reading test data file", e);
968
1082
  }
969
1083
  }
970
1084
 
@@ -973,10 +1087,12 @@ export class BVTRecorder {
973
1087
  this.watcher.on("all", async (event, path) => {
974
1088
  try {
975
1089
  const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
976
- this.logger.info("Test data", testData);
1090
+ // this.logger.info("Test data", testData);
1091
+ console.log("Test data changed", testData);
977
1092
  this.sendEvent(this.events.getTestData, testData);
978
1093
  } catch (e) {
979
- this.logger.error("Error reading test data file", e);
1094
+ // this.logger.error("Error reading test data file", e);
1095
+ console.log("Error reading test data file", e);
980
1096
  }
981
1097
  });
982
1098
  }
@@ -1009,7 +1125,7 @@ export class BVTRecorder {
1009
1125
  .filter((file) => file.endsWith(".feature"))
1010
1126
  .map((file) => path.join(this.projectDir, "features", file));
1011
1127
  try {
1012
- const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
1128
+ const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
1013
1129
  const output = {};
1014
1130
  parsedFiles.forEach((file) => {
1015
1131
  if (!file.feature) return;
@@ -1037,7 +1153,7 @@ export class BVTRecorder {
1037
1153
  loadExistingScenario({ featureName, scenarioName }) {
1038
1154
  const step_definitions = loadStepDefinitions(this.projectDir);
1039
1155
  const featureFilePath = path.join(this.projectDir, "features", featureName);
1040
- const gherkinDoc = parseFeatureFile(featureFilePath);
1156
+ const gherkinDoc = this.parseFeatureFile(featureFilePath);
1041
1157
  const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
1042
1158
 
1043
1159
  const steps = [];
@@ -1196,20 +1312,103 @@ export class BVTRecorder {
1196
1312
  await this.cleanupExecution({ tags });
1197
1313
  await this.initExecution({ tags });
1198
1314
  }
1199
- }
1200
1315
 
1201
- const parseFeatureFile = (featureFilePath) => {
1202
- try {
1203
- let id = 0;
1204
- const uuidFn = () => (++id).toString(16);
1205
- const builder = new AstBuilder(uuidFn);
1206
- const matcher = new GherkinClassicTokenMatcher();
1207
- const parser = new Parser(builder, matcher);
1208
- const source = readFileSync(featureFilePath, "utf8");
1209
- const gherkinDocument = parser.parse(source);
1210
- return gherkinDocument;
1211
- } catch (e) {
1212
- console.log(e);
1316
+ parseFeatureFile(featureFilePath) {
1317
+ try {
1318
+ let id = 0;
1319
+ const uuidFn = () => (++id).toString(16);
1320
+ const builder = new AstBuilder(uuidFn);
1321
+ const matcher = new GherkinClassicTokenMatcher();
1322
+ const parser = new Parser(builder, matcher);
1323
+ const source = readFileSync(featureFilePath, "utf8");
1324
+ const gherkinDocument = parser.parse(source);
1325
+ return gherkinDocument;
1326
+ } catch (e) {
1327
+ this.logger.error(`Error parsing feature file: ${featureFilePath}`);
1328
+ console.log(e);
1329
+ }
1330
+ return {};
1213
1331
  }
1214
- return {};
1215
- };
1332
+
1333
+ stopRecordingNetwork(input) {
1334
+ if (this.bvtContext) {
1335
+ this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
1336
+ }
1337
+ }
1338
+
1339
+ async fakeParams(params) {
1340
+ const newFakeParams = {};
1341
+ Object.keys(params).forEach((key) => {
1342
+ if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
1343
+ newFakeParams[key] = params[key];
1344
+ return;
1345
+ }
1346
+
1347
+ try {
1348
+ const value = params[key].substring(2, params[key].length - 2).trim();
1349
+ const faking = value.split("(")[0].split(".");
1350
+ let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
1351
+ argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
1352
+ let fakeFunc = faker;
1353
+ faking.forEach((f) => {
1354
+ fakeFunc = fakeFunc[f];
1355
+ });
1356
+ const newValue = fakeFunc(argument);
1357
+ newFakeParams[key] = newValue;
1358
+ } catch (error) {
1359
+ newFakeParams[key] = params[key];
1360
+ }
1361
+ });
1362
+
1363
+ return newFakeParams;
1364
+ }
1365
+
1366
+ async getBrowserState() {
1367
+ try {
1368
+ const state = await this.browserEmitter?.getState();
1369
+ this.sendEvent(this.events.browserStateSync, state);
1370
+ } catch (error) {
1371
+ this.logger.error("Error getting browser state:", error);
1372
+ this.sendEvent(this.events.browserStateError, {
1373
+ message: "Error getting browser state",
1374
+ code: "GET_STATE_ERROR",
1375
+ });
1376
+ }
1377
+ }
1378
+
1379
+ async createTab({ url }) {
1380
+ try {
1381
+ await this.browserEmitter?.createTab(url);
1382
+ } catch (error) {
1383
+ this.logger.error("Error creating tab:", error);
1384
+ this.sendEvent(this.events.browserStateError, {
1385
+ message: "Error creating tab",
1386
+ code: "CREATE_TAB_ERROR",
1387
+ });
1388
+ }
1389
+ }
1390
+
1391
+ async closeTab({ pageId }) {
1392
+ try {
1393
+ await this.browserEmitter?.closeTab(pageId);
1394
+ } catch (error) {
1395
+ this.logger.error("Error closing tab:", error);
1396
+ this.sendEvent(this.events.browserStateError, {
1397
+ message: "Error closing tab",
1398
+ code: "CLOSE_TAB_ERROR",
1399
+ });
1400
+ }
1401
+ }
1402
+
1403
+ async selectTab({ pageId }) {
1404
+ try {
1405
+ await this.browserEmitter?.selectTab(pageId);
1406
+ } catch (error) {
1407
+ this.logger.error("Error selecting tab:", error);
1408
+ this.sendEvent(this.events.browserStateError, {
1409
+ message: "Error selecting tab",
1410
+ code: "SELECT_TAB_ERROR",
1411
+ });
1412
+ }
1413
+ }
1414
+ }