@dev-blinq/cucumber_client 1.0.1253-dev → 1.0.1255-dev

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.
@@ -0,0 +1,155 @@
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;
16
+ }
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
31
+ }
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
+ }
61
+ }
62
+ }
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;
133
+
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;
137
+
138
+ // Process the clone
139
+ this.processStyles(doc);
140
+ this.processImages(doc);
141
+ this.removeExcludedElements(doc);
142
+ this.processComputedStyles(doc);
143
+
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
+ }
154
+ }
155
+ export default SnapshotCapturer;
@@ -0,0 +1,552 @@
1
+
2
+ import DOM_Attr from "./dom_attr";
3
+ import DOM_Parent from "./dom_parent";
4
+ // import SnapshotCapturer from "./snapshot_capturer";
5
+ import { __PW } from "./pw"
6
+
7
+ class LocatorGenerator {
8
+ constructor(injectedScript, options = {}) {
9
+ this.locatorStrategies = {
10
+ text: "basic",
11
+ no_text: "no_text",
12
+ custom: "custom",
13
+ context: "context",
14
+ digitIgnore: "ignore_digit",
15
+ text_with_index: "text_with_index",
16
+ };
17
+ this.options = options;
18
+ this.dom_Attr = new DOM_Attr();
19
+ this.dom_Parent = new DOM_Parent();
20
+ this.PW = __PW;
21
+ this.injectedScript = injectedScript;
22
+ }
23
+
24
+ getMatchingElements(selector, options = {}) {
25
+ const { root = window.document, prefix, visible = true } = options;
26
+ if (visible) {
27
+ selector = `${selector} >> visible=true`;
28
+ }
29
+ if (prefix) {
30
+ selector = `${prefix} >> ${selector}`;
31
+ }
32
+ return this.injectedScript.querySelectorAll(
33
+ this.injectedScript.parseSelector(selector),
34
+ root)
35
+ }
36
+
37
+ getLocatorStrategies() {
38
+ return this.locatorStrategies;
39
+ }
40
+ getTextLocators(element, options) {
41
+ const injectedScript = this.injectedScript;
42
+ const selectorPartLists = this.PW.selectorGenerator.buildTextCandidates(injectedScript, element, options);
43
+ const result = [];
44
+ for (const selectorPartList of selectorPartLists) {
45
+ let tScore = 0;
46
+ const tSelectorList = [];
47
+ for (const selectorPart of selectorPartList) {
48
+ const { engine, selector } = selectorPart;
49
+ if (engine === "css") {
50
+ tSelectorList.push(selector);
51
+ } else {
52
+ tSelectorList.push(`${engine}=${selector}`);
53
+ }
54
+ tScore += selectorPart.score;
55
+ }
56
+ const selector = tSelectorList.join(" >> ");
57
+ const score = tScore / selectorPartList.length;
58
+ result.push({
59
+ css: selector,
60
+ score,
61
+ });
62
+ }
63
+ return result;
64
+ }
65
+ getNoTextLocators(element, options) {
66
+ const injectedScript = this.injectedScript;
67
+ const locators = this.PW.selectorGenerator.buildNoTextCandidates(injectedScript, element, options);
68
+ for (const locator of locators) {
69
+ if (locator.engine === "css") {
70
+ locator.css = locator.selector;
71
+ } else {
72
+ locator.css = `${locator.engine}=${locator.selector}`;
73
+ }
74
+ delete locator.engine; // remove engine to avoid memory leak
75
+ delete locator.selector; // remove selector to avoid memory leak
76
+ }
77
+ return locators;
78
+ }
79
+ getCustomLocators(element, options) {
80
+ const { customAttributes = [] } = this.options;
81
+ if (!customAttributes || !Array.isArray(customAttributes)) {
82
+ console.error("Custom attributes must be an array");
83
+ return [];
84
+ }
85
+
86
+ const result = [];
87
+ let hasAttribures = []
88
+ for (const customAttribute of customAttributes) {
89
+ if (!customAttribute || typeof customAttribute !== "string") {
90
+ console.error("Custom attribute must be a string");
91
+ continue;
92
+ }
93
+ const val = element.getAttribute(customAttribute);
94
+ if (val !== null) {
95
+ hasAttribures.push(customAttribute);
96
+ result.push({
97
+ css: `[${customAttribute}="${val}"]`,
98
+ score: 1,
99
+ priority: 1,
100
+ customAttribute,
101
+ value: val
102
+ })
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ getContextLocators(locators) {
108
+
109
+ if (!locators || !Array.isArray(locators)) {
110
+ console.error("Locators must be an array");
111
+ return [];
112
+ }
113
+ const result = [];
114
+ const textSet = new Set();
115
+ for (const locator of locators) {
116
+ const selector = locator.css;
117
+ if (!selector || typeof selector !== "string") {
118
+ console.error("Locator must have a valid css selector");
119
+ continue;
120
+ }
121
+ const parseResult = this.injectedScript.parseSelector(selector);
122
+ const parts = parseResult.parts;
123
+ if (!parts || !Array.isArray(parts) || parts.length === 0) {
124
+ console.error("Locator must have a valid css selector");
125
+ continue;
126
+ }
127
+ // ignore parts.length < 3
128
+ if (parts.length < 3) {
129
+ // console.warn("Locator must have at least 3 parts to be a context locator");
130
+ continue;
131
+ }
132
+ const firstPart = parts[0];
133
+ if (firstPart.name !== "internal:text") {
134
+ // console.warn("Locator must have internal:text as the first part to be a context locator");
135
+ continue;
136
+ }
137
+ const textBody = firstPart.body;
138
+ if (!textBody || typeof textBody !== "string" || textBody.length === 0) {
139
+ console.error("Locator must have a valid text in the first part to be a context locator");
140
+ continue;
141
+ }
142
+ const secondPart = parts[1];
143
+ if (secondPart.name !== "xpath") {
144
+ continue;
145
+ }
146
+ const xpath = secondPart.body;
147
+ if (!xpath || typeof xpath !== "string" || xpath.length === 0) {
148
+ // console.error("Locator must have a valid xpath in the second part to be a context locator");
149
+ continue;
150
+ }
151
+ const climbString = secondPart.body;
152
+ if (!climbString || typeof climbString !== "string" || climbString.length === 0) {
153
+ continue;
154
+ }
155
+ const climbStringRegex = /(\.\.)(\/\.\.)*/;
156
+ try {
157
+ const match = climbStringRegex.test(climbString);
158
+ if (match) {
159
+ const climbCount = climbString.split("..").length - 1;
160
+ const lastIndex = selector.indexOf(climbString);
161
+ const restOfSelector = selector.substring(lastIndex + climbString.length + 3).trim();
162
+ if (restOfSelector.length === 0) {
163
+ // console.warn("Locator must have a valid rest of selector after the xpath part to
164
+ // be a context locator");
165
+ continue;
166
+ }
167
+
168
+ const textLocator = `internal:text=${textBody}`;
169
+ const elements = this.getMatchingElements(textLocator, {});
170
+ if (elements.length !== 1) {
171
+ throw new Error("Context locator must have exactly one element matching the text part");
172
+
173
+ }
174
+ const textElement = elements[0];
175
+ // const text = this.PW.selectorUtils.elementText(textElement);
176
+ const text = this.injectedScript.utils.elementText(new Map(), textElement).full;
177
+ if (!textSet.has(text)) {
178
+ textSet.add(text);
179
+ result.push({
180
+ css: restOfSelector,
181
+ climb: climbCount,
182
+ text,
183
+ priority: 1,
184
+ });
185
+
186
+ }
187
+
188
+
189
+ }
190
+ } catch (error) {
191
+ console.error("Error parsing climb string:", error);
192
+ continue;
193
+
194
+ }
195
+
196
+ }
197
+ // Sort by text length to prioritize shorter texts
198
+ result.sort((a, b) => a.text.length - b.text.length);
199
+ return result;
200
+ }
201
+ getDigitIgnoreLocators(locators) {
202
+ const result = [];
203
+ if (!locators || !Array.isArray(locators)) {
204
+ console.error("Locators must be an array");
205
+ return [];
206
+ }
207
+
208
+ for (const locator of locators) {
209
+ const selector = locator.css;
210
+ if (!selector || typeof selector !== "string") {
211
+ console.error("Locator must have a valid css selector");
212
+ continue;
213
+ }
214
+ const parseresult = this.injectedScript.parseSelector(selector);
215
+ const parts = parseresult.parts;
216
+ if (!parts || !Array.isArray(parts) || parts.length === 0) {
217
+ console.error("Locator must have a valid css selector");
218
+ continue;
219
+ }
220
+ let finalSelector = "";
221
+ let hasDigitsInText = false;
222
+ for (const part of parts) {
223
+
224
+ if (part.name !== "internal:text") {
225
+ finalSelector += `${part.name === "css" ? "" : part.name}=${part.source} >> `;
226
+ continue;
227
+ }
228
+ if (typeof part.body !== "string" || part.body.length === 0) {
229
+ // console.error("Locator must have a valid text in the first part to be a digit ignore locator");
230
+ finalSelector += `${part.name === "css" ? "" : part.name}=${part.source} >> `;
231
+ continue;
232
+ }
233
+ const text = part.body;
234
+ const digitsRegex = /\d+/g;
235
+ hasDigitsInText = digitsRegex.test(text);
236
+
237
+ const regex = text.replace(/\d+/g, "\\d+");
238
+
239
+ finalSelector += `internal:text=${regex} >> `;
240
+
241
+ }
242
+ if (!hasDigitsInText) {
243
+ continue
244
+ }
245
+ if (finalSelector.endsWith(` >> `)) {
246
+ finalSelector = finalSelector.slice(0, -4);
247
+ }
248
+ if (finalSelector) {
249
+ result.push({
250
+ css: finalSelector,
251
+ priority: locator.priority || 1,
252
+ });
253
+ }
254
+ }
255
+ return result;
256
+ }
257
+ getTextwithIndexLocators(locators) {
258
+ if (!locators || !Array.isArray(locators)) {
259
+ console.error("Locators must be an array");
260
+ return [];
261
+ }
262
+ const result = [];
263
+ for (const locator of locators) {
264
+ if (!locator || !locator.css || typeof locator.css !== "string") {
265
+ console.error("Locator must have a valid css selector");
266
+ continue;
267
+ }
268
+ const index = locator.index;
269
+ if (typeof index !== "number" || index < 0) {
270
+ console.error("Locator must have a valid index");
271
+ continue;
272
+ }
273
+ result.push(locator);
274
+ }
275
+ return result;
276
+ }
277
+
278
+ getXPathSelector(climb) {
279
+ if (typeof climb !== "number" || climb < 0) {
280
+ // throw new Error("Climb must be a non-negative integer");
281
+ console.error("Climb must be a non-negative integer");
282
+ return "";
283
+ }
284
+ if (climb === 0) return "";
285
+ let selector = "xpath=..";
286
+ if (climb === 1) {
287
+ return selector;
288
+ }
289
+ for (let i = 1; i < climb; i++) {
290
+ selector += "/..";
291
+ }
292
+ return selector;
293
+
294
+ }
295
+ categorizeLocators(element, locators, options) {
296
+ const unique = [];
297
+ const nonUnique = [];
298
+ for (const locator of locators) {
299
+ const elements = this.getMatchingElements(locator.css, options);
300
+ if (elements.length === 1) {
301
+ if (element === elements[0]) {
302
+ locator.priority = 1;
303
+ unique.push(locator);
304
+ } else if (element.contains(elements[0])) {
305
+ locator.priority = 1;
306
+ const climb = this.dom_Parent.getClimbCountToParent(elements[0], element);
307
+ const climbSelector = this.getXPathSelector(climb);
308
+ const newSelector = `${locator.css} >> ${climbSelector}`;
309
+ locator.css = newSelector;
310
+ unique.push(locator);
311
+ }
312
+ } else {
313
+ locator.priority = 2;
314
+ locator.elements = elements;
315
+ nonUnique.push(locator);
316
+ }
317
+ }
318
+ return { unique, nonUnique };
319
+ }
320
+
321
+
322
+
323
+
324
+ getUniqueLocators(element, locatorGenerator = this.getNoTextLocators, options = {}) {
325
+ try {
326
+
327
+ const { maxLocators = 5, root = window.document.body, next = "LCA" } = options;
328
+
329
+ if (!element) {
330
+ return [];
331
+ }
332
+ if (element === root) {
333
+ if (element === window.document.documentElement) {
334
+ return [{
335
+ css: "html",
336
+ score: 1,
337
+ priority: 1,
338
+ }, {
339
+ css: ":root",
340
+ score: 1,
341
+ priority: 1,
342
+ }
343
+ ]
344
+ } else {
345
+ return [{
346
+ css: ":root",
347
+ score: 1,
348
+ priority: 1,
349
+ // }, {
350
+ // css: ":root",
351
+ // score: 1,
352
+ // priority: 1,
353
+ }
354
+ ]
355
+ }
356
+ }
357
+
358
+ console.log("Generating locators for element:", element);
359
+ const locators = locatorGenerator(element, options);
360
+ console.log("Generated locators:", locators);
361
+ if (!locators || !Array.isArray(locators)) {
362
+ // throw new Error("Locator generator did not return an array of locators");
363
+ console.error("Locator generator did not return an array of locators");
364
+ return [];
365
+ }
366
+
367
+ console.log("Categorizing locators for element:", element);
368
+ const categorizedLocators = this.categorizeLocators(element, locators, options);
369
+ console.log("Categorized locators:", categorizedLocators);
370
+ // categorizedLocators.unique = limitLocators(categorizedLocators.unique, options);
371
+ // categorizedLocators.nonUnique = limitLocators(categorizedLocators.nonUnique, options);
372
+
373
+ const { unique, nonUnique } = categorizedLocators;
374
+ const result = [];
375
+ if (unique.length > 0) {
376
+ result.push(...unique);
377
+ }
378
+ if (result.length >= maxLocators) {
379
+ return result.slice(0, maxLocators);
380
+ }
381
+ let nextElement = null;
382
+ for (const locator of nonUnique) {
383
+ const selector = locator.css ?? locator.selector;
384
+ const elements = locator.elements || this.getMatchingElements(selector, options);
385
+
386
+ if (next === "parent") {
387
+ nextElement = this.dom_Parent.getActualParent(element);
388
+ } else {
389
+
390
+ // find the branching element the child of common parent that contains the element
391
+ const branchingParent = this.dom_Parent.findBranchingParent(elements, element)
392
+ nextElement = branchingParent;
393
+ }
394
+
395
+ if (nextElement && nextElement !== element) {
396
+ if (root.contains(nextElement)) {
397
+ const _result = this.getUniqueLocators(nextElement, locatorGenerator, {
398
+ ...options,
399
+ root,
400
+ });
401
+ for (const _locator of _result) {
402
+
403
+ if (result.length >= maxLocators) {
404
+ return result.slice(0, maxLocators);
405
+ }
406
+ const _selector = _locator.css ?? _locator.selector;
407
+ const fullSelector = `${_selector} >> ${selector}`;
408
+ const _elements = this.getMatchingElements(fullSelector, options);
409
+ const effectiveScore = (_locator.score + locator.score) / 2 + 100;
410
+ if (_elements.length === 1 && _elements[0] === element) {
411
+ _locator.css = fullSelector;
412
+ _locator.score = effectiveScore;
413
+ _locator.priority = 1; // unique locators have higher priority
414
+ result.push(_locator);
415
+ } else {
416
+ const index = _elements.indexOf(element);
417
+ if (index !== -1) {
418
+ // _locator.selector = fullSelector;
419
+ _locator.css = fullSelector;
420
+ _locator.index = index;
421
+ _locator.priority = 2; // non-unique locators have lower priority
422
+ _locator.score = effectiveScore;
423
+ result.push(_locator);
424
+ }
425
+ }
426
+
427
+ }
428
+
429
+ } else {
430
+ const index = elements.indexOf(element);
431
+ if (index !== -1) {
432
+ locator.index = index
433
+ locator.priority = 2; // non-unique locators have lower priority
434
+ result.push(locator);
435
+ }
436
+ }
437
+
438
+ } else {
439
+ const index = elements.indexOf(element);
440
+ if (index !== -1) {
441
+ locator.index = index
442
+ locator.priority = 2; // non-unique locators have lower priority
443
+ result.push(locator);
444
+ }
445
+ }
446
+
447
+ delete locator.elements; // remove elements to avoid memory leak
448
+ delete locator.strategy;
449
+ delete locator.engine;
450
+ delete locator.selector;
451
+ }
452
+
453
+ if (result.length === 0) {
454
+ const parent = this.dom_Parent.getActualParent(element);
455
+ const locs = this.getUniqueLocators(parent, locatorGenerator, {
456
+ ...options,
457
+ root
458
+ });
459
+ result.push(...locs);
460
+ }
461
+
462
+
463
+ result.sort((a, b) => a.score - b.score);
464
+ console.log("Final locators:", result, element);
465
+ console.groupEnd();
466
+ return result.slice(0, maxLocators);
467
+ }
468
+ catch (error) {
469
+ console.error("Error in getUniqueLocators:", error);
470
+ return [];
471
+ }
472
+ }
473
+ getElementLocators(element, options = {}) {
474
+
475
+ const { excludeText = false } = options;
476
+
477
+ const allStrategyLocators = {
478
+ // [LOCATOR_STRATEGIES.text]: basicLocators,
479
+ // [LOCATOR_STRATEGIES.no_text]: noTextLocators,
480
+ }
481
+ if (this.locatorStrategies.options?.customAttributes) {
482
+ console.groupCollapsed("Generating Custom locators for element:", element);
483
+ const customLocators = this.getUniqueLocators(element, this.getCustomLocators.bind(this), options);
484
+ if (customLocators.length > 0) {
485
+ allStrategyLocators[this.locatorStrategies.custom] = customLocators;
486
+
487
+ }
488
+ }
489
+ console.groupEnd();
490
+ if (!excludeText) {
491
+ console.groupCollapsed("Generating Text locators for element:", element);
492
+ const basicLocators = this.getUniqueLocators(element, this.getTextLocators.bind(this), options);
493
+ console.groupEnd();
494
+ if (basicLocators.length > 0) {
495
+ allStrategyLocators[this.locatorStrategies.text] = basicLocators;
496
+ }
497
+
498
+ const textWithIndexLocators = this.getTextwithIndexLocators(basicLocators);
499
+ if (textWithIndexLocators.length > 0) {
500
+ allStrategyLocators[this.locatorStrategies.text_with_index] = textWithIndexLocators;
501
+ }
502
+ const digitIgnoreLocators = this.getDigitIgnoreLocators(basicLocators);
503
+ if (digitIgnoreLocators.length > 0) {
504
+ allStrategyLocators[this.locatorStrategies.digitIgnore] = digitIgnoreLocators;
505
+ }
506
+ const contextLocators = this.getContextLocators(basicLocators);
507
+ if (contextLocators.length > 0) {
508
+ allStrategyLocators[this.locatorStrategies.context] = contextLocators;
509
+ }
510
+ }
511
+ console.groupCollapsed("Generating No Text locators for element:", element);
512
+ const noTextLocators = this.getUniqueLocators(element, this.getNoTextLocators.bind(this), options);
513
+ if (noTextLocators.length > 0) {
514
+ allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
515
+ }
516
+ console.groupEnd();
517
+
518
+ let bestStrategy = this.getBestStrategy(allStrategyLocators);
519
+ if (!bestStrategy) {
520
+ console.warn("No locators found for element:", element);
521
+ bestStrategy = this.locatorStrategies.no_text;
522
+ }
523
+ allStrategyLocators.strategy = bestStrategy;
524
+
525
+ const locators = allStrategyLocators[allStrategyLocators.strategy];
526
+ const result = {
527
+ allStrategyLocators,
528
+ locators,
529
+ element_name: ""
530
+ }
531
+ console.log("Generated locators:", result);
532
+ return result
533
+ }
534
+ getBestStrategy(allStrategyLocators) {
535
+ const orderedPriorities = [
536
+ this.locatorStrategies.custom,
537
+ this.locatorStrategies.context,
538
+ this.locatorStrategies.text,
539
+ this.locatorStrategies.text_with_index,
540
+ this.locatorStrategies.digitIgnore,
541
+ this.locatorStrategies.no_text
542
+ ];
543
+ for (const strategy of orderedPriorities) {
544
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
545
+ return strategy;
546
+ }
547
+ }
548
+ return null;
549
+ }
550
+ }
551
+
552
+ export default LocatorGenerator;