ff-dom 1.0.17 → 1.0.18-beta.2

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,936 @@
1
+ import { parseDOM } from "./xpath.ts";
2
+ import {
3
+ findXpathWithIndex,
4
+ findRoot,
5
+ getShadowRoot,
6
+ getTextContent,
7
+ escapeCharacters,
8
+ getCountOfXPath,
9
+ checkBlockedAttributes,
10
+ isNumberExist,
11
+ getAbsoluteXPath,
12
+ isSvg,
13
+ getCombinationXpath,
14
+ getTextXpathFunction,
15
+ replaceTempAttributes,
16
+ replaceActualAttributes,
17
+ getReferenceElementsXpath,
18
+ getRelativeXPath,
19
+ findMatchingParenthesis,
20
+ removeParenthesis,
21
+ modifiedElementAttributes
22
+ } from "./xpathHelpers.ts";
23
+
24
+ let xpathDataWithIndex: any[] = [];
25
+ let xpathData: { key: string; value: string }[] = [];
26
+ const reWhiteSpace = /^[\S]+( [\S]+)*$/gi;
27
+ let multiElementReferenceMode: boolean = false;
28
+
29
+ export const findRelativeXpath = (
30
+ element1: HTMLElement | Element,
31
+ element2: HTMLElement | Element,
32
+ docmt: Document,
33
+ xpaths1: any[],
34
+ xpaths2: any[],
35
+ isIndex: any
36
+ ) => {
37
+ // debugger;
38
+ const par1 = element1.parentElement;
39
+ const par2 = element2.parentElement;
40
+ let rel_xpath: any[] = [];
41
+
42
+ let tempElement;
43
+ const root = findRoot(element1);
44
+ let finalXpaths: any[] = [];
45
+
46
+ if (isIndex) {
47
+ if (xpathDataWithIndex.length) {
48
+ const xpathWithIndex = findXpathWithIndex(
49
+ xpathDataWithIndex[0].value,
50
+ element2,
51
+ element2.ownerDocument,
52
+ xpathDataWithIndex[0].count
53
+ );
54
+ if (xpathWithIndex) {
55
+ finalXpaths = finalXpaths.concat({
56
+ key: `${xpathDataWithIndex[0]?.key
57
+ ? xpathDataWithIndex[0]?.key
58
+ : "xpath with "
59
+ } index`,
60
+ value: xpathWithIndex,
61
+ });
62
+ xpathDataWithIndex.pop();
63
+ }
64
+ }
65
+ }
66
+
67
+ if (!finalXpaths.length) {
68
+ // both are same
69
+ if (element1.isSameNode(element2)) {
70
+ // rel_xpath = xpath1 + "/self::" + element1.tagName;
71
+ rel_xpath = getXpathRelationExpression(
72
+ element1,
73
+ element2,
74
+ "self",
75
+ xpaths1,
76
+ xpaths2,
77
+ isIndex
78
+ );
79
+ if (rel_xpath) finalXpaths = finalXpaths.concat(rel_xpath);
80
+ }
81
+
82
+ // parent
83
+ tempElement = element1.parentElement;
84
+
85
+ if (tempElement === element2) {
86
+ rel_xpath = getXpathRelationExpression(
87
+ element1,
88
+ element2,
89
+ "parent",
90
+ xpaths1,
91
+ xpaths2,
92
+ isIndex
93
+ );
94
+ if (rel_xpath) finalXpaths = finalXpaths.concat(rel_xpath);
95
+ }
96
+
97
+ // ancestor
98
+ tempElement = element1.parentElement;
99
+
100
+ while (tempElement !== null) {
101
+ if (tempElement === element2) {
102
+ rel_xpath = getXpathRelationExpression(
103
+ element1,
104
+ element2,
105
+ "ancestor",
106
+ xpaths1,
107
+ xpaths2,
108
+ isIndex
109
+ );
110
+ if (rel_xpath) {
111
+ finalXpaths = finalXpaths.concat(rel_xpath);
112
+ break;
113
+ }
114
+ }
115
+
116
+ tempElement = tempElement.parentNode;
117
+ }
118
+
119
+ // ancestor-or-self
120
+ tempElement = element1;
121
+ do {
122
+ if (tempElement === element2) {
123
+ rel_xpath = getXpathRelationExpression(
124
+ element1,
125
+ element2,
126
+ "ancestor-or-self",
127
+ xpaths1,
128
+ xpaths2,
129
+ isIndex
130
+ );
131
+ if (rel_xpath) {
132
+ finalXpaths = finalXpaths.concat(rel_xpath);
133
+ break;
134
+ }
135
+ }
136
+
137
+ tempElement = tempElement.parentNode;
138
+ } while (tempElement !== null);
139
+
140
+ // both has same parent
141
+ if (par1?.isSameNode(par2)) {
142
+ for (
143
+ let m = element1.nextElementSibling;
144
+ m != null;
145
+ m = m.nextElementSibling
146
+ ) {
147
+ if (m != null && m.isSameNode(element2)) {
148
+ rel_xpath = getXpathRelationExpression(
149
+ element1,
150
+ element2,
151
+ "following-sibling",
152
+ xpaths1,
153
+ xpaths2,
154
+ isIndex
155
+ );
156
+ if (rel_xpath) {
157
+ finalXpaths = finalXpaths.concat(rel_xpath);
158
+ break;
159
+ }
160
+ }
161
+ }
162
+
163
+ for (
164
+ let n = element1.previousElementSibling;
165
+ n != null;
166
+ n = n.previousElementSibling
167
+ ) {
168
+ if (n != null && n.isSameNode(element2)) {
169
+ rel_xpath = getXpathRelationExpression(
170
+ element1,
171
+ element2,
172
+ "preceding-sibling",
173
+ xpaths1,
174
+ xpaths2,
175
+ isIndex
176
+ );
177
+ if (rel_xpath) {
178
+ finalXpaths = finalXpaths.concat(rel_xpath);
179
+ break;
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ // child
186
+ if (element1?.children?.length) {
187
+ for (let m = element1.children[0]; m !== null; m = m?.nextElementSibling!) {
188
+ if (m === element2) {
189
+ rel_xpath = getXpathRelationExpression(
190
+ element1,
191
+ element2,
192
+ "child",
193
+ xpaths1,
194
+ xpaths2,
195
+ isIndex
196
+ );
197
+ if (rel_xpath) {
198
+ finalXpaths = finalXpaths.concat(rel_xpath);
199
+ break;
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ // following
206
+ const relation = element1.compareDocumentPosition(element2);
207
+ if (relation === 2) {
208
+ rel_xpath = getXpathRelationExpression(
209
+ element1,
210
+ element2,
211
+ "preceding",
212
+ xpaths1,
213
+ xpaths2,
214
+ isIndex
215
+ );
216
+ }
217
+
218
+ if (relation === 4) {
219
+ rel_xpath = getXpathRelationExpression(
220
+ element1,
221
+ element2,
222
+ "following",
223
+ xpaths1,
224
+ xpaths2,
225
+ isIndex
226
+ );
227
+ }
228
+ if (rel_xpath) {
229
+ finalXpaths = finalXpaths.concat(rel_xpath);
230
+ }
231
+
232
+ const descendantXpath = getDescendantXpath(
233
+ [element1, element2],
234
+ docmt,
235
+ xpaths1,
236
+ xpaths2,
237
+ "descendant",
238
+ isIndex
239
+ );
240
+ if (descendantXpath) finalXpaths = finalXpaths.concat(descendantXpath);
241
+
242
+ const descendantSelfXpath = getDescendantXpath(
243
+ [element1, element2],
244
+ docmt,
245
+ xpaths1,
246
+ xpaths2,
247
+ "descedant-or-self",
248
+ isIndex
249
+ );
250
+ if (descendantSelfXpath) {
251
+ finalXpaths = finalXpaths.concat(descendantSelfXpath);
252
+ }
253
+ }
254
+
255
+ if (finalXpaths.length) {
256
+ if (finalXpaths.length > 1) {
257
+ finalXpaths.sort(function (a, b) {
258
+ return a.value.length - b.value.length;
259
+ });
260
+ }
261
+
262
+ if (finalXpaths.filter((x) => !x.key?.includes("index"))?.length) {
263
+ return [finalXpaths.filter((x) => !x.key?.includes("index"))[0]];
264
+ }
265
+ return [finalXpaths[0]];
266
+ }
267
+ };
268
+
269
+ const descendantExpression = (
270
+ refExpectElement: Array<HTMLElement | Element>,
271
+ xpath2: string,
272
+ relation: string,
273
+ docmt: Node,
274
+ isIndex: any,
275
+ expCommonParentXpathElements: string | any[],
276
+ step4: string,
277
+ refCommonParentXpathElementLength: number
278
+ ) => {
279
+ let finalExpectedElementXpath: string | undefined = "";
280
+
281
+ if (
282
+ xpath2.split(/\/(?=(?:[^']*\'[^\']*\')*[^\']*$)/g)?.length >=
283
+ expCommonParentXpathElements.length
284
+ ) {
285
+ const xpaths2Els = [];
286
+ const xpath2Elements = xpath2.split(/\/(?=(?:[^']*\'[^\']*\')*[^\']*$)/g);
287
+ for (let x = 1; x <= expCommonParentXpathElements.length; x++) {
288
+ xpaths2Els.unshift(
289
+ x === expCommonParentXpathElements.length
290
+ ? expCommonParentXpathElements[
291
+ expCommonParentXpathElements.length - x
292
+ ]
293
+ : xpath2Elements[xpath2Elements.length - x]
294
+ );
295
+ }
296
+ const traverseXpath = getTraverseXpathExpression(
297
+ `${step4 +
298
+ (refCommonParentXpathElementLength
299
+ ? "]".repeat(refCommonParentXpathElementLength)
300
+ : "")
301
+ }`,
302
+ xpaths2Els,
303
+ refExpectElement[refExpectElement.length - 1],
304
+ refExpectElement,
305
+ docmt,
306
+ relation,
307
+ isIndex
308
+ );
309
+ if (traverseXpath) {
310
+ return traverseXpath;
311
+ }
312
+ } else {
313
+ finalExpectedElementXpath = `//${step4 +
314
+ (refCommonParentXpathElementLength
315
+ ? "]".repeat(refCommonParentXpathElementLength)
316
+ : "")
317
+ }/${relation}::${replaceActualAttributes(
318
+ xpath2,
319
+ refExpectElement[refExpectElement.length - 1]
320
+ )}`;
321
+ let rel_count = getCountOfXPath(
322
+ finalExpectedElementXpath,
323
+ refExpectElement[refExpectElement.length - 1],
324
+ docmt
325
+ );
326
+ if (rel_count === 1) {
327
+ return [
328
+ {
329
+ key: `dynamic ${relation}`,
330
+ value: replaceTempAttributes(finalExpectedElementXpath),
331
+ },
332
+ ];
333
+ }
334
+ if (rel_count > 1) {
335
+ if (isIndex) {
336
+ finalExpectedElementXpath = findXpathWithIndex(
337
+ finalExpectedElementXpath,
338
+ refExpectElement[refExpectElement.length - 1],
339
+ docmt,
340
+ rel_count
341
+ );
342
+ if (finalExpectedElementXpath) {
343
+ rel_count = getCountOfXPath(
344
+ finalExpectedElementXpath,
345
+ refExpectElement[refExpectElement.length - 1],
346
+ docmt
347
+ );
348
+ if (rel_count === 1) {
349
+ return [
350
+ {
351
+ key: `dynamic ${relation} ${isIndex ? " index" : ""}`,
352
+ value: replaceTempAttributes(finalExpectedElementXpath),
353
+ },
354
+ ];
355
+ }
356
+ }
357
+ }
358
+ }
359
+ }
360
+ };
361
+
362
+ const getDescendantXpath = (
363
+ refExpectElement: Array<HTMLElement | Element>,
364
+ docmt: Document,
365
+ xpaths1: { key: string; value: string }[],
366
+ xpaths2: { key: string; value: string }[],
367
+ relation: "descendant" | "descedant-or-self",
368
+ isIndex: boolean
369
+ ) => {
370
+ const refElement: HTMLElement | Element = refExpectElement[refExpectElement.length - 2];
371
+ const expElement: HTMLElement | Element = refExpectElement[refExpectElement.length - 1];
372
+ const expElementDocmnt = getShadowRoot(expElement) ?? expElement.ownerDocument;
373
+
374
+ const refAbsoluteXpath =
375
+ xpaths1.find((x) => x?.key?.includes("absolute"))?.value ||
376
+ getAbsoluteXPath(refElement, refElement.ownerDocument);
377
+
378
+ const refFullXpathElements: any[] = [];
379
+ const refFullXpathElementsWithoutNumber: any[] = [];
380
+ refAbsoluteXpath.split("/").map((x) => refFullXpathElements.push(x));
381
+ refFullXpathElements.map((x) =>
382
+ refFullXpathElementsWithoutNumber.push(x.replace(/\[([0-9]+)\]/gm, ""))
383
+ );
384
+
385
+ const expAbsoluteXpath =
386
+ xpaths2.find((x) => x?.key?.includes("absolute"))?.value ||
387
+ getAbsoluteXPath(expElement, expElement.ownerDocument);
388
+
389
+ const expFullXpathElements: any[] = [];
390
+ const expFullXpathElementsWithoutNumber: any[] = [];
391
+ expAbsoluteXpath.split("/").map((x) => expFullXpathElements.push(x));
392
+ expFullXpathElements.map((x) =>
393
+ expFullXpathElementsWithoutNumber.push(x.replace(/\[([0-9]+)\]/gm, ""))
394
+ );
395
+
396
+ for (
397
+ var parentElementNumber = 0;
398
+ parentElementNumber < refFullXpathElements.length;
399
+ parentElementNumber++
400
+ ) {
401
+ if (
402
+ refFullXpathElements[parentElementNumber] !=
403
+ expFullXpathElements[parentElementNumber]
404
+ ) {
405
+ break;
406
+ }
407
+ }
408
+
409
+ const refCommonParentXpathElements = [];
410
+ for (let i = parentElementNumber - 1; i < refFullXpathElements.length; i++) {
411
+ if (refFullXpathElements[i]) {
412
+ refCommonParentXpathElements.push(refFullXpathElementsWithoutNumber[i]);
413
+ }
414
+ }
415
+
416
+ const expCommonParentXpathElements = [];
417
+ for (
418
+ let j =
419
+ relation === "descendant" ? parentElementNumber : parentElementNumber - 1;
420
+ j < expFullXpathElements.length;
421
+ j++
422
+ ) {
423
+ if (expFullXpathElements[j]) {
424
+ if (expCommonParentXpathElements.length)
425
+ expCommonParentXpathElements.push(expFullXpathElementsWithoutNumber[j]);
426
+ else
427
+ expCommonParentXpathElements.push(
428
+ expFullXpathElementsWithoutNumber[j].replace(/\[([0-9]+)\]/gm, "")
429
+ );
430
+ }
431
+ }
432
+
433
+ xpathData = xpaths2;
434
+
435
+ let nodeXpath2;
436
+ if (refExpectElement[refExpectElement.length - 2].textContent) {
437
+ if (
438
+ !reWhiteSpace.test(
439
+ refExpectElement[refExpectElement.length - 2].textContent
440
+ )
441
+ ) {
442
+ nodeXpath2 = isSvg(refExpectElement[refExpectElement.length - 2])
443
+ ? `*[local-name()='${refExpectElement[refExpectElement.length - 2].tagName
444
+ }' and ${getTextXpathFunction(
445
+ refExpectElement[refExpectElement.length - 2]
446
+ )})]`
447
+ : `${refExpectElement[refExpectElement.length - 2].tagName
448
+ }[${getTextXpathFunction(
449
+ refExpectElement[refExpectElement.length - 2]
450
+ )}]`;
451
+ } else {
452
+ nodeXpath2 = isSvg(refExpectElement[refExpectElement.length - 2])
453
+ ? `*[local-name()='${refExpectElement[refExpectElement.length - 2].tagName
454
+ }' and .=${escapeCharacters(
455
+ getTextContent(refExpectElement[refExpectElement.length - 2])
456
+ )}]`
457
+ : `${refExpectElement[refExpectElement.length - 2].tagName
458
+ }[.=${escapeCharacters(
459
+ getTextContent(refExpectElement[refExpectElement.length - 2])
460
+ )}]`;
461
+ }
462
+
463
+ refCommonParentXpathElements[refCommonParentXpathElements.length - 1] =
464
+ nodeXpath2;
465
+
466
+ const refCommonParentXpath = refCommonParentXpathElements.join("[");
467
+ const refCommonParentXpathElementLength =
468
+ refCommonParentXpathElements.length - 1;
469
+
470
+ const step4 = refCommonParentXpath;
471
+
472
+ for (let i = 0; i < xpathData.length; i++) {
473
+ let xpath2;
474
+
475
+ if (xpathData[i].value.startsWith("//")) {
476
+ xpath2 = xpathData[i].value.substring(
477
+ xpathData[i].value.indexOf("//") + 2
478
+ );
479
+ } else {
480
+ xpath2 = xpathData[i].value; // No need to modify the value
481
+ }
482
+ if (xpath2) {
483
+ return descendantExpression(
484
+ refExpectElement,
485
+ xpath2,
486
+ relation,
487
+ expElementDocmnt || docmt,
488
+ isIndex,
489
+ expCommonParentXpathElements,
490
+ step4,
491
+ refCommonParentXpathElementLength
492
+ );
493
+ }
494
+ }
495
+ }
496
+
497
+ if (refExpectElement[refExpectElement.length - 2].attributes) {
498
+ for (const attrName of Array.from(refExpectElement[refExpectElement.length - 2]
499
+ .attributes)) {
500
+ if (
501
+ checkBlockedAttributes(attrName, refExpectElement[refExpectElement.length - 2], false)
502
+ ) {
503
+ let attrValue = attrName.nodeValue;
504
+ if (attrValue) {
505
+ attrValue = attrValue.replace("removePointers", "");
506
+ const elementName = attrName.name;
507
+
508
+ nodeXpath2 = isSvg(refExpectElement[refExpectElement.length - 2])
509
+ ? `*[local-name()='${refExpectElement[refExpectElement.length - 2].tagName
510
+ }' and @${elementName}=${escapeCharacters(attrValue)}]`
511
+ : `${refExpectElement[refExpectElement.length - 2].tagName
512
+ }[@${elementName}=${escapeCharacters(attrValue)}]`;
513
+
514
+ refCommonParentXpathElements[
515
+ refCommonParentXpathElements.length - 1
516
+ ] = nodeXpath2;
517
+
518
+ const refCommonParentXpath = refCommonParentXpathElements.join("[");
519
+ const refCommonParentXpathElementLength =
520
+ refCommonParentXpathElements.length - 1;
521
+
522
+ const step4 = refCommonParentXpath;
523
+
524
+ for (let i = 0; i < xpathData.length; i++) {
525
+ let xpath2;
526
+ if (xpathData[i].value.startsWith("//")) {
527
+ xpath2 = xpathData[i].value.substring(
528
+ xpathData[i].value.indexOf("//") + 2
529
+ );
530
+ } else {
531
+ xpath2 = xpathData[i].value; // No need to modify the value
532
+ }
533
+
534
+ return descendantExpression(
535
+ refExpectElement,
536
+ xpath2,
537
+ relation,
538
+ expElementDocmnt || docmt,
539
+ isIndex,
540
+ expCommonParentXpathElements,
541
+ step4,
542
+ refCommonParentXpathElementLength
543
+ );
544
+ }
545
+ }
546
+ }
547
+ }
548
+
549
+ for (const attrName of Array.from(refExpectElement[refExpectElement.length - 2]
550
+ .attributes)) {
551
+ if (
552
+ checkBlockedAttributes(attrName, refExpectElement[refExpectElement.length - 2], false)
553
+ ) {
554
+ let attrValue = attrName.nodeValue;
555
+ if (attrValue) {
556
+ attrValue = attrValue.replace("removePointers", "");
557
+ const combinationXpath = getCombinationXpath(
558
+ attrName,
559
+ refExpectElement[refExpectElement.length - 2]
560
+ );
561
+ if (combinationXpath) {
562
+ if (combinationXpath.startsWith("//")) {
563
+ nodeXpath2 = combinationXpath.substring(
564
+ combinationXpath.indexOf("//") + 2
565
+ );
566
+ } else {
567
+ nodeXpath2 = combinationXpath; // No need to modify the value
568
+ }
569
+
570
+ refCommonParentXpathElements[
571
+ refCommonParentXpathElements.length - 1
572
+ ] = nodeXpath2;
573
+
574
+ const refCommonParentXpath = refCommonParentXpathElements.join("[");
575
+ const refCommonParentXpathElementLength =
576
+ refCommonParentXpathElements.length - 1;
577
+
578
+ const step4 = refCommonParentXpath;
579
+
580
+ for (let i = 0; i < xpathData.length; i++) {
581
+ let xpath2;
582
+ if (xpathData[i].value.startsWith("//")) {
583
+ xpath2 = xpathData[i].value.substring(
584
+ xpathData[i].value.indexOf("//") + 2
585
+ );
586
+ } else {
587
+ xpath2 = xpathData[i].value; // No need to modify the value
588
+ }
589
+
590
+ return descendantExpression(
591
+ refExpectElement,
592
+ xpath2,
593
+ relation,
594
+ expElementDocmnt || docmt,
595
+ isIndex,
596
+ expCommonParentXpathElements,
597
+ step4,
598
+ refCommonParentXpathElementLength
599
+ );
600
+ }
601
+ }
602
+ }
603
+ }
604
+ }
605
+ }
606
+
607
+ const refCommonParentXpath = refCommonParentXpathElements.join("[");
608
+ const refCommonParentXpathElementLength =
609
+ refCommonParentXpathElements.length - 1;
610
+ const step4 = refCommonParentXpath;
611
+ const traverseXpath = getTraverseXpathExpression(
612
+ `${step4 +
613
+ (refCommonParentXpathElementLength
614
+ ? "]".repeat(refCommonParentXpathElementLength)
615
+ : "")
616
+ }`,
617
+ expCommonParentXpathElements.reverse(),
618
+ refExpectElement[refExpectElement.length - 1],
619
+ refExpectElement,
620
+ docmt,
621
+ relation,
622
+ isIndex
623
+ );
624
+ if (traverseXpath) {
625
+ return traverseXpath;
626
+ }
627
+ };
628
+
629
+ const getXpathRelationExpression = (
630
+ element1: HTMLElement | Element,
631
+ element2: HTMLElement | Element,
632
+ relation: string,
633
+ xpath1: any[],
634
+ xpath2: any[],
635
+ isIndex: any
636
+ ) => {
637
+ let xpaths1;
638
+ let xpaths2;
639
+ console.log('getXpathRelationExpression', relation)
640
+ const finalXpaths: { key: string; value: any }[] = [];
641
+
642
+ try {
643
+ xpaths1 = xpath1.filter((x) => !x?.key?.includes("absolute"));
644
+
645
+ xpaths2 = xpath2.filter((x) => !x?.key?.includes("absolute"));
646
+
647
+ for (let i = 0; i < xpaths1.length; i++) {
648
+ for (let j = 0; j < xpaths2.length; j++) {
649
+ let rel_xpath: string | undefined = `//${xpaths1[i].value.indexOf("//") !== 0
650
+ ? replaceActualAttributes(xpaths1[i].value, element1)
651
+ : replaceActualAttributes(
652
+ xpaths1[i].value.substring(xpaths1[i].value.indexOf("//") + 2),
653
+ element1
654
+ )
655
+ }/${relation}::${xpaths2[j].value.indexOf("//") !== 0
656
+ ? replaceActualAttributes(xpaths2[j].value, element2)
657
+ : replaceActualAttributes(
658
+ xpaths2[j].value.substring(xpaths2[j].value.indexOf("//") + 2),
659
+ element2
660
+ )
661
+ }`;
662
+ console.log('getXpathRelationExpression', rel_xpath)
663
+ const rel_count = getCountOfXPath(
664
+ rel_xpath,
665
+ element2,
666
+ element2.ownerDocument
667
+ );
668
+ if (rel_count > 1) {
669
+ if (isIndex) {
670
+ rel_xpath = findXpathWithIndex(
671
+ rel_xpath,
672
+ element2,
673
+ element2.ownerDocument,
674
+ rel_count
675
+ );
676
+ if (rel_xpath) {
677
+ finalXpaths.push({
678
+ key: `dynamic ${relation}${isIndex ? " index" : ""}`,
679
+ value: replaceTempAttributes(rel_xpath),
680
+ });
681
+ return finalXpaths;
682
+ }
683
+ } else if (rel_count > 1) {
684
+ if (xpathDataWithIndex.length) {
685
+ if (rel_count < xpathDataWithIndex[0].count) {
686
+ xpathDataWithIndex.pop();
687
+ xpathDataWithIndex.push({
688
+ key: `relative xpath by relative child ${isIndex ? "index" : ""
689
+ }`,
690
+ value: rel_xpath,
691
+ count: rel_count,
692
+ });
693
+ }
694
+ } else {
695
+ xpathDataWithIndex.push({
696
+ key: `relative xpath by relative child ${isIndex ? "index" : ""
697
+ }`,
698
+ value: rel_xpath,
699
+ count: rel_count,
700
+ });
701
+ }
702
+ }
703
+ } else if (rel_count === 1) {
704
+ finalXpaths.push({
705
+ key: `dynamic ${relation}`,
706
+ value: replaceTempAttributes(rel_xpath),
707
+ });
708
+ return finalXpaths;
709
+ }
710
+ }
711
+ }
712
+ if (!finalXpaths.length) {
713
+ for (let i = 0; i < xpaths1.length; i++) {
714
+ for (let j = 0; j < xpaths2.length; j++) {
715
+ const tempPath = `${xpaths2[j].value.indexOf("//") !== 0
716
+ ? xpaths2[j].value
717
+ : xpaths2[j].value.substring(xpaths2[j].value.indexOf("//") + 2)
718
+ }`;
719
+ const xpath2Elements = tempPath.split(
720
+ /\/(?=(?:[^']*\'[^\']*\')*[^\']*$)/g
721
+ );
722
+ if (xpath2Elements.length > 1) {
723
+ const traverseXpath = getTraverseXpathExpression(
724
+ `${xpaths1[i].value.indexOf("//") !== 0
725
+ ? replaceActualAttributes(xpaths1[i].value, element1)
726
+ : replaceActualAttributes(
727
+ xpaths1[i].value.substring(
728
+ xpaths1[i].value.indexOf("//") + 2
729
+ ),
730
+ element1
731
+ )
732
+ }`,
733
+ xpath2Elements,
734
+ element2,
735
+ [element1, element2],
736
+ element2.ownerDocument,
737
+ relation,
738
+ isIndex
739
+ );
740
+
741
+ console.log('getXpathRelationExpression traverseXpath', traverseXpath)
742
+ if (traverseXpath) {
743
+ finalXpaths.concat(traverseXpath);
744
+ return finalXpaths;
745
+ }
746
+ }
747
+ }
748
+ }
749
+ }
750
+ } catch (error) {
751
+ console.log(error);
752
+ }
753
+
754
+ return finalXpaths;
755
+ };
756
+
757
+ const getReferenceElementXpath = (element: HTMLElement | Element) => {
758
+ let xpaths1: {
759
+ key: string;
760
+ value: string;
761
+ }[] = [];
762
+
763
+ xpaths1 = parseDOM(element, element.ownerDocument, false, false);
764
+
765
+ if (!xpaths1?.length) {
766
+ xpaths1 = parseDOM(element, element.ownerDocument, true, false);
767
+ xpaths1 = xpaths1?.map((x) =>
768
+ x.value.charAt(0) == "(" &&
769
+ findMatchingParenthesis(x.value, 0) + 1 === x.value.lastIndexOf("[")
770
+ ? { key: "", value: removeParenthesis(x.value) }
771
+ : { key: "", value: x.value }
772
+ );
773
+ } else {
774
+ let xpaths = parseDOM(element, element.ownerDocument, true, false);
775
+ if (xpaths?.length) {
776
+ xpaths = xpaths?.map((x) =>
777
+ x.value.charAt(0) == "(" &&
778
+ findMatchingParenthesis(x.value, 0) + 1 === x.value.lastIndexOf("[")
779
+ ? { key: "", value: removeParenthesis(x.value) }
780
+ : { key: "", value: x.value }
781
+ );
782
+ xpaths1 = xpaths1.concat(xpaths);
783
+ }
784
+ }
785
+
786
+ if (!xpaths1?.length) {
787
+ xpaths1 = [
788
+ {
789
+ key: "",
790
+ value: getRelativeXPath(element, element.ownerDocument, false, false, Array.from(element.attributes)),
791
+ },
792
+ ];
793
+ }
794
+
795
+ if (!xpaths1?.length) {
796
+ xpaths1 = [
797
+ {
798
+ key: "",
799
+ value: getRelativeXPath(element, element.ownerDocument, true, false, Array.from(element.attributes)),
800
+ },
801
+ ];
802
+ xpaths1 = xpaths1?.map((x) =>
803
+ x.value.charAt(0) == "(" &&
804
+ findMatchingParenthesis(x.value, 0) + 1 === x.value.lastIndexOf("[")
805
+ ? { key: "", value: removeParenthesis(x.value) }
806
+ : { key: "", value: x.value }
807
+ );
808
+ }
809
+
810
+ if (!xpaths1?.length) {
811
+ xpaths1 = getReferenceElementsXpath(element, element.ownerDocument, false);
812
+ } else {
813
+ const xpaths = getReferenceElementsXpath(
814
+ element,
815
+ element.ownerDocument,
816
+ false
817
+ );
818
+ if (xpaths?.length) {
819
+ xpaths1 = xpaths1.concat(xpaths);
820
+ }
821
+ }
822
+
823
+ const referenceXpathElement = getAbsoluteXPath(
824
+ element,
825
+ element.ownerDocument
826
+ );
827
+ xpaths1 = xpaths1.filter((x) => x.value !== referenceXpathElement);
828
+
829
+ xpaths1.push({
830
+ key: "absolute xpath",
831
+ value: referenceXpathElement.slice(1),
832
+ });
833
+
834
+ return xpaths1;
835
+ };
836
+
837
+ const getTraverseXpathExpression = (
838
+ xpathe1: string,
839
+ absoluteXpathElements: any[],
840
+ element2: HTMLElement | Element,
841
+ refExpectElement: Array<HTMLElement | Element>,
842
+ docmt: Node,
843
+ relation: string,
844
+ isIndex: any
845
+ ) => {
846
+ let finalExpectedElementXpath: string | undefined
847
+ if (multiElementReferenceMode) {
848
+ const xpath2 = absoluteXpathElements.join("/");
849
+ finalExpectedElementXpath = `//${xpathe1}/${relation}::${replaceActualAttributes(
850
+ xpath2,
851
+ element2
852
+ )}`;
853
+ const rel_count = getCountOfXPath(
854
+ finalExpectedElementXpath,
855
+ element2,
856
+ docmt
857
+ );
858
+ if (rel_count === 1) {
859
+ return [
860
+ {
861
+ key: `dynamic ${relation}`,
862
+ value: replaceTempAttributes(finalExpectedElementXpath),
863
+ },
864
+ ];
865
+ }
866
+ if (rel_count > 1) {
867
+ if (isIndex) {
868
+ finalExpectedElementXpath = findXpathWithIndex(
869
+ finalExpectedElementXpath,
870
+ refExpectElement[refExpectElement.length - 1],
871
+ docmt,
872
+ rel_count
873
+ );
874
+ if (finalExpectedElementXpath) {
875
+ return [
876
+ {
877
+ key: `dynamic ${relation}${isIndex ? " index" : ""}`,
878
+ value: replaceTempAttributes(finalExpectedElementXpath),
879
+ },
880
+ ];
881
+ }
882
+ }
883
+ }
884
+ } else {
885
+ for (let x = 1; x <= absoluteXpathElements.length; x++) {
886
+ const xpath2 = absoluteXpathElements
887
+ .slice(absoluteXpathElements.length - x, absoluteXpathElements.length)
888
+ .join("/");
889
+ finalExpectedElementXpath = `//${xpathe1}/${relation}::${replaceActualAttributes(
890
+ xpath2,
891
+ element2
892
+ )}`;
893
+ const rel_count = getCountOfXPath(
894
+ finalExpectedElementXpath,
895
+ element2,
896
+ docmt
897
+ );
898
+ if (rel_count === 1) {
899
+ return [
900
+ {
901
+ key: `dynamic ${relation}`,
902
+ value: replaceTempAttributes(finalExpectedElementXpath),
903
+ },
904
+ ];
905
+ }
906
+ if (rel_count > 1) {
907
+ if (isIndex) {
908
+ finalExpectedElementXpath = findXpathWithIndex(
909
+ finalExpectedElementXpath,
910
+ refExpectElement[refExpectElement.length - 1],
911
+ docmt,
912
+ rel_count
913
+ );
914
+ if (finalExpectedElementXpath) {
915
+ return [
916
+ {
917
+ key: `dynamic ${relation}${isIndex ? " index" : ""}`,
918
+ value: replaceTempAttributes(finalExpectedElementXpath),
919
+ },
920
+ ];
921
+ }
922
+ }
923
+ }
924
+ }
925
+ }
926
+ };
927
+
928
+ const referenceXpath = {
929
+ findRelativeXpath,
930
+ getDescendantXpath,
931
+ getXpathRelationExpression,
932
+ getTraverseXpathExpression,
933
+ getReferenceElementXpath,
934
+ };
935
+
936
+ export default referenceXpath;