@dev-blinq/cucumber_client 1.0.1478-dev → 1.0.1478-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 (47) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +28 -28
  2. package/bin/assets/scripts/recorder.js +87 -34
  3. package/bin/assets/scripts/snapshot_capturer.js +10 -17
  4. package/bin/assets/scripts/unique_locators.js +45 -13
  5. package/bin/assets/templates/_hooks_template.txt +6 -2
  6. package/bin/assets/templates/utils_template.txt +16 -16
  7. package/bin/client/code_cleanup/codemod/find_harcoded_locators.js +173 -0
  8. package/bin/client/code_cleanup/codemod/fix_hardcoded_locators.js +247 -0
  9. package/bin/client/code_cleanup/utils.js +59 -13
  10. package/bin/client/code_gen/code_inversion.js +125 -1
  11. package/bin/client/code_gen/duplication_analysis.js +2 -1
  12. package/bin/client/code_gen/function_signature.js +8 -0
  13. package/bin/client/code_gen/index.js +4 -0
  14. package/bin/client/code_gen/page_reflection.js +90 -9
  15. package/bin/client/code_gen/playwright_codeget.js +173 -77
  16. package/bin/client/codemod/find_harcoded_locators.js +173 -0
  17. package/bin/client/codemod/fix_hardcoded_locators.js +247 -0
  18. package/bin/client/codemod/index.js +8 -0
  19. package/bin/client/codemod/locators_array/find_misstructured_elements.js +148 -0
  20. package/bin/client/codemod/locators_array/fix_misstructured_elements.js +144 -0
  21. package/bin/client/codemod/locators_array/index.js +114 -0
  22. package/bin/client/codemod/types.js +1 -0
  23. package/bin/client/cucumber/feature.js +4 -17
  24. package/bin/client/cucumber/steps_definitions.js +17 -12
  25. package/bin/client/recorderv3/bvt_init.js +310 -0
  26. package/bin/client/recorderv3/bvt_recorder.js +1559 -1182
  27. package/bin/client/recorderv3/constants.js +45 -0
  28. package/bin/client/recorderv3/implemented_steps.js +2 -0
  29. package/bin/client/recorderv3/index.js +3 -305
  30. package/bin/client/recorderv3/mixpanel.js +39 -0
  31. package/bin/client/recorderv3/services.js +839 -142
  32. package/bin/client/recorderv3/step_runner.js +36 -7
  33. package/bin/client/recorderv3/step_utils.js +316 -98
  34. package/bin/client/recorderv3/update_feature.js +85 -37
  35. package/bin/client/recorderv3/utils.js +80 -0
  36. package/bin/client/recorderv3/wbr_entry.js +61 -0
  37. package/bin/client/recording.js +1 -0
  38. package/bin/client/types/locators.js +2 -0
  39. package/bin/client/upload-service.js +2 -0
  40. package/bin/client/utils/app_dir.js +21 -0
  41. package/bin/client/utils/socket_logger.js +100 -125
  42. package/bin/index.js +5 -0
  43. package/package.json +21 -6
  44. package/bin/client/recorderv3/app_dir.js +0 -23
  45. package/bin/client/recorderv3/network.js +0 -299
  46. package/bin/client/recorderv3/scriptTest.js +0 -5
  47. package/bin/client/recorderv3/ws_server.js +0 -72
@@ -144,7 +144,7 @@ class BVTRecorder {
144
144
  getAction: (e) => {
145
145
  this.eventUtils.consumeEvent(e);
146
146
  },
147
- getInterestedElement: () => {},
147
+ getInterestedElement: () => { },
148
148
  hoverOutlineStyle: "",
149
149
  });
150
150
 
@@ -159,7 +159,7 @@ class BVTRecorder {
159
159
  };
160
160
  },
161
161
  getAction: () => null,
162
- getInterestedElement: () => {},
162
+ getInterestedElement: () => { },
163
163
  hoverOutlineStyle: "",
164
164
  });
165
165
 
@@ -174,7 +174,7 @@ class BVTRecorder {
174
174
  };
175
175
  },
176
176
  getAction: () => null,
177
- getInterestedElement: () => {},
177
+ getInterestedElement: () => { },
178
178
  hoverOutlineStyle: "",
179
179
  });
180
180
 
@@ -390,6 +390,7 @@ class BVTRecorder {
390
390
  }
391
391
  case "input": {
392
392
  const target = this.eventUtils.getNearestInteractiveElement(this.eventUtils.deepEventTarget(event));
393
+
393
394
  if (target.nodeName === "INPUT" && target.type.toLowerCase() === "file") {
394
395
  return {
395
396
  details: {
@@ -434,6 +435,36 @@ class BVTRecorder {
434
435
  return;
435
436
  }
436
437
  case "keydown": {
438
+ // override event.preventDefault to capture the value
439
+
440
+ const oldPreventDefault = event.preventDefault.bind(event);
441
+ event.preventDefault = () => {
442
+ if (event.key.length >= 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
443
+ const target = this.eventUtils.getNearestInteractiveElement(this.eventUtils.deepEventTarget(event));
444
+ setTimeout(() => {
445
+ if (event.__bvt_recorded !== undefined) return;
446
+ const valueBefore = target.value;
447
+ let newValue = valueBefore;
448
+
449
+ this.recordEvent(
450
+ {
451
+ details: {
452
+ name: "fill",
453
+ text: newValue,
454
+ },
455
+ element: target,
456
+ },
457
+ target,
458
+ "input",
459
+ event
460
+ );
461
+
462
+ event.__bvt_recorded = true;
463
+ }, 20);
464
+ }
465
+ oldPreventDefault();
466
+ };
467
+
437
468
  if (!this.eventUtils.shouldGenerateKeyPressFor(event)) return;
438
469
  // if (this._actionInProgress(event)) {
439
470
  // this._expectProgrammaticKeyUp = true;
@@ -676,7 +707,8 @@ class BVTRecorder {
676
707
 
677
708
  lastInputId = el.dataset.inputId;
678
709
 
679
- el.__locators = this.getLocatorsObject(el);
710
+ el.__locators = this.getLocatorsObject(el, { maxLocators: 1 });
711
+
680
712
  }
681
713
  const role = this.PW.roleUtils.getAriaRole(el);
682
714
  const label =
@@ -684,13 +716,15 @@ class BVTRecorder {
684
716
  this.PW.roleUtils.getElementAccessibleName(el, true) ||
685
717
  "";
686
718
  const result = this.getElementProperties(el);
719
+ const elText = this.PW.selectorUtils.elementText(new Map(), el);
687
720
  return {
688
721
  role,
689
722
  label,
690
723
  inputID: el.dataset.inputId,
691
724
  tagName: el.tagName,
692
725
  type: el.type,
693
- text: this.PW.selectorUtils.elementText(new Map(), el).full.trim(),
726
+ text: elText.full.trim(),
727
+ textNormalized: elText.normalized.trim(),
694
728
  parent: `tagname: ${el.parentElement?.tagName}\ninnerText: ${el.parentElement?.innerText}`,
695
729
  attrs: {
696
730
  placeholder: el.getAttribute("placeholder"),
@@ -787,15 +821,31 @@ class BVTRecorder {
787
821
  return el.matches(closeBtnSelector);
788
822
  });
789
823
  }
790
-
791
- getLocatorsObject(el) {
824
+ docHasShadowDOM() {
825
+ for (const el of document.all) {
826
+ if (el.tagName.includes("-")) {
827
+ if (["x-bvt-toolbar", "x-bvt-tool"].includes(el.tagName.toLowerCase())) continue;
828
+ if (el.shadowRoot) {
829
+ return true;
830
+ }
831
+ }
832
+ }
833
+ return false;
834
+ }
835
+ getLocatorsObject(el, options = {}) {
836
+ if (!this.docHasShadowDOM()) {
837
+ console.log("No custom elements detected, skipping locator generation");
838
+ return;
839
+ }
840
+ console.log("Custom elements detected locator generation");
792
841
  if (this.contextElement) {
793
- const result = this.locatorGenerator.toContextLocators(el, this.contextElement);
842
+ const result = this.locatorGenerator.toContextLocators(el, this.contextElement, options);
794
843
  return result;
795
844
  }
796
845
  const isRecordingText = this.#mode === "recordingText";
797
846
  return this.locatorGenerator.getElementLocators(el, {
798
847
  excludeText: isRecordingText,
848
+ ...options,
799
849
  });
800
850
  }
801
851
  addListeners() {
@@ -832,31 +882,7 @@ class BVTRecorder {
832
882
  }
833
883
 
834
884
  performance.mark("command-send");
835
- const cmd = {
836
- mode: this.#mode,
837
- action: action.details,
838
- element: this.getElementDetails(actionElement, eventName),
839
- isPopupCloseClick: this.isPopupCloseEvent(e),
840
- // ...this.getLocatorsObject(actionElement),
841
- ...(actionElement.__locators ?? this.getLocatorsObject(actionElement)),
842
- frame: this.getFrameDetails(),
843
- statistics: {
844
- time: `${performance.measure("command-received", "command-send").duration.toFixed(2)} ms`,
845
- },
846
- };
847
- const snapshotDetails = {
848
- id: actionElement.getAttribute("data-blinq-id"),
849
- contextId: this.contextElement?.getAttribute("data-blinq-context-id"),
850
- doc: this.snapshotCapturer.createSnapshot({
851
- excludeSelectors: ["x-bvt-toolbar", "script", "style", "link[rel=stylesheet]"],
852
- }),
853
- };
854
- cmd.snapshotDetails = snapshotDetails;
855
- // eventQueue.enqueue(async () => {
856
- // await bvtRecorderBindings.validateLocators(snapshotDetails);
857
- // });
858
- // console.log(cmd);
859
- await bvtRecorderBindings.recordCommand(cmd);
885
+ this.recordEvent(action, actionElement, eventName, e);
860
886
  this.handleStateTransition(action.element);
861
887
  },
862
888
  { capture: true }
@@ -864,6 +890,33 @@ class BVTRecorder {
864
890
  });
865
891
  }
866
892
 
893
+ recordEvent(action, actionElement, eventName, e) {
894
+ const cmd = {
895
+ mode: this.#mode,
896
+ action: action.details,
897
+ element: this.getElementDetails(actionElement, eventName),
898
+ isPopupCloseClick: this.isPopupCloseEvent(e),
899
+ ...(actionElement.__locators ?? this.getLocatorsObject(actionElement, { maxLocators: 1 })),
900
+ frame: this.getFrameDetails(),
901
+ statistics: {
902
+ time: `${performance.measure("command-received", "command-send").duration.toFixed(2)} ms`,
903
+ },
904
+ };
905
+ const snapshotDetails = {
906
+ id: actionElement.getAttribute("data-blinq-id"),
907
+ contextId: this.contextElement?.getAttribute("data-blinq-context-id"),
908
+ doc: this.snapshotCapturer.createSnapshot({
909
+ excludeSelectors: ["x-bvt-toolbar", "script", "style", "link[rel=stylesheet]"],
910
+ }),
911
+ };
912
+ cmd.snapshotDetails = snapshotDetails;
913
+ // eventQueue.enqueue(async () => {
914
+ // await bvtRecorderBindings.validateLocators(snapshotDetails);
915
+ // });
916
+ // console.log(cmd);
917
+ bvtRecorderBindings.recordCommand(cmd);
918
+ }
919
+
867
920
  // TODO: implement the corresponding logic for the below methods
868
921
  setPopupHandlers(_popopHandlers) {
869
922
  this.popupHandlers = _popopHandlers;
@@ -882,7 +935,7 @@ class BVTRecorder {
882
935
  this.interestedElements.clear();
883
936
  }
884
937
  processAriaSnapshot(snapshot) {
885
- const matchedElements = this.findMatchingElements(snapshot, this.snapshotElements);
938
+ const matchedElements = this.snapshotUtils.findMatchingElements(snapshot, this.snapshotElements);
886
939
  for (const el of matchedElements.values()) {
887
940
  const element = el;
888
941
  if (element) {
@@ -29,25 +29,18 @@ class SnapshotCapturer {
29
29
  processStyles(document) {
30
30
  if (!this.inlineStyles) return;
31
31
 
32
- const stylesheets = Array.from(document.styleSheets);
32
+ const stylesheets = Array.from(window.document.styleSheets);
33
33
  for (const sheet of stylesheets) {
34
34
  try {
35
- if (!sheet.href) continue; // Skip inline styles
36
- const styleEl = document.createElement("style");
37
- let text = "";
38
- const cssRules = Array.from(sheet.cssRules || []);
39
- for (const rule of cssRules) {
40
- if (rule.cssText) {
41
- text += rule.cssText + "\n";
42
- }
43
- }
44
- styleEl.textContent = text;
45
-
46
- // Replace the link with our new style element
47
- if (sheet.ownerNode && sheet.ownerNode.parentNode) {
48
- sheet.ownerNode.parentNode.replaceChild(styleEl, sheet.ownerNode);
49
- } else if (sheet.ownerNode) {
50
- // If the owner node is not in the document, just append it
35
+ const el = sheet.ownerNode.cloneNode(true);
36
+ if (!el.href) {
37
+ document.head.appendChild(el);
38
+ } else {
39
+ const rules = Array.from(sheet.cssRules)
40
+ .map((rule) => rule.cssText)
41
+ .join("\n");
42
+ const styleEl = document.createElement("style");
43
+ styleEl.textContent = rules;
51
44
  document.head.appendChild(styleEl);
52
45
  }
53
46
  } catch (error) {
@@ -217,7 +217,13 @@ class LocatorGenerator {
217
217
  }
218
218
  return result;
219
219
  }
220
- toContextLocators(element, contextElement) {
220
+ getMixedLocators(element, options) {
221
+ const noTextLocators = this.getNoTextLocators(element, options);
222
+ const textLocators = this.getTextLocators(element, options);
223
+ const customLocators = this.getCustomLocators(element, options);
224
+ return [...customLocators, ...textLocators, ...noTextLocators];
225
+ }
226
+ toContextLocators(element, contextElement, options = {}) {
221
227
  const commonParent = this.dom_Parent.findLowestCommonAncestor([contextElement, element]);
222
228
  const climb = this.dom_Parent.getClimbCountToParent(contextElement, commonParent);
223
229
  const text = contextElement.innerText.trim();
@@ -231,6 +237,7 @@ class LocatorGenerator {
231
237
  [this.locatorStrategies.no_text]: true,
232
238
  },
233
239
  prefix,
240
+ ...options,
234
241
  });
235
242
 
236
243
  const attachContextToLocators = (locs) => {
@@ -239,6 +246,7 @@ class LocatorGenerator {
239
246
  loc.text = text;
240
247
  });
241
248
  };
249
+
242
250
  const allStrategyLocators = result.allStrategyLocators;
243
251
  const locators = result.locators;
244
252
  if (allStrategyLocators) {
@@ -486,12 +494,16 @@ class LocatorGenerator {
486
494
  categorizeLocators(element, locators, options) {
487
495
  const unique = [];
488
496
  const nonUnique = [];
497
+ const visible = options?.visible ?? true;
489
498
  try {
490
499
  for (const locator of locators) {
491
500
  if (!locator || !locator.css || typeof locator.css !== "string") {
492
501
  console.error("Locator must have a valid css selector found: ", locator);
493
502
  continue;
494
503
  }
504
+ if (visible === false) {
505
+ locator.visible = false;
506
+ }
495
507
  const elements = this.getMatchingElements(locator.css, options);
496
508
  if (elements.length === 0) {
497
509
  console.warn(`No elements found for locator: ${locator.css}`);
@@ -616,7 +628,7 @@ class LocatorGenerator {
616
628
  result.push(_locator);
617
629
  } else {
618
630
  const index = _elements.indexOf(element);
619
- if (index !== -1 && index < 5) {
631
+ if (index !== -1 && index < 10) {
620
632
  // _locator.selector = fullSelector;
621
633
  _locator.css = fullSelector;
622
634
  _locator.index = index;
@@ -628,7 +640,7 @@ class LocatorGenerator {
628
640
  }
629
641
  } else {
630
642
  const index = elements.indexOf(element);
631
- if (index !== -1 && index < 5) {
643
+ if (index !== -1 && index < 10) {
632
644
  locator.index = index;
633
645
  locator.priority = 2; // non-unique locators have lower priority
634
646
  result.push(locator);
@@ -636,7 +648,7 @@ class LocatorGenerator {
636
648
  }
637
649
  } else {
638
650
  const index = elements.indexOf(element);
639
- if (index !== -1 && index < 5) {
651
+ if (index !== -1 && index < 10) {
640
652
  locator.index = index;
641
653
  locator.priority = 2; // non-unique locators have lower priority
642
654
  result.push(locator);
@@ -789,7 +801,7 @@ class LocatorGenerator {
789
801
  result.push(newLocator);
790
802
  } else {
791
803
  const index = elements.indexOf(element);
792
- if (index !== -1 && index < 5) {
804
+ if (index !== -1 && index < 10) {
793
805
  const effectiveScore = (unique_locator.score + locator.score) / 2;
794
806
  const newLocator = {
795
807
  ...unique_locator,
@@ -812,7 +824,20 @@ class LocatorGenerator {
812
824
  return [];
813
825
  }
814
826
  }
827
+ isElementVisible(element) {
828
+ if (!(element instanceof Element)) return false;
829
+ const style = window.getComputedStyle(element);
830
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
831
+ const rect = element.getBoundingClientRect();
832
+ if (rect.width === 0 || rect.height === 0) return false;
833
+ return true;
834
+ }
815
835
  getElementLocators(element, options = {}) {
836
+ const isVisible = this.PW.domUtils.isElementVisible(element);
837
+ if (isVisible === false) {
838
+ console.warn("Element is not visible: ", element);
839
+ options.visible = isVisible;
840
+ }
816
841
  try {
817
842
  const {
818
843
  excludeText = false,
@@ -883,14 +908,21 @@ class LocatorGenerator {
883
908
  if (noTextLocators.length > 0) {
884
909
  allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
885
910
  } else {
886
- const _locators = [];
887
- _locators.push({
888
- css: this.generateUniqueCSSSelector(element, options),
889
- score: 500,
890
- priority: 3,
891
- });
892
- if (_locators.length > 0) {
893
- allStrategyLocators[this.locatorStrategies.no_text] = _locators;
911
+
912
+ // try mixed locator strategy as last resort
913
+ const mixedLocators = this.getUniqueLocators(element, this.getMixedLocators.bind(this), options);
914
+ if (mixedLocators.length > 0) {
915
+ allStrategyLocators[this.locatorStrategies.no_text] = mixedLocators;
916
+ } else {
917
+ const _locators = [];
918
+ _locators.push({
919
+ css: this.generateUniqueCSSSelector(element, options),
920
+ score: 500,
921
+ priority: 3,
922
+ });
923
+ if (_locators.length > 0) {
924
+ allStrategyLocators[this.locatorStrategies.no_text] = _locators;
925
+ }
894
926
  }
895
927
  }
896
928
  console.groupEnd();
@@ -1,3 +1,7 @@
1
+ import dotenv from "dotenv";
2
+ // Load .env into process.env
3
+ dotenv.config();
4
+
1
5
  import {
2
6
  After,
3
7
  setDefaultTimeout,
@@ -30,8 +34,8 @@ BeforeStep(async function (step) {
30
34
  }
31
35
  });
32
36
 
33
- AfterStep(async function (step) {
37
+ AfterStep(async function ({ result, pickleStep }) {
34
38
  if (context) {
35
- await context.web.afterStep(this, step);
39
+ await context.web.afterStep(this, pickleStep, result);
36
40
  }
37
41
  });
@@ -12,7 +12,7 @@ import path from "path";
12
12
  * @param {string} text the text to verify exists in page
13
13
  * @protect
14
14
  */
15
- async function verifyTextExistsInPage(text) {
15
+ export async function verifyTextExistsInPage(text) {
16
16
  await context.web.verifyTextExistInPage(text, null, this);
17
17
  }
18
18
  Then("Verify the text {string} can be found in the page", verifyTextExistsInPage);
@@ -22,7 +22,7 @@ Then("Verify the text {string} can be found in the page", verifyTextExistsInPage
22
22
  * @param {string} elementDescription element description
23
23
  * @protect
24
24
  */
25
- async function clickOnElement(elementDescription) {
25
+ export async function clickOnElement(elementDescription) {
26
26
  await context.web.simpleClick(elementDescription, null, null, this);
27
27
  }
28
28
  When("click on {string}", clickOnElement);
@@ -36,7 +36,7 @@ When("Click {string}", clickOnElement);
36
36
  * @param {string} value value to fill the element with
37
37
  * @protect
38
38
  */
39
- async function fillElement(elementDescription, value) {
39
+ export async function fillElement(elementDescription, value) {
40
40
  await context.web.simpleClickType(elementDescription, value, null, null, this);
41
41
  }
42
42
  When("fill {string} with {string}", fillElement);
@@ -47,7 +47,7 @@ When("Fill {string} with {string}", fillElement);
47
47
  * @param {string} text the text to verify does not exist in page
48
48
  * @protect
49
49
  */
50
- async function verifyTextNotExistsInPage(text) {
50
+ export async function verifyTextNotExistsInPage(text) {
51
51
  await context.web.waitForTextToDisappear(text, null, this);
52
52
  }
53
53
  Then("Verify the text {string} cannot be found in the page", verifyTextNotExistsInPage);
@@ -57,7 +57,7 @@ Then("Verify the text {string} cannot be found in the page", verifyTextNotExists
57
57
  * @param {string} url URL to navigate
58
58
  * @protect
59
59
  */
60
- async function navigateTo(url) {
60
+ export async function navigateTo(url) {
61
61
  await context.web.goto(url, this);
62
62
  }
63
63
  When("Navigate to {string}", navigateTo);
@@ -66,7 +66,7 @@ When("Navigate to {string}", navigateTo);
66
66
  * Navigate to the current page
67
67
  * @protect
68
68
  */
69
- async function browserNavigateBack() {
69
+ export async function browserNavigateBack() {
70
70
  await context.web.goBack({}, this);
71
71
  }
72
72
  Then("Browser navigate back", browserNavigateBack);
@@ -75,7 +75,7 @@ Then("Browser navigate back", browserNavigateBack);
75
75
  * Navigate forward in browser history
76
76
  * @protect
77
77
  */
78
- async function browserNavigateForward() {
78
+ export async function browserNavigateForward() {
79
79
  await context.web.goForward({}, this);
80
80
  }
81
81
  Then("Browser navigate forward", browserNavigateForward);
@@ -85,7 +85,7 @@ Then("Browser navigate forward", browserNavigateForward);
85
85
  * @param {string} filePath the file path or empty to store in the test data file
86
86
  * @protect
87
87
  */
88
- async function storeBrowserSession(filePath) {
88
+ export async function storeBrowserSession(filePath) {
89
89
  await context.web.saveStoreState(filePath, this);
90
90
  }
91
91
  When("Store browser session {string}", storeBrowserSession);
@@ -95,7 +95,7 @@ When("Store browser session {string}", storeBrowserSession);
95
95
  * @param {string} filePath the file path or empty
96
96
  * @protect
97
97
  */
98
- async function resetBrowserSession(filePath) {
98
+ export async function resetBrowserSession(filePath) {
99
99
  await context.web.restoreSaveState(filePath, this);
100
100
  }
101
101
  When("Reset browser session {string}", resetBrowserSession);
@@ -107,7 +107,7 @@ When("Reset browser session {string}", resetBrowserSession);
107
107
  * @param {string} textToVerify the target text to verify
108
108
  * @protect
109
109
  */
110
- async function verifyTextRelatedToText(textAnchor, climb, textToVerify) {
110
+ export async function verifyTextRelatedToText(textAnchor, climb, textToVerify) {
111
111
  await context.web.verifyTextRelatedToText(textAnchor, climb, textToVerify, null, this);
112
112
  }
113
113
  Then(
@@ -120,7 +120,7 @@ Then(
120
120
  * @requestName the name of the bruno request file
121
121
  * @protect
122
122
  */
123
- async function runBrunoRequest(requestName) {
123
+ export async function runBrunoRequest(requestName) {
124
124
  await executeBrunoRequest(requestName, {}, context, this);
125
125
  }
126
126
  When("Bruno - {string}", runBrunoRequest);
@@ -131,7 +131,7 @@ When("bruno - {string}", runBrunoRequest);
131
131
  * @param {string} fileName the downloaded file to verify
132
132
  * @protect
133
133
  */
134
- async function verify_the_downloaded_file_exists(fileName) {
134
+ export async function verify_the_downloaded_file_exists(fileName) {
135
135
  const downloadFolder = path.join(context.reportFolder, "downloads");
136
136
  const downloadFile = path.join(downloadFolder, fileName);
137
137
  await verifyFileExists(downloadFile, {}, context, this);
@@ -148,7 +148,7 @@ When("Noop", async function () {});
148
148
  * @param {string} url URL to be verified against current URL
149
149
  * @protect
150
150
  */
151
- async function verify_page_url(url) {
151
+ export async function verify_page_url(url) {
152
152
  await context.web.verifyPagePath(url, {}, this);
153
153
  }
154
154
  Then("Verify the page url is {string}", verify_page_url);
@@ -158,7 +158,7 @@ Then("Verify the page url is {string}", verify_page_url);
158
158
  * @param {string} title Title to be verified against current Title
159
159
  * @protect
160
160
  */
161
- async function verify_page_title(title) {
161
+ export async function verify_page_title(title) {
162
162
  await context.web.verifyPageTitle(title, {}, this);
163
163
  }
164
164
  Then("Verify the page title is {string}", verify_page_title);
@@ -170,7 +170,7 @@ Then("Verify the page title is {string}", verify_page_title);
170
170
  * @param {world} - Optional world context
171
171
  * @returns Promise that resolves after the specified duration
172
172
  */
173
- async function sleep(duration) {
174
- await context.web.sleep(duration, {}, null);
173
+ export async function sleep(duration) {
174
+ await context.web.sleep(duration, {}, this);
175
175
  }
176
176
  Then("Sleep for {string} ms", { timeout: -1 }, sleep);