@dev-blinq/cucumber_client 1.0.1352-dev → 1.0.1354-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.
- package/bin/assets/bundled_scripts/recorder.js +1 -1
- package/bin/assets/preload/recorderv3.js +3 -1
- package/bin/assets/scripts/unique_locators.js +822 -815
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/cucumber/feature.js +73 -41
- package/bin/client/local_agent.js +2 -2
- package/bin/client/project.js +1 -1
- package/bin/client/recorderv3/bvt_recorder.js +7 -5
- package/bin/client/recorderv3/implemented_steps.js +11 -12
- package/bin/client/recorderv3/network.js +22 -5
- package/bin/client/recorderv3/step_runner.js +8 -9
- package/bin/client/recorderv3/update_feature.js +19 -24
- package/bin/client/run_cucumber.js +1 -1
- package/bin/client/scenario_report.js +0 -5
- package/bin/client/test_scenario.js +0 -1
- package/bin/index.js +1 -0
- package/package.json +5 -5
|
@@ -1,834 +1,841 @@
|
|
|
1
1
|
import DOM_Parent from "./dom_parent";
|
|
2
2
|
import { __PW } from "./pw";
|
|
3
3
|
const candidateAttributes = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
"slot",
|
|
5
|
+
"alt",
|
|
6
|
+
"name",
|
|
7
|
+
"placeholder",
|
|
8
|
+
"title",
|
|
9
|
+
"aria-label",
|
|
10
|
+
"aria-placeholder",
|
|
11
|
+
"role",
|
|
12
|
+
"data-testid",
|
|
13
|
+
"data-cy",
|
|
14
14
|
];
|
|
15
15
|
function quoteCSSAttributeValue(text) {
|
|
16
|
-
|
|
16
|
+
return `"${text.replace(/["\\]/g, (char) => "\\" + char)}"`;
|
|
17
17
|
}
|
|
18
18
|
function makeSelectorForId(id) {
|
|
19
|
-
|
|
19
|
+
return /^[a-zA-Z][a-zA-Z0-9\-\_]+$/.test(id) ? "#" + id : `[id=${quoteCSSAttributeValue(id)}]`;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function escapeClassName(className) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
let result = "";
|
|
24
|
+
for (let i = 0; i < className.length; i++) result += cssEscapeCharacter(className, i);
|
|
25
|
+
return result;
|
|
26
26
|
}
|
|
27
27
|
function cssEscapeCharacter(s, i) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const c = s.charCodeAt(i);
|
|
29
|
+
if (c === 0) return "\uFFFD";
|
|
30
|
+
if ((c >= 1 && c <= 31) || (c >= 48 && c <= 57 && (i === 0 || (i === 1 && s.charCodeAt(0) === 45))))
|
|
31
|
+
return "\\" + c.toString(16) + " ";
|
|
32
|
+
if (i === 0 && c === 45 && s.length === 1) return "\\" + s.charAt(i);
|
|
33
|
+
if (c >= 128 || c === 45 || c === 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))
|
|
34
|
+
return s.charAt(i);
|
|
35
|
+
return "\\" + s.charAt(i);
|
|
36
36
|
}
|
|
37
37
|
function escapeRegexForSelector(re) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (re.unicode || re.unicodeSets) return String(re);
|
|
39
|
+
return String(re)
|
|
40
|
+
.replace(/(^|[^\\])(\\\\)*(["'`])/g, "$1$2\\$3")
|
|
41
|
+
.replace(/>>/g, "\\>\\>");
|
|
42
42
|
}
|
|
43
43
|
class LocatorGenerator {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
44
|
+
constructor(injectedScript, options = {}) {
|
|
45
|
+
this.locatorStrategies = {
|
|
46
|
+
text: "basic",
|
|
47
|
+
no_text: "no_text",
|
|
48
|
+
custom: "custom",
|
|
49
|
+
context: "context",
|
|
50
|
+
digitIgnore: "ignore_digit",
|
|
51
|
+
text_with_index: "text_with_index",
|
|
52
|
+
};
|
|
53
|
+
this.options = options;
|
|
54
|
+
this.dom_Parent = new DOM_Parent();
|
|
55
|
+
this.PW = __PW;
|
|
56
|
+
this.injectedScript = injectedScript;
|
|
57
|
+
this.cache = new Map();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
generateUniqueCSSSelector(element, options) {
|
|
61
|
+
const root = options?.root || window.document;
|
|
62
|
+
const separator = options?.separator || " > ";
|
|
63
|
+
const isUnique = options?.isunique || ((selector) => this.getMatchingElements(selector, options).length === 1);
|
|
64
|
+
const noCSSId = options?.noCSSId || false;
|
|
65
|
+
|
|
66
|
+
if (!(element instanceof Element)) return "";
|
|
67
|
+
|
|
68
|
+
if (!root.contains(element)) return "";
|
|
69
|
+
|
|
70
|
+
let selector = "";
|
|
71
|
+
const id = element.getAttribute("id");
|
|
72
|
+
if (id && !/\d/.test(id) && !noCSSId) {
|
|
73
|
+
selector = makeSelectorForId(id);
|
|
74
|
+
if (isUnique(selector)) return selector;
|
|
75
|
+
}
|
|
76
|
+
if (element.tagName) {
|
|
77
|
+
selector = element.tagName.toLowerCase() + selector;
|
|
78
|
+
if (isUnique(selector)) return selector;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const classList = Array.from(element.classList).filter((c) => !/\d/.test(c));
|
|
82
|
+
if (classList.length) {
|
|
83
|
+
selector += classList.map((c) => "." + escapeClassName(c)).join("");
|
|
84
|
+
if (isUnique(selector)) return selector;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let attr of candidateAttributes) {
|
|
88
|
+
if (element.hasAttribute(attr)) {
|
|
89
|
+
let value = element.getAttribute(attr);
|
|
90
|
+
if (value === "") continue;
|
|
91
|
+
if (/\d/.test(value)) continue;
|
|
92
|
+
// Escape special characters in attribute value
|
|
93
|
+
value = value.replace(/[!"#$%&'()*+,./:;<=>?@[\]^`{|}~]/g, "\\$&");
|
|
94
|
+
selector += `[${attr}=${this.PW.stringUtils.escapeForAttributeSelector(value)}]`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (isUnique(selector)) return selector;
|
|
98
|
+
|
|
99
|
+
if (element === root) return selector;
|
|
100
|
+
|
|
101
|
+
let parentElement = element.parentElement;
|
|
102
|
+
if (!parentElement) {
|
|
103
|
+
// if element is shadowRoot
|
|
104
|
+
if (element.parentNode instanceof ShadowRoot) {
|
|
105
|
+
const parentElement = element.parentNode.host;
|
|
106
|
+
if (parentElement && parentElement !== root) {
|
|
107
|
+
const parentSelector = this.generateUniqueCSSSelector(parentElement, options);
|
|
108
|
+
selector = parentSelector + " >>> " + selector;
|
|
109
|
+
if (isUnique(selector)) return selector;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else if (parentElement !== root) {
|
|
113
|
+
// if is a slotted element
|
|
114
|
+
if (element.assignedSlot) {
|
|
115
|
+
parentElement = element.assignedSlot.parentElement ?? element.assignedSlot.parentNode.host;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (parentElement && parentElement !== root) {
|
|
119
|
+
const parentSelector = this.generateUniqueCSSSelector(parentElement, options);
|
|
120
|
+
|
|
121
|
+
selector = parentSelector + separator + selector;
|
|
122
|
+
if (isUnique(selector)) return selector;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const siblings = element.parentElement?.children;
|
|
127
|
+
if (siblings) {
|
|
128
|
+
for (let i = 0; i < siblings.length; i++) {
|
|
129
|
+
if (siblings[i] === element) {
|
|
130
|
+
return selector + `:nth-child(${i + 1})`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return "";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getMatchingElements(selector, options = {}) {
|
|
138
|
+
const { root = window.document, prefix, visible = true } = options;
|
|
139
|
+
if (visible) {
|
|
140
|
+
selector = `${selector} >> visible=true`;
|
|
141
|
+
}
|
|
142
|
+
if (prefix) {
|
|
143
|
+
selector = `${prefix} >> ${selector}`;
|
|
144
|
+
}
|
|
145
|
+
return this.injectedScript.querySelectorAll(this.injectedScript.parseSelector(selector), root);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
getLocatorStrategies() {
|
|
149
|
+
return this.locatorStrategies;
|
|
150
|
+
}
|
|
151
|
+
getTextLocators(element, options) {
|
|
152
|
+
const injectedScript = this.injectedScript;
|
|
153
|
+
const selectorPartLists = this.PW.selectorGenerator.buildTextCandidates(injectedScript, element, options);
|
|
154
|
+
const result = [];
|
|
155
|
+
for (const selectorPartList of selectorPartLists) {
|
|
156
|
+
let tScore = 0;
|
|
157
|
+
const tSelectorList = [];
|
|
158
|
+
for (const selectorPart of selectorPartList) {
|
|
159
|
+
const { engine, selector } = selectorPart;
|
|
160
|
+
if (engine === "css") {
|
|
161
|
+
tSelectorList.push(selector);
|
|
162
|
+
} else {
|
|
163
|
+
tSelectorList.push(`${engine}=${selector}`);
|
|
164
|
+
}
|
|
165
|
+
tScore += selectorPart.score;
|
|
166
|
+
}
|
|
167
|
+
const selector = tSelectorList.join(" >> ");
|
|
168
|
+
const score = tScore / selectorPartList.length;
|
|
169
|
+
result.push({
|
|
170
|
+
css: selector,
|
|
171
|
+
score,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
getNoTextLocators(element, options) {
|
|
177
|
+
const injectedScript = this.injectedScript;
|
|
178
|
+
const locators = this.PW.selectorGenerator.buildNoTextCandidates(injectedScript, element, options);
|
|
179
|
+
for (const locator of locators) {
|
|
180
|
+
if (locator.engine === "css") {
|
|
181
|
+
locator.css = locator.selector;
|
|
182
|
+
} else {
|
|
183
|
+
locator.css = `${locator.engine}=${locator.selector}`;
|
|
184
|
+
}
|
|
185
|
+
delete locator.engine; // remove engine to avoid memory leak
|
|
186
|
+
delete locator.selector; // remove selector to avoid memory leak
|
|
187
|
+
}
|
|
188
|
+
return locators;
|
|
189
|
+
}
|
|
190
|
+
getCustomLocators(element, options) {
|
|
191
|
+
const { customAttributes = [] } = this.options;
|
|
192
|
+
if (!customAttributes || !Array.isArray(customAttributes)) {
|
|
193
|
+
console.error("Custom attributes must be an array");
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const result = [];
|
|
198
|
+
let hasAttribures = [];
|
|
199
|
+
for (const customAttribute of customAttributes) {
|
|
200
|
+
if (!customAttribute || typeof customAttribute !== "string") {
|
|
201
|
+
console.error("Custom attribute must be a string");
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const val = element.getAttribute(customAttribute);
|
|
205
|
+
if (val !== null) {
|
|
206
|
+
hasAttribures.push(customAttribute);
|
|
207
|
+
result.push({
|
|
208
|
+
css: `[${customAttribute}=${this.PW.stringUtils.escapeForAttributeSelector(val)}]`,
|
|
209
|
+
score: 1,
|
|
210
|
+
priority: 1,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
getContextLocators(element, locators) {
|
|
217
|
+
if (!locators || !Array.isArray(locators)) {
|
|
218
|
+
console.error("Locators must be an array");
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const result = [];
|
|
222
|
+
try {
|
|
223
|
+
const textSet = new Set();
|
|
224
|
+
for (const locator of locators) {
|
|
225
|
+
const selector = locator.css;
|
|
226
|
+
if (!selector || typeof selector !== "string") {
|
|
227
|
+
console.error("Locator must have a valid css selector");
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const parseResult = this.injectedScript.parseSelector(selector);
|
|
231
|
+
const parts = parseResult.parts;
|
|
232
|
+
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
233
|
+
console.error("Locator must have a valid css selector");
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// ignore parts.length < 3
|
|
237
|
+
if (parts.length < 3) {
|
|
238
|
+
// console.warn("Locator must have at least 3 parts to be a context locator");
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const firstPart = parts[0];
|
|
242
|
+
if (firstPart.name !== "internal:text") {
|
|
243
|
+
// console.warn("Locator must have internal:text as the first part to be a context locator");
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const textBody = firstPart.body;
|
|
247
|
+
if (!textBody || typeof textBody !== "string" || textBody.length === 0) {
|
|
248
|
+
console.error("Locator must have a valid text in the first part to be a context locator");
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const secondPart = parts[1];
|
|
252
|
+
if (secondPart.name !== "xpath") {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const xpath = secondPart.body;
|
|
256
|
+
if (!xpath || typeof xpath !== "string" || xpath.length === 0) {
|
|
257
|
+
// console.error("Locator must have a valid xpath in the second part to be a context locator");
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const climbString = secondPart.body;
|
|
261
|
+
if (!climbString || typeof climbString !== "string" || climbString.length === 0) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const climbStringRegex = /(\.\.)(\/\.\.)*/;
|
|
265
|
+
try {
|
|
266
|
+
const match = climbStringRegex.test(climbString);
|
|
267
|
+
if (match) {
|
|
268
|
+
const climbCount = climbString.split("..").length - 1;
|
|
269
|
+
const lastIndex = selector.indexOf(climbString);
|
|
270
|
+
const restOfSelector = selector.substring(lastIndex + climbString.length + 3).trim();
|
|
271
|
+
if (restOfSelector.length === 0) {
|
|
272
|
+
// console.warn("Locator must have a valid rest of selector after the xpath part to
|
|
273
|
+
// be a context locator");
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const textLocator = `internal:text=${textBody}`;
|
|
278
|
+
const elements = this.getMatchingElements(textLocator, {});
|
|
279
|
+
if (elements.length !== 1) {
|
|
280
|
+
// throw new Error("Context locator must have exactly one element matching the text part");
|
|
281
|
+
console.error("Context locator must have exactly one element matching the text part");
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const textElement = elements[0];
|
|
285
|
+
// const text = this.PW.selectorUtils.elementText(textElement);
|
|
286
|
+
const text = this.injectedScript.utils.elementText(new Map(), textElement).full.trim();
|
|
287
|
+
|
|
288
|
+
const fullSelector = `${textLocator} >> xpath=${xpath} >> ${restOfSelector}`;
|
|
289
|
+
const fullElements = this.getMatchingElements(fullSelector, {});
|
|
290
|
+
if (fullElements.length !== 1) {
|
|
291
|
+
// throw new Error("Context locator must have exactly one element matching the full selector");
|
|
292
|
+
console.error("Context locator must have exactly one element matching the full selector");
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const fullElement = fullElements[0];
|
|
296
|
+
if (fullElement !== element) {
|
|
297
|
+
// throw new Error("Context locator must have the text element as the full element");
|
|
298
|
+
console.error("Context locator must have the text element as the full element");
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (!textSet.has(text)) {
|
|
302
|
+
textSet.add(text);
|
|
303
|
+
const loc = {
|
|
304
|
+
css: restOfSelector,
|
|
305
|
+
climb: climbCount,
|
|
306
|
+
text,
|
|
307
|
+
priority: 1,
|
|
308
|
+
};
|
|
309
|
+
if (locator.index !== undefined) {
|
|
310
|
+
loc.index = locator.index;
|
|
311
|
+
}
|
|
312
|
+
result.push(loc);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error("Error parsing climb string:", error);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error("Error generating context locators:", error);
|
|
322
|
+
}
|
|
323
|
+
// Sort by text length to prioritize shorter texts
|
|
324
|
+
result.sort((a, b) => a.text.length - b.text.length);
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
getDigitIgnoreLocators(element, locators) {
|
|
328
|
+
const result = [];
|
|
329
|
+
try {
|
|
330
|
+
if (!locators || !Array.isArray(locators)) {
|
|
331
|
+
console.error("Locators must be an array");
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
for (const locator of locators) {
|
|
336
|
+
const selector = locator.css;
|
|
337
|
+
if (!selector || typeof selector !== "string") {
|
|
338
|
+
console.error("Locator must have a valid css selector");
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const parseresult = this.injectedScript.parseSelector(selector);
|
|
342
|
+
const parts = parseresult.parts;
|
|
343
|
+
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
344
|
+
console.error("Locator must have a valid css selector");
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
let finalSelector = "";
|
|
348
|
+
let hasDigitsInText = false;
|
|
349
|
+
for (const part of parts) {
|
|
350
|
+
if (part.name !== "internal:text") {
|
|
351
|
+
finalSelector += `${part.name === "css" ? "" : part.name + "="}${part.source} >> `;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (typeof part.body !== "string" || part.body.length === 0) {
|
|
355
|
+
// console.error("Locator must have a valid text in the first part to be a digit ignore locator");
|
|
356
|
+
finalSelector += `${part.name === "css" ? "" : part.name + "="}${part.source} >> `;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const text = part.body;
|
|
360
|
+
const digitsRegex = /\d+/g;
|
|
361
|
+
hasDigitsInText = digitsRegex.test(text);
|
|
362
|
+
|
|
363
|
+
let pattern = this.PW.stringUtils.escapeRegExp(text.substring(1, text.length - 2));
|
|
364
|
+
const re = new RegExp(pattern);
|
|
365
|
+
|
|
366
|
+
finalSelector += `internal:text=${escapeRegexForSelector(re).replace(digitsRegex, "\\d+")} >> `;
|
|
367
|
+
}
|
|
368
|
+
if (!hasDigitsInText) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (finalSelector.endsWith(` >> `)) {
|
|
372
|
+
finalSelector = finalSelector.slice(0, -4);
|
|
373
|
+
}
|
|
374
|
+
if (finalSelector) {
|
|
375
|
+
const elements = this.getMatchingElements(finalSelector, {});
|
|
376
|
+
if (elements.length !== 1) {
|
|
377
|
+
console.error("Digit ignore locator must have exactly one element matching the final selector");
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (elements[0] !== element) {
|
|
381
|
+
console.error("Digit ignore locator must match the original element");
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
result.push({
|
|
385
|
+
css: finalSelector,
|
|
386
|
+
priority: locator.priority || 1,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error("Error generating digit ignore locators:", error);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
getTextwithIndexLocators(locators) {
|
|
397
|
+
if (!locators || !Array.isArray(locators)) {
|
|
398
|
+
console.error("Locators must be an array");
|
|
399
|
+
return [];
|
|
400
|
+
}
|
|
401
|
+
const result = [];
|
|
402
|
+
try {
|
|
403
|
+
for (const locator of locators) {
|
|
404
|
+
if (!locator || !locator.css || typeof locator.css !== "string") {
|
|
405
|
+
console.error("Locator must have a valid css selector");
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const index = locator.index;
|
|
409
|
+
if (typeof index !== "number" || index < 0) {
|
|
410
|
+
// console.error("Locator must have a valid index");
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
result.push(locator);
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error("Error getting text with index locators:", error);
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
getXPathSelector(climb) {
|
|
422
|
+
if (typeof climb !== "number" || climb < 0) {
|
|
423
|
+
// throw new Error("Climb must be a non-negative integer");
|
|
424
|
+
console.error("Climb must be a non-negative integer");
|
|
425
|
+
return "";
|
|
426
|
+
}
|
|
427
|
+
if (climb === 0) return "";
|
|
428
|
+
let selector = "xpath=..";
|
|
429
|
+
if (climb === 1) {
|
|
430
|
+
return selector;
|
|
431
|
+
}
|
|
432
|
+
for (let i = 1; i < climb; i++) {
|
|
433
|
+
selector += "/..";
|
|
434
|
+
}
|
|
435
|
+
return selector;
|
|
436
|
+
}
|
|
437
|
+
categorizeLocators(element, locators, options) {
|
|
438
|
+
const unique = [];
|
|
439
|
+
const nonUnique = [];
|
|
440
|
+
try {
|
|
441
|
+
for (const locator of locators) {
|
|
442
|
+
const elements = this.getMatchingElements(locator.css, options);
|
|
443
|
+
if (elements.length === 0) {
|
|
444
|
+
console.warn(`No elements found for locator: ${locator.css}`);
|
|
445
|
+
continue;
|
|
446
|
+
} else if (elements.length === 1) {
|
|
447
|
+
if (element === elements[0]) {
|
|
448
|
+
locator.priority = 1;
|
|
449
|
+
unique.push(locator);
|
|
450
|
+
} else if (element.contains(elements[0])) {
|
|
451
|
+
locator.priority = 1;
|
|
452
|
+
const climb = this.dom_Parent.getClimbCountToParent(elements[0], element);
|
|
453
|
+
const climbSelector = this.getXPathSelector(climb);
|
|
454
|
+
const newSelector = `${locator.css} >> ${climbSelector}`;
|
|
455
|
+
locator.css = newSelector;
|
|
456
|
+
unique.push(locator);
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
locator.priority = 2;
|
|
460
|
+
locator.elements = elements;
|
|
461
|
+
nonUnique.push(locator);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.error("Error categorizing locators:", error);
|
|
466
|
+
}
|
|
467
|
+
return { unique, nonUnique };
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
getUniqueLocators(element, locatorGenerator = this.getNoTextLocators, options = {}) {
|
|
471
|
+
return this.getUniqueLocators2(element, locatorGenerator, options);
|
|
472
|
+
}
|
|
473
|
+
getUniqueLocators1(element, locatorGenerator = this.getNoTextLocators, options = {}) {
|
|
474
|
+
try {
|
|
475
|
+
const { maxLocators = 5, root = window.document.body, next = "LCA", minLocators = 3 } = options;
|
|
476
|
+
|
|
477
|
+
if (!element) {
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
if (element === root) {
|
|
481
|
+
if (element === window.document.documentElement) {
|
|
482
|
+
return [
|
|
483
|
+
{
|
|
484
|
+
css: "html",
|
|
485
|
+
score: 1,
|
|
486
|
+
priority: 1,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
css: ":root",
|
|
490
|
+
score: 1,
|
|
491
|
+
priority: 1,
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
} else {
|
|
495
|
+
return [
|
|
496
|
+
{
|
|
497
|
+
css: ":root",
|
|
498
|
+
score: 1,
|
|
499
|
+
priority: 1,
|
|
500
|
+
// }, {
|
|
501
|
+
// css: ":root",
|
|
502
|
+
// score: 1,
|
|
503
|
+
// priority: 1,
|
|
504
|
+
},
|
|
505
|
+
];
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
console.log("Generating locators for element:", element);
|
|
510
|
+
const locators = locatorGenerator(element, options);
|
|
511
|
+
console.log("Generated locators:", locators);
|
|
512
|
+
if (!locators || !Array.isArray(locators)) {
|
|
513
|
+
// throw new Error("Locator generator did not return an array of locators");
|
|
514
|
+
console.error("Locator generator did not return an array of locators");
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
console.log("Categorizing locators for element:", element);
|
|
519
|
+
const categorizedLocators = this.categorizeLocators(element, locators, options);
|
|
520
|
+
console.log("Categorized locators:", categorizedLocators);
|
|
521
|
+
// categorizedLocators.unique = limitLocators(categorizedLocators.unique, options);
|
|
522
|
+
// categorizedLocators.nonUnique = limitLocators(categorizedLocators.nonUnique, options);
|
|
523
|
+
|
|
524
|
+
const { unique, nonUnique } = categorizedLocators;
|
|
525
|
+
const result = [];
|
|
526
|
+
if (unique.length > 0) {
|
|
527
|
+
result.push(...unique);
|
|
528
|
+
}
|
|
529
|
+
if (result.length >= maxLocators) {
|
|
530
|
+
return result.slice(0, maxLocators);
|
|
531
|
+
}
|
|
532
|
+
let nextElement = null;
|
|
533
|
+
for (const locator of nonUnique) {
|
|
534
|
+
const selector = locator.css ?? locator.selector;
|
|
535
|
+
const elements = locator.elements || this.getMatchingElements(selector, options);
|
|
536
|
+
|
|
537
|
+
if (next === "parent") {
|
|
538
|
+
nextElement = this.dom_Parent.getActualParent(element);
|
|
539
|
+
} else {
|
|
540
|
+
// find the branching element the child of common parent that contains the element
|
|
541
|
+
const branchingParent = this.dom_Parent.findBranchingParent(elements, element);
|
|
542
|
+
nextElement = branchingParent;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (nextElement && nextElement !== element) {
|
|
546
|
+
if (root.contains(nextElement)) {
|
|
547
|
+
const _result = this.getUniqueLocators(nextElement, locatorGenerator, {
|
|
548
|
+
...options,
|
|
549
|
+
root,
|
|
550
|
+
});
|
|
551
|
+
for (const _locator of _result) {
|
|
552
|
+
if (result.length >= maxLocators) {
|
|
553
|
+
return result.slice(0, maxLocators);
|
|
554
|
+
}
|
|
555
|
+
const _selector = _locator.css ?? _locator.selector;
|
|
556
|
+
const fullSelector = `${_selector} >> ${selector}`;
|
|
557
|
+
const _elements = this.getMatchingElements(fullSelector, options);
|
|
558
|
+
const effectiveScore = (_locator.score + locator.score) / 2 + 100;
|
|
559
|
+
if (_elements.length === 1 && _elements[0] === element) {
|
|
560
|
+
_locator.css = fullSelector;
|
|
561
|
+
_locator.score = effectiveScore;
|
|
562
|
+
_locator.priority = 1; // unique locators have higher priority
|
|
563
|
+
result.push(_locator);
|
|
564
|
+
} else {
|
|
565
|
+
const index = _elements.indexOf(element);
|
|
566
|
+
if (index !== -1 && index < 5) {
|
|
567
|
+
// _locator.selector = fullSelector;
|
|
568
|
+
_locator.css = fullSelector;
|
|
569
|
+
_locator.index = index;
|
|
570
|
+
_locator.priority = 2; // non-unique locators have lower priority
|
|
571
|
+
_locator.score = effectiveScore;
|
|
572
|
+
result.push(_locator);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
const index = elements.indexOf(element);
|
|
578
|
+
if (index !== -1 && index < 5) {
|
|
579
|
+
locator.index = index;
|
|
580
|
+
locator.priority = 2; // non-unique locators have lower priority
|
|
581
|
+
result.push(locator);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
const index = elements.indexOf(element);
|
|
586
|
+
if (index !== -1 && index < 5) {
|
|
587
|
+
locator.index = index;
|
|
588
|
+
locator.priority = 2; // non-unique locators have lower priority
|
|
589
|
+
result.push(locator);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
delete locator.elements; // remove elements to avoid memory leak
|
|
594
|
+
delete locator.strategy;
|
|
595
|
+
delete locator.engine;
|
|
596
|
+
delete locator.selector;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (result.length < minLocators && root && root.contains(element)) {
|
|
600
|
+
const parent = this.dom_Parent.getActualParent(element);
|
|
601
|
+
const locs = this.getUniqueLocators(parent, locatorGenerator, {
|
|
602
|
+
...options,
|
|
603
|
+
root,
|
|
604
|
+
});
|
|
605
|
+
result.push(...locs);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
result.sort((a, b) => a.score - b.score);
|
|
609
|
+
console.log("Final locators:", result, element);
|
|
610
|
+
console.groupEnd();
|
|
611
|
+
return result.slice(0, maxLocators);
|
|
612
|
+
} catch (error) {
|
|
613
|
+
console.error("Error in getUniqueLocators:", error);
|
|
614
|
+
return [];
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
getUniqueLocators2(element, locatorGenerator = this.getNoTextLocators, options = {}) {
|
|
619
|
+
try {
|
|
620
|
+
const { maxLocators = 5, root = window.document.body } = options;
|
|
621
|
+
|
|
622
|
+
if (!element) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
if (element === root) {
|
|
626
|
+
if (element === window.document.documentElement) {
|
|
627
|
+
return [
|
|
628
|
+
{
|
|
629
|
+
css: "html",
|
|
630
|
+
score: 1,
|
|
631
|
+
priority: 1,
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
css: ":root",
|
|
635
|
+
score: 1,
|
|
636
|
+
priority: 1,
|
|
637
|
+
},
|
|
638
|
+
];
|
|
639
|
+
} else {
|
|
640
|
+
return [
|
|
641
|
+
{
|
|
642
|
+
css: ":root",
|
|
643
|
+
score: 1,
|
|
644
|
+
priority: 1,
|
|
645
|
+
},
|
|
646
|
+
];
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
console.log("Generating locators for element:", element);
|
|
651
|
+
const locators = locatorGenerator(element, options);
|
|
652
|
+
console.log("Generated locators:", locators);
|
|
653
|
+
if (!locators || !Array.isArray(locators)) {
|
|
654
|
+
console.error("Locator generator did not return an array of locators");
|
|
655
|
+
return [];
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
console.log("Categorizing locators for element:", element);
|
|
659
|
+
const categorizedLocators = this.categorizeLocators(element, locators, options);
|
|
660
|
+
console.log("Categorized locators:", categorizedLocators);
|
|
661
|
+
|
|
662
|
+
const { unique, nonUnique } = categorizedLocators;
|
|
663
|
+
const result = [];
|
|
664
|
+
if (unique.length > 0) {
|
|
665
|
+
result.push(...unique);
|
|
666
|
+
}
|
|
667
|
+
if (result.length >= maxLocators) {
|
|
668
|
+
return result.slice(0, maxLocators);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const elementsCache = new Map();
|
|
672
|
+
|
|
673
|
+
const allAncestors = this.dom_Parent.getFullAncestorChainToRoot(element, root);
|
|
674
|
+
allAncestors.shift(); // remove the element itself from the ancestors
|
|
675
|
+
|
|
676
|
+
const ancestorLocators = [];
|
|
677
|
+
for (const ancestor of allAncestors) {
|
|
678
|
+
const _locators = locatorGenerator(ancestor, options);
|
|
679
|
+
if (!_locators || !Array.isArray(_locators) || _locators.length === 0) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const _categorized = this.categorizeLocators(ancestor, _locators, options);
|
|
683
|
+
ancestorLocators.push({
|
|
684
|
+
element: ancestor,
|
|
685
|
+
locators: _categorized,
|
|
686
|
+
});
|
|
687
|
+
elementsCache.set(ancestor, _categorized);
|
|
688
|
+
if (_categorized.unique.length > 0) {
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const uniqueAncestor = ancestorLocators[ancestorLocators.length - 1];
|
|
694
|
+
|
|
695
|
+
for (const locator of nonUnique) {
|
|
696
|
+
const selector = locator.css ?? locator.selector;
|
|
697
|
+
const elements = locator.elements || this.getMatchingElements(selector, options);
|
|
698
|
+
if (elements.length === 0) {
|
|
699
|
+
console.warn(`No elements found for locator: ${selector}`);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
for (const unique_locator of uniqueAncestor.locators.unique) {
|
|
704
|
+
const fullSelector = `${unique_locator.css} >> ${selector}`;
|
|
705
|
+
const elements = this.getMatchingElements(fullSelector, options);
|
|
706
|
+
if (elements.length === 1 && elements[0] === element) {
|
|
707
|
+
const effectiveScore = (unique_locator.score + locator.score) / 2 + 100;
|
|
708
|
+
const newLocator = {
|
|
709
|
+
...unique_locator,
|
|
710
|
+
css: fullSelector,
|
|
711
|
+
score: effectiveScore,
|
|
712
|
+
priority: 1, // unique locators have higher priority
|
|
713
|
+
};
|
|
714
|
+
result.push(newLocator);
|
|
715
|
+
} else {
|
|
716
|
+
const index = elements.indexOf(element);
|
|
717
|
+
if (index !== -1 && index < 5) {
|
|
718
|
+
const effectiveScore = (unique_locator.score + locator.score) / 2;
|
|
719
|
+
const newLocator = {
|
|
720
|
+
...unique_locator,
|
|
721
|
+
css: fullSelector,
|
|
722
|
+
index,
|
|
723
|
+
score: effectiveScore + 200,
|
|
724
|
+
priority: 2, // non-unique locators have lower priority
|
|
725
|
+
};
|
|
726
|
+
result.push(newLocator);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
result.sort((a, b) => a.score - b.score);
|
|
732
|
+
console.log("Final locators:", result, element);
|
|
733
|
+
console.groupEnd();
|
|
734
|
+
return result.slice(0, maxLocators);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.error("Error in getUniqueLocators:", error);
|
|
737
|
+
return [];
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
getElementLocators(element, options = {}) {
|
|
741
|
+
try {
|
|
742
|
+
const { excludeText = false } = options;
|
|
743
|
+
|
|
744
|
+
const allStrategyLocators = {
|
|
745
|
+
[this.locatorStrategies.custom]: [],
|
|
746
|
+
[this.locatorStrategies.context]: [],
|
|
747
|
+
[this.locatorStrategies.text]: [],
|
|
748
|
+
[this.locatorStrategies.text_with_index]: [],
|
|
749
|
+
[this.locatorStrategies.digitIgnore]: [],
|
|
750
|
+
[this.locatorStrategies.no_text]: [],
|
|
751
|
+
};
|
|
752
|
+
if (this.options?.customAttributes) {
|
|
753
|
+
console.groupCollapsed("Generating Custom locators for element:", element);
|
|
754
|
+
const customLocators = this.getUniqueLocators(element, this.getCustomLocators.bind(this), options);
|
|
755
|
+
if (customLocators.length > 0) {
|
|
756
|
+
allStrategyLocators[this.locatorStrategies.custom] = customLocators;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
console.groupEnd();
|
|
760
|
+
if (!excludeText) {
|
|
761
|
+
console.groupCollapsed("Generating Text locators for element:", element);
|
|
762
|
+
const basicLocators = this.getUniqueLocators(element, this.getTextLocators.bind(this), options);
|
|
763
|
+
console.groupEnd();
|
|
764
|
+
if (basicLocators.length > 0) {
|
|
765
|
+
allStrategyLocators[this.locatorStrategies.text] = basicLocators;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const textWithIndexLocators = this.getTextwithIndexLocators(basicLocators);
|
|
769
|
+
if (textWithIndexLocators.length > 0) {
|
|
770
|
+
allStrategyLocators[this.locatorStrategies.text_with_index] = textWithIndexLocators;
|
|
771
|
+
}
|
|
772
|
+
const digitIgnoreLocators = this.getDigitIgnoreLocators(element, basicLocators);
|
|
773
|
+
if (digitIgnoreLocators.length > 0) {
|
|
774
|
+
allStrategyLocators[this.locatorStrategies.digitIgnore] = digitIgnoreLocators;
|
|
775
|
+
}
|
|
776
|
+
const contextLocators = this.getContextLocators(element, basicLocators);
|
|
777
|
+
if (contextLocators.length > 0) {
|
|
778
|
+
allStrategyLocators[this.locatorStrategies.context] = contextLocators;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
console.groupCollapsed("Generating No Text locators for element:", element);
|
|
782
|
+
const noTextLocators = this.getUniqueLocators(element, this.getNoTextLocators.bind(this), options);
|
|
783
|
+
|
|
784
|
+
if (noTextLocators.length > 0) {
|
|
785
|
+
allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
|
|
786
|
+
} else {
|
|
787
|
+
const _locators = [];
|
|
788
|
+
_locators.push({
|
|
789
|
+
css: this.generateUniqueCSSSelector(element, options),
|
|
790
|
+
score: 500,
|
|
791
|
+
priority: 3,
|
|
792
|
+
});
|
|
793
|
+
if (_locators.length > 0) {
|
|
794
|
+
allStrategyLocators[this.locatorStrategies.no_text] = _locators;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
console.groupEnd();
|
|
798
|
+
|
|
799
|
+
let bestStrategy = this.getBestStrategy(allStrategyLocators);
|
|
800
|
+
if (!bestStrategy) {
|
|
801
|
+
throw new Error("No locators found for element: " + element);
|
|
802
|
+
}
|
|
803
|
+
allStrategyLocators.strategy = bestStrategy;
|
|
804
|
+
|
|
805
|
+
const locators = allStrategyLocators[allStrategyLocators.strategy] ?? [];
|
|
806
|
+
const result = {
|
|
807
|
+
locators,
|
|
808
|
+
element_name: "",
|
|
809
|
+
};
|
|
810
|
+
if (Object.keys(allStrategyLocators).length > 1) {
|
|
811
|
+
result.allStrategyLocators = allStrategyLocators;
|
|
812
|
+
}
|
|
813
|
+
console.log("Generated locators:", result);
|
|
814
|
+
return result;
|
|
815
|
+
} catch (error) {
|
|
816
|
+
console.error("Error in getElementLocators:", error);
|
|
817
|
+
return {
|
|
818
|
+
locators: [],
|
|
819
|
+
element_name: "",
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
getBestStrategy(allStrategyLocators) {
|
|
824
|
+
const orderedPriorities = [
|
|
825
|
+
this.locatorStrategies.custom,
|
|
826
|
+
this.locatorStrategies.context,
|
|
827
|
+
this.locatorStrategies.text,
|
|
828
|
+
this.locatorStrategies.text_with_index,
|
|
829
|
+
this.locatorStrategies.digitIgnore,
|
|
830
|
+
this.locatorStrategies.no_text,
|
|
831
|
+
];
|
|
832
|
+
for (const strategy of orderedPriorities) {
|
|
833
|
+
if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
|
|
834
|
+
return strategy;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
832
839
|
}
|
|
833
840
|
|
|
834
841
|
export default LocatorGenerator;
|