@dev-blinq/cucumber_client 1.0.1416-dev → 1.0.1417-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.
|
@@ -452,6 +452,126 @@ this.imports[2].node.source.value
|
|
|
452
452
|
let startEnd = this._getVariableStartEnd("elements");
|
|
453
453
|
this._replaceCode(code, startEnd[0], startEnd[1]);
|
|
454
454
|
}
|
|
455
|
+
mergeSimilarElements() {
|
|
456
|
+
const elements = this.getVariableDeclarationAsObject("elements");
|
|
457
|
+
if (elements === null) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const keys = Object.keys(elements);
|
|
462
|
+
|
|
463
|
+
// Ensure each element has element_key
|
|
464
|
+
for (let i = 0; i < keys.length; i++) {
|
|
465
|
+
const key = keys[i];
|
|
466
|
+
if (!elements[key].element_key) {
|
|
467
|
+
elements[key].element_key = key;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const replacedKeys = {};
|
|
472
|
+
const mergedElements = {};
|
|
473
|
+
|
|
474
|
+
// Walk from end so "later" keys win (your intended behavior)
|
|
475
|
+
for (let i = keys.length - 1; i >= 0; i--) {
|
|
476
|
+
const currentElement = elements[keys[i]];
|
|
477
|
+
let foundMatch = false;
|
|
478
|
+
|
|
479
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
480
|
+
const nextElement = elements[keys[j]];
|
|
481
|
+
|
|
482
|
+
if (this._isSimilarElement(currentElement, nextElement)) {
|
|
483
|
+
// Keep currentElement, replace earlier key with current's key
|
|
484
|
+
mergedElements[currentElement.element_key] = currentElement;
|
|
485
|
+
replacedKeys[nextElement.element_key] = currentElement.element_key;
|
|
486
|
+
foundMatch = true;
|
|
487
|
+
|
|
488
|
+
// Remove the earlier key so we don't carry it forward
|
|
489
|
+
keys.splice(j, 1);
|
|
490
|
+
|
|
491
|
+
// Since we removed an index before i, adjust i so it still points to currentElement
|
|
492
|
+
i--;
|
|
493
|
+
|
|
494
|
+
// IMPORTANT: compensate for splice so the loop’s j-- lands on the same logical next item
|
|
495
|
+
j++;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (!foundMatch) {
|
|
500
|
+
mergedElements[currentElement.element_key] = currentElement;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// If no replacements occurred, nothing else to do
|
|
505
|
+
if (Object.keys(replacedKeys).length === 0) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Replace all element references in file content
|
|
510
|
+
for (const key in replacedKeys) {
|
|
511
|
+
const regexp = new RegExp(`elements\\[\\s*["']${key}["']\\s*\\]`, "g");
|
|
512
|
+
this.fileContent = this.fileContent.replace(regexp, () => {
|
|
513
|
+
return `elements["${replacedKeys[key]}"]`;
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Save merged elements back
|
|
518
|
+
this.insertElements(mergedElements);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
//const regexp = new RegExp(`elements\\[\\s*["']${key}["']\\s*\\]`, "g");
|
|
522
|
+
/*
|
|
523
|
+
element shape example:
|
|
524
|
+
{
|
|
525
|
+
locators: [
|
|
526
|
+
{ css: 'internal:text="YES Subscription"s', priority: 1 },
|
|
527
|
+
{ css: 'internal:text="YES Subscription"i', priority: 1 },
|
|
528
|
+
{ css: 'h3 >> internal:has-text="YES Subscription"i', priority: 1 },
|
|
529
|
+
{ css: 'h3 >> internal:has-text=/^YES Subscription$/', priority: 1 },
|
|
530
|
+
{ css: 'internal:role=heading[name="YES Subscription"s]', priority: 1 },
|
|
531
|
+
],
|
|
532
|
+
element_name: "YES Subscription heading",
|
|
533
|
+
element_key: "heading_yes_subscription",
|
|
534
|
+
}
|
|
535
|
+
*/
|
|
536
|
+
|
|
537
|
+
_isSimilarElement(element1, element2) {
|
|
538
|
+
if (!element1 || !element2) return false;
|
|
539
|
+
|
|
540
|
+
const locs1 = Array.isArray(element1.locators) ? element1.locators : [];
|
|
541
|
+
const locs2 = Array.isArray(element2.locators) ? element2.locators : [];
|
|
542
|
+
|
|
543
|
+
// If both empty, fallback to false (names/keys already handled)
|
|
544
|
+
if (locs1.length === 0 && locs2.length === 0) return false;
|
|
545
|
+
if (locs1.length === 0 || locs2.length === 0) return false;
|
|
546
|
+
|
|
547
|
+
// Decide which set is "less" (subset candidate) and "more" (superset candidate)
|
|
548
|
+
const moreLocatorsElement = locs1.length >= locs2.length ? locs1 : locs2;
|
|
549
|
+
const lessLocatorsElement = locs1.length >= locs2.length ? locs2 : locs1;
|
|
550
|
+
|
|
551
|
+
// Helper: check if all key/value pairs in 'needle' exist identically in 'hay' (superset)
|
|
552
|
+
const locatorIncludes = (hay, needle) => {
|
|
553
|
+
if (hay == null || needle == null) return false;
|
|
554
|
+
// Only consider known keys; ignore undefined values
|
|
555
|
+
// ignore priority
|
|
556
|
+
const KEYS = ["css", "index", "text", "climb"];
|
|
557
|
+
for (const k of KEYS) {
|
|
558
|
+
if (needle[k] !== undefined) {
|
|
559
|
+
if (!(k in hay)) return false;
|
|
560
|
+
// Strict equality comparison for predictability
|
|
561
|
+
if (hay[k] !== needle[k]) return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return true;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// For every locator in the smaller set, we require a matching superset locator in the larger set
|
|
568
|
+
for (const less of lessLocatorsElement) {
|
|
569
|
+
const match = moreLocatorsElement.some((more) => locatorIncludes(more, less));
|
|
570
|
+
if (!match) return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
455
575
|
removeUnusedElements() {
|
|
456
576
|
const usedElementsKeys = this.getUsedElementsKeys();
|
|
457
577
|
const elements = this.getVariableDeclarationAsObject("elements");
|