@dev-blinq/cucumber_client 1.0.1324-dev → 1.0.1324-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 (52) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +108 -108
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/recorderv3.js +3 -1
  4. package/bin/assets/preload/toolbar.js +27 -29
  5. package/bin/assets/preload/unique_locators.js +1 -1
  6. package/bin/assets/preload/yaml.js +288 -275
  7. package/bin/assets/scripts/aria_snapshot.js +223 -220
  8. package/bin/assets/scripts/dom_attr.js +329 -329
  9. package/bin/assets/scripts/dom_parent.js +169 -174
  10. package/bin/assets/scripts/event_utils.js +94 -94
  11. package/bin/assets/scripts/pw.js +2050 -1949
  12. package/bin/assets/scripts/recorder.js +5 -17
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +156 -48
  15. package/bin/assets/scripts/yaml.js +796 -783
  16. package/bin/assets/templates/_hooks_template.txt +41 -0
  17. package/bin/assets/templates/utils_template.txt +1 -44
  18. package/bin/client/apiTest/apiTest.js +6 -0
  19. package/bin/client/cli_helpers.js +11 -13
  20. package/bin/client/code_cleanup/utils.js +5 -1
  21. package/bin/client/code_gen/api_codegen.js +2 -2
  22. package/bin/client/code_gen/code_inversion.js +53 -4
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +26 -18
  25. package/bin/client/cucumber/feature.js +89 -27
  26. package/bin/client/cucumber/feature_data.js +2 -2
  27. package/bin/client/cucumber/project_to_document.js +9 -3
  28. package/bin/client/cucumber/steps_definitions.js +6 -3
  29. package/bin/client/cucumber_selector.js +17 -1
  30. package/bin/client/local_agent.js +6 -5
  31. package/bin/client/parse_feature_file.js +23 -26
  32. package/bin/client/playground/projects/env.json +2 -2
  33. package/bin/client/project.js +186 -196
  34. package/bin/client/recorderv3/bvt_recorder.js +190 -127
  35. package/bin/client/recorderv3/implemented_steps.js +74 -16
  36. package/bin/client/recorderv3/index.js +68 -54
  37. package/bin/client/recorderv3/network.js +22 -5
  38. package/bin/client/recorderv3/scriptTest.js +1 -1
  39. package/bin/client/recorderv3/services.js +4 -16
  40. package/bin/client/recorderv3/step_runner.js +303 -220
  41. package/bin/client/recorderv3/step_utils.js +484 -7
  42. package/bin/client/recorderv3/update_feature.js +32 -30
  43. package/bin/client/run_cucumber.js +5 -1
  44. package/bin/client/scenario_report.js +0 -5
  45. package/bin/client/test_scenario.js +0 -1
  46. package/bin/client/upload-service.js +3 -2
  47. package/bin/client/utils/socket_logger.js +132 -0
  48. package/bin/index.js +1 -0
  49. package/bin/logger.js +3 -2
  50. package/bin/min/consoleApi.min.cjs +2 -3
  51. package/bin/min/injectedScript.min.cjs +16 -16
  52. package/package.json +21 -12
@@ -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
 
@@ -790,19 +790,7 @@ class BVTRecorder {
790
790
 
791
791
  getLocatorsObject(el) {
792
792
  if (this.contextElement) {
793
- const text = this.contextElement.innerText; // TODO: handle case where contextElement is not in dom/ children removed
794
- const contextEl = this.contextElement;
795
- // const { climb, commonParent } = window.getCommonParent(contextEl, el);
796
- const commonParent = this.locatorGenerator.dom_Parent.findLowestCommonAncestor([contextEl, el]);
797
- const climb = this.locatorGenerator.dom_Parent.getClimbCountToParent(contextEl, commonParent);
798
- const result = this.locatorGenerator.getElementLocators(el, {
799
- excludeText: true,
800
- root: commonParent,
801
- });
802
- result.locators.forEach((locator) => {
803
- locator.text = text;
804
- locator.climb = climb;
805
- });
793
+ const result = this.locatorGenerator.toContextLocators(el, this.contextElement);
806
794
  return result;
807
795
  }
808
796
  const isRecordingText = this.#mode === "recordingText";
@@ -894,7 +882,7 @@ class BVTRecorder {
894
882
  this.interestedElements.clear();
895
883
  }
896
884
  processAriaSnapshot(snapshot) {
897
- const matchedElements = this.findMatchingElements(snapshot, this.snapshotElements);
885
+ const matchedElements = this.snapshotUtils.findMatchingElements(snapshot, this.snapshotElements);
898
886
  for (const el of matchedElements.values()) {
899
887
  const element = el;
900
888
  if (element) {
@@ -1,155 +1,162 @@
1
1
  class SnapshotCapturer {
2
- constructor(options = {}) {
3
- const {
4
- inlineImages = true,
5
- inlineStyles = true,
6
- excludeSelectors = []
7
- } = options;
8
- // this.options = {
9
- // inlineImages,
10
- // inlineStyles,
11
- // excludeSelectors
12
- // };
13
- this.inlineImages = inlineImages;
14
- this.inlineStyles = inlineStyles;
15
- this.excludeSelectors = excludeSelectors;
2
+ constructor(options = {}) {
3
+ const { inlineImages = true, inlineStyles = true, excludeSelectors = [] } = options;
4
+ // this.options = {
5
+ // inlineImages,
6
+ // inlineStyles,
7
+ // excludeSelectors
8
+ // };
9
+ this.inlineImages = inlineImages;
10
+ this.inlineStyles = inlineStyles;
11
+ this.excludeSelectors = excludeSelectors;
12
+ }
13
+ imageToDataURL(img, document) {
14
+ try {
15
+ // Create canvas to draw the image
16
+ const canvas = document.createElement("canvas");
17
+ canvas.width = img.naturalWidth || img.width;
18
+ canvas.height = img.naturalHeight || img.height;
19
+ const ctx = canvas.getContext("2d");
20
+
21
+ // Draw image to canvas and convert to data URL
22
+ ctx.drawImage(img, 0, 0);
23
+ return canvas.toDataURL("image/png");
24
+ } catch (error) {
25
+ console.warn(`Failed to inline image: ${img.src}`, error);
26
+ return img.src; // Fall back to original source
16
27
  }
17
- imageToDataURL(img, document) {
18
- try {
19
- // Create canvas to draw the image
20
- const canvas = document.createElement('canvas');
21
- canvas.width = img.naturalWidth || img.width;
22
- canvas.height = img.naturalHeight || img.height;
23
- const ctx = canvas.getContext('2d');
24
-
25
- // Draw image to canvas and convert to data URL
26
- ctx.drawImage(img, 0, 0);
27
- return canvas.toDataURL('image/png');
28
- } catch (error) {
29
- console.warn(`Failed to inline image: ${img.src}`, error);
30
- return img.src; // Fall back to original source
28
+ }
29
+ processStyles(document) {
30
+ if (!this.inlineStyles) return;
31
+
32
+ const stylesheets = Array.from(document.styleSheets);
33
+ for (const sheet of stylesheets) {
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
+ }
31
43
  }
32
- }
33
- processStyles(document) {
34
- if (!this.inlineStyles) return;
35
-
36
- const stylesheets = Array.from(document.styleSheets);
37
- for (const sheet of stylesheets) {
38
- try {
39
- if (!sheet.href) continue; // Skip inline styles
40
- const styleEl = document.createElement('style');
41
- let text = ""
42
- const cssRules = Array.from(sheet.cssRules || []);
43
- for (const rule of cssRules) {
44
- if (rule.cssText) {
45
- text += rule.cssText + '\n';
46
- }
47
- }
48
- styleEl.textContent = text;
49
-
50
- // Replace the link with our new style element
51
- if (sheet.ownerNode && sheet.ownerNode.parentNode) {
52
- sheet.ownerNode.parentNode.replaceChild(styleEl, sheet.ownerNode);
53
- } else if (sheet.ownerNode) {
54
- // If the owner node is not in the document, just append it
55
- document.head.appendChild(styleEl);
56
- }
57
-
58
- } catch (error) {
59
- console.warn(`Error processing stylesheet: ${sheet.href}`, error);
60
- }
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
51
+ document.head.appendChild(styleEl);
61
52
  }
53
+ } catch (error) {
54
+ console.warn(`Error processing stylesheet: ${sheet.href}`, error);
55
+ }
62
56
  }
63
- processImages(document) {
64
- if (!this.inlineImages) return;
65
-
66
- const images = document.querySelectorAll('img');
67
- images.forEach(img => {
68
- // Skip SVGs and already data URLs
69
- if (img.src.startsWith('data:') || img.src.endsWith('.svg')) return;
70
-
71
- // Skip if the image is excluded
72
- if (this.excludeSelectors.some(selector => img.matches(selector))) return;
73
-
74
- // Only inline complete images
75
- if (img.complete && img.naturalWidth !== 0) {
76
- try {
77
- img.setAttribute('src', this.imageToDataURL(img, document));
78
- } catch (e) {
79
- console.warn(`Failed to process image: ${img.src}`, e);
80
- }
81
- }
82
- });
83
- }
84
- removeExcludedElements(document) {
85
- this.excludeSelectors.forEach(selector => {
86
- const elements = document.querySelectorAll(selector);
87
- elements.forEach(el => {
88
- el.parentNode?.removeChild(el);
89
- });
90
- });
91
- }
92
- processComputedStyles(document) {
93
- const elements = document.querySelectorAll('*');
94
-
95
- elements.forEach(el => {
96
- // Skip excluded elements
97
- if (this.excludeSelectors.some(selector => el.matches(selector))) return;
98
-
99
- // Get computed style
100
- const style = window.getComputedStyle(el);
101
-
102
- // Copy important styles to inline style
103
- const importantStyles = [
104
- 'display', 'position', 'width', 'height', 'margin', 'padding',
105
- 'color', 'background-color', 'font-family', 'font-size',
106
- 'text-align', 'line-height', 'border', 'box-shadow', 'opacity'
107
- // Add more styles as needed
108
- ];
109
-
110
- const inlineStyles = [];
111
-
112
- importantStyles.forEach(prop => {
113
- const value = style.getPropertyValue(prop);
114
- if (value) {
115
- inlineStyles.push(`${prop}: ${value}`);
116
- }
117
- });
118
-
119
- // Set inline style
120
- if (inlineStyles.length > 0) {
121
- const currentStyle = el.getAttribute('style') || '';
122
- el.setAttribute('style', currentStyle + inlineStyles.join('; ') + ';');
123
- }
124
- });
125
-
126
- }
127
- createSnapshot() {
128
- // Clone the document to avoid modifying the original
129
- const docClone = window.document.cloneNode(true);
130
-
131
- // Store the original document
132
- const originalDoc = window.document;
57
+ }
58
+ processImages(document) {
59
+ if (!this.inlineImages) return;
133
60
 
134
- // Temporarily "swap" the document for processing
135
- // (We're just using this as a convention - it doesn't actually replace the global document)
136
- const doc = docClone;
61
+ const images = document.querySelectorAll("img");
62
+ images.forEach((img) => {
63
+ // Skip SVGs and already data URLs
64
+ if (img.src.startsWith("data:") || img.src.endsWith(".svg")) return;
137
65
 
138
- // Process the clone
139
- this.processStyles(doc);
140
- this.processImages(doc);
141
- this.removeExcludedElements(doc);
142
- this.processComputedStyles(doc);
66
+ // Skip if the image is excluded
67
+ if (this.excludeSelectors.some((selector) => img.matches(selector))) return;
143
68
 
144
- // Generate HTML with doctype
145
- const doctype = originalDoc.doctype ?
146
- new XMLSerializer().serializeToString(originalDoc.doctype) : '<!DOCTYPE html>';
147
-
148
- // Get the HTML content
149
- const htmlContent = doc.documentElement.outerHTML;
150
-
151
- // Combine doctype and HTML content
152
- return `${doctype}${htmlContent}`;
153
- }
69
+ // Only inline complete images
70
+ if (img.complete && img.naturalWidth !== 0) {
71
+ try {
72
+ img.setAttribute("src", this.imageToDataURL(img, document));
73
+ } catch (e) {
74
+ console.warn(`Failed to process image: ${img.src}`, e);
75
+ }
76
+ }
77
+ });
78
+ }
79
+ removeExcludedElements(document) {
80
+ this.excludeSelectors.forEach((selector) => {
81
+ const elements = document.querySelectorAll(selector);
82
+ elements.forEach((el) => {
83
+ el.parentNode?.removeChild(el);
84
+ });
85
+ });
86
+ }
87
+ processComputedStyles(document) {
88
+ const elements = document.querySelectorAll("*");
89
+
90
+ elements.forEach((el) => {
91
+ // Skip excluded elements
92
+ if (this.excludeSelectors.some((selector) => el.matches(selector))) return;
93
+
94
+ // Get computed style
95
+ const style = window.getComputedStyle(el);
96
+
97
+ // Copy important styles to inline style
98
+ const importantStyles = [
99
+ "display",
100
+ "position",
101
+ "width",
102
+ "height",
103
+ "margin",
104
+ "padding",
105
+ "color",
106
+ "background-color",
107
+ "font-family",
108
+ "font-size",
109
+ "text-align",
110
+ "line-height",
111
+ "border",
112
+ "box-shadow",
113
+ "opacity",
114
+ // Add more styles as needed
115
+ ];
116
+
117
+ const inlineStyles = [];
118
+
119
+ importantStyles.forEach((prop) => {
120
+ const value = style.getPropertyValue(prop);
121
+ if (value) {
122
+ inlineStyles.push(`${prop}: ${value}`);
123
+ }
124
+ });
125
+
126
+ // Set inline style
127
+ if (inlineStyles.length > 0) {
128
+ const currentStyle = el.getAttribute("style") || "";
129
+ el.setAttribute("style", currentStyle + inlineStyles.join("; ") + ";");
130
+ }
131
+ });
132
+ }
133
+ createSnapshot() {
134
+ // Clone the document to avoid modifying the original
135
+ const docClone = window.document.cloneNode(true);
136
+
137
+ // Store the original document
138
+ const originalDoc = window.document;
139
+
140
+ // Temporarily "swap" the document for processing
141
+ // (We're just using this as a convention - it doesn't actually replace the global document)
142
+ const doc = docClone;
143
+
144
+ // Process the clone
145
+ this.processStyles(doc);
146
+ this.processImages(doc);
147
+ this.removeExcludedElements(doc);
148
+ this.processComputedStyles(doc);
149
+
150
+ // Generate HTML with doctype
151
+ const doctype = originalDoc.doctype
152
+ ? new XMLSerializer().serializeToString(originalDoc.doctype)
153
+ : "<!DOCTYPE html>";
154
+
155
+ // Get the HTML content
156
+ const htmlContent = doc.documentElement.outerHTML;
157
+
158
+ // Combine doctype and HTML content
159
+ return `${doctype}${htmlContent}`;
160
+ }
154
161
  }
155
- export default SnapshotCapturer;
162
+ export default SnapshotCapturer;
@@ -150,6 +150,7 @@ class LocatorGenerator {
150
150
  }
151
151
  getTextLocators(element, options) {
152
152
  const injectedScript = this.injectedScript;
153
+ const { textToIgnore = null } = options;
153
154
  const selectorPartLists = this.PW.selectorGenerator.buildTextCandidates(injectedScript, element, options);
154
155
  const result = [];
155
156
  for (const selectorPartList of selectorPartLists) {
@@ -157,6 +158,9 @@ class LocatorGenerator {
157
158
  const tSelectorList = [];
158
159
  for (const selectorPart of selectorPartList) {
159
160
  const { engine, selector } = selectorPart;
161
+ if (textToIgnore && selector.includes(textToIgnore)) {
162
+ continue;
163
+ }
160
164
  if (engine === "css") {
161
165
  tSelectorList.push(selector);
162
166
  } else {
@@ -213,6 +217,52 @@ class LocatorGenerator {
213
217
  }
214
218
  return result;
215
219
  }
220
+ toContextLocators(element, contextElement) {
221
+ const commonParent = this.dom_Parent.findLowestCommonAncestor([contextElement, element]);
222
+ const climb = this.dom_Parent.getClimbCountToParent(contextElement, commonParent);
223
+ const text = contextElement.innerText.trim();
224
+
225
+ const prefix = `internal:text="${text}" >> ${this.getXPathSelector(climb)}`;
226
+ const result = this.getElementLocators(element, {
227
+ root: commonParent,
228
+ strategies: {
229
+ [this.locatorStrategies.custom]: true,
230
+ [this.locatorStrategies.text]: true,
231
+ [this.locatorStrategies.no_text]: true,
232
+ },
233
+ prefix,
234
+ });
235
+
236
+ const attachContextToLocators = (locs) => {
237
+ locs.forEach((loc) => {
238
+ loc.climb = climb;
239
+ loc.text = text;
240
+ });
241
+ };
242
+
243
+ const allStrategyLocators = result.allStrategyLocators;
244
+ const locators = result.locators;
245
+ if (allStrategyLocators) {
246
+ const allLocators = [];
247
+ for (const strategy in allStrategyLocators) {
248
+ if (strategy === "strategy") continue;
249
+ const locators = allStrategyLocators[strategy];
250
+ if (locators.length === 0) continue;
251
+ allLocators.push(...locators);
252
+ allStrategyLocators[strategy] = [];
253
+ }
254
+ attachContextToLocators(allLocators);
255
+ allStrategyLocators[this.locatorStrategies.context] = allLocators;
256
+ allStrategyLocators.strategy = this.locatorStrategies.context;
257
+ result.locators = allLocators;
258
+ return result;
259
+ }
260
+ if (locators) {
261
+ attachContextToLocators(locators);
262
+ return result;
263
+ }
264
+ return result;
265
+ }
216
266
  getContextLocators(element, locators) {
217
267
  if (!locators || !Array.isArray(locators)) {
218
268
  console.error("Locators must be an array");
@@ -283,7 +333,7 @@ class LocatorGenerator {
283
333
  }
284
334
  const textElement = elements[0];
285
335
  // const text = this.PW.selectorUtils.elementText(textElement);
286
- const text = this.injectedScript.utils.elementText(new Map(), textElement).full;
336
+ const text = this.injectedScript.utils.elementText(new Map(), textElement).full.trim();
287
337
 
288
338
  const fullSelector = `${textLocator} >> xpath=${xpath} >> ${restOfSelector}`;
289
339
  const fullElements = this.getMatchingElements(fullSelector, {});
@@ -361,7 +411,7 @@ class LocatorGenerator {
361
411
  hasDigitsInText = digitsRegex.test(text);
362
412
 
363
413
  let pattern = this.PW.stringUtils.escapeRegExp(text.substring(1, text.length - 2));
364
- const re = new RegExp("^" + pattern + "$");
414
+ const re = new RegExp(pattern);
365
415
 
366
416
  finalSelector += `internal:text=${escapeRegexForSelector(re).replace(digitsRegex, "\\d+")} >> `;
367
417
  }
@@ -439,6 +489,10 @@ class LocatorGenerator {
439
489
  const nonUnique = [];
440
490
  try {
441
491
  for (const locator of locators) {
492
+ if (!locator || !locator.css || typeof locator.css !== "string") {
493
+ console.error("Locator must have a valid css selector found: ", locator);
494
+ continue;
495
+ }
442
496
  const elements = this.getMatchingElements(locator.css, options);
443
497
  if (elements.length === 0) {
444
498
  console.warn(`No elements found for locator: ${locator.css}`);
@@ -617,7 +671,7 @@ class LocatorGenerator {
617
671
 
618
672
  getUniqueLocators2(element, locatorGenerator = this.getNoTextLocators, options = {}) {
619
673
  try {
620
- const { maxLocators = 5, root = window.document.body } = options;
674
+ const { maxLocators = 5, root = window.document.body, prefix } = options;
621
675
 
622
676
  if (!element) {
623
677
  return [];
@@ -637,13 +691,7 @@ class LocatorGenerator {
637
691
  },
638
692
  ];
639
693
  } else {
640
- return [
641
- {
642
- css: ":root",
643
- score: 1,
644
- priority: 1,
645
- },
646
- ];
694
+ return [];
647
695
  }
648
696
  }
649
697
 
@@ -670,12 +718,15 @@ class LocatorGenerator {
670
718
 
671
719
  const elementsCache = new Map();
672
720
 
673
- const allAncestors = this.dom_Parent.getFullAncestorChainToRoot(element, root);
721
+ const allAncestors = prefix ? [element] : this.dom_Parent.getFullAncestorChainToRoot(element, root);
674
722
  allAncestors.shift(); // remove the element itself from the ancestors
675
723
 
724
+ const cache = new Map();
725
+ const textToIgnore = this.PW.selectorUtils.elementText(cache, element).full.trim();
676
726
  const ancestorLocators = [];
727
+ let uniqueAncestor = null;
677
728
  for (const ancestor of allAncestors) {
678
- const _locators = locatorGenerator(ancestor, options);
729
+ const _locators = locatorGenerator(ancestor, { ...options, textToIgnore });
679
730
  if (!_locators || !Array.isArray(_locators) || _locators.length === 0) {
680
731
  continue;
681
732
  }
@@ -686,11 +737,15 @@ class LocatorGenerator {
686
737
  });
687
738
  elementsCache.set(ancestor, _categorized);
688
739
  if (_categorized.unique.length > 0) {
740
+ uniqueAncestor = {
741
+ element: ancestor,
742
+ locators: _categorized,
743
+ };
689
744
  break;
690
745
  }
691
746
  }
692
747
 
693
- const uniqueAncestor = ancestorLocators[ancestorLocators.length - 1];
748
+ // const uniqueAncestor = ancestorLocators[ancestorLocators.length - 1];
694
749
 
695
750
  for (const locator of nonUnique) {
696
751
  const selector = locator.css ?? locator.selector;
@@ -699,6 +754,27 @@ class LocatorGenerator {
699
754
  console.warn(`No elements found for locator: ${selector}`);
700
755
  continue;
701
756
  }
757
+ if (!uniqueAncestor) {
758
+ const elements = this.getMatchingElements(selector, options);
759
+ if (elements.length === 1 && elements[0] === element) {
760
+ result.push({
761
+ css: selector,
762
+ score: locator.score,
763
+ priority: 1,
764
+ });
765
+ } else {
766
+ const index = elements.indexOf(element);
767
+ if (index !== -1) {
768
+ result.push({
769
+ css: selector,
770
+ index,
771
+ score: locator.score + 200,
772
+ priority: 2,
773
+ });
774
+ }
775
+ }
776
+ continue;
777
+ }
702
778
 
703
779
  for (const unique_locator of uniqueAncestor.locators.unique) {
704
780
  const fullSelector = `${unique_locator.css} >> ${selector}`;
@@ -739,10 +815,32 @@ class LocatorGenerator {
739
815
  }
740
816
  getElementLocators(element, options = {}) {
741
817
  try {
742
- const { excludeText = false } = options;
743
-
744
- const allStrategyLocators = {};
745
- if (this.options?.customAttributes) {
818
+ const {
819
+ excludeText = false,
820
+ strategies = {
821
+ [this.locatorStrategies.custom]: true,
822
+ [this.locatorStrategies.context]: true,
823
+ [this.locatorStrategies.text]: true,
824
+ [this.locatorStrategies.text_with_index]: true,
825
+ [this.locatorStrategies.digitIgnore]: true,
826
+ [this.locatorStrategies.no_text]: true,
827
+ },
828
+ } = options;
829
+
830
+ const allStrategyLocators = {
831
+ [this.locatorStrategies.custom]: [],
832
+ [this.locatorStrategies.context]: [],
833
+ [this.locatorStrategies.text]: [],
834
+ [this.locatorStrategies.text_with_index]: [],
835
+ [this.locatorStrategies.digitIgnore]: [],
836
+ [this.locatorStrategies.no_text]: [],
837
+ };
838
+ if (
839
+ strategies[this.locatorStrategies.custom] &&
840
+ this.options?.customAttributes &&
841
+ Array.isArray(this.options.customAttributes) &&
842
+ this.options.customAttributes.length > 0
843
+ ) {
746
844
  console.groupCollapsed("Generating Custom locators for element:", element);
747
845
  const customLocators = this.getUniqueLocators(element, this.getCustomLocators.bind(this), options);
748
846
  if (customLocators.length > 0) {
@@ -751,43 +849,53 @@ class LocatorGenerator {
751
849
  }
752
850
  console.groupEnd();
753
851
  if (!excludeText) {
754
- console.groupCollapsed("Generating Text locators for element:", element);
755
- const basicLocators = this.getUniqueLocators(element, this.getTextLocators.bind(this), options);
756
- console.groupEnd();
757
- if (basicLocators.length > 0) {
758
- allStrategyLocators[this.locatorStrategies.text] = basicLocators;
759
- }
852
+ if (strategies[this.locatorStrategies.text]) {
853
+ console.groupCollapsed("Generating Text locators for element:", element);
760
854
 
761
- const textWithIndexLocators = this.getTextwithIndexLocators(basicLocators);
762
- if (textWithIndexLocators.length > 0) {
763
- allStrategyLocators[this.locatorStrategies.text_with_index] = textWithIndexLocators;
764
- }
765
- const digitIgnoreLocators = this.getDigitIgnoreLocators(element, basicLocators);
766
- if (digitIgnoreLocators.length > 0) {
767
- allStrategyLocators[this.locatorStrategies.digitIgnore] = digitIgnoreLocators;
768
- }
769
- const contextLocators = this.getContextLocators(element, basicLocators);
770
- if (contextLocators.length > 0) {
771
- allStrategyLocators[this.locatorStrategies.context] = contextLocators;
855
+ const basicLocators = this.getUniqueLocators(element, this.getTextLocators.bind(this), options);
856
+ console.groupEnd();
857
+ if (basicLocators.length > 0) {
858
+ allStrategyLocators[this.locatorStrategies.text] = basicLocators;
859
+ }
860
+ if (strategies[this.locatorStrategies.text_with_index]) {
861
+ const textWithIndexLocators = this.getTextwithIndexLocators(basicLocators);
862
+ if (textWithIndexLocators.length > 0) {
863
+ allStrategyLocators[this.locatorStrategies.text_with_index] = textWithIndexLocators;
864
+ }
865
+ }
866
+ if (strategies[this.locatorStrategies.digitIgnore]) {
867
+ const digitIgnoreLocators = this.getDigitIgnoreLocators(element, basicLocators);
868
+ if (digitIgnoreLocators.length > 0) {
869
+ allStrategyLocators[this.locatorStrategies.digitIgnore] = digitIgnoreLocators;
870
+ }
871
+ }
872
+ if (strategies[this.locatorStrategies.context]) {
873
+ const contextLocators = this.getContextLocators(element, basicLocators);
874
+ if (contextLocators.length > 0) {
875
+ allStrategyLocators[this.locatorStrategies.context] = contextLocators;
876
+ }
877
+ }
772
878
  }
773
879
  }
774
- console.groupCollapsed("Generating No Text locators for element:", element);
775
- const noTextLocators = this.getUniqueLocators(element, this.getNoTextLocators.bind(this), options);
880
+ if (strategies[this.locatorStrategies.no_text]) {
881
+ console.groupCollapsed("Generating No Text locators for element:", element);
882
+ const noTextLocators = this.getUniqueLocators(element, this.getNoTextLocators.bind(this), options);
776
883
 
777
- if (noTextLocators.length > 0) {
778
- allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
779
- } else {
780
- const _locators = [];
781
- _locators.push({
782
- css: this.generateUniqueCSSSelector(element, options),
783
- score: 500,
784
- priority: 3,
785
- });
786
- if (_locators.length > 0) {
787
- allStrategyLocators[this.locatorStrategies.no_text] = _locators;
884
+ if (noTextLocators.length > 0) {
885
+ allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
886
+ } else {
887
+ const _locators = [];
888
+ _locators.push({
889
+ css: this.generateUniqueCSSSelector(element, options),
890
+ score: 500,
891
+ priority: 3,
892
+ });
893
+ if (_locators.length > 0) {
894
+ allStrategyLocators[this.locatorStrategies.no_text] = _locators;
895
+ }
788
896
  }
897
+ console.groupEnd();
789
898
  }
790
- console.groupEnd();
791
899
 
792
900
  let bestStrategy = this.getBestStrategy(allStrategyLocators);
793
901
  if (!bestStrategy) {