@dev-blinq/cucumber_client 1.0.1465-dev → 1.0.1465-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/assets/bundled_scripts/recorder.js +73 -73
- package/bin/assets/scripts/recorder.js +87 -49
- package/bin/assets/scripts/snapshot_capturer.js +10 -17
- package/bin/assets/scripts/unique_locators.js +169 -47
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/code_inversion.js +127 -2
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/function_signature.js +8 -0
- package/bin/client/code_gen/index.js +4 -0
- package/bin/client/code_gen/page_reflection.js +95 -10
- package/bin/client/code_gen/playwright_codeget.js +173 -77
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/steps_definitions.js +13 -0
- package/bin/client/recorderv3/bvt_init.js +310 -0
- package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -293
- package/bin/client/recorderv3/mixpanel.js +39 -0
- package/bin/client/recorderv3/services.js +838 -142
- package/bin/client/recorderv3/step_runner.js +36 -7
- package/bin/client/recorderv3/step_utils.js +171 -95
- package/bin/client/recorderv3/update_feature.js +87 -39
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/types/locators.js +2 -0
- package/bin/client/upload-service.js +2 -0
- package/bin/client/utils/app_dir.js +21 -0
- package/bin/client/utils/socket_logger.js +100 -125
- package/bin/index.js +4 -1
- package/package.json +12 -5
- package/bin/client/recorderv3/app_dir.js +0 -23
- package/bin/client/recorderv3/network.js +0 -299
- package/bin/client/recorderv3/scriptTest.js +0 -5
- package/bin/client/recorderv3/ws_server.js +0 -72
|
@@ -144,7 +144,7 @@ class BVTRecorder {
|
|
|
144
144
|
getAction: (e) => {
|
|
145
145
|
this.eventUtils.consumeEvent(e);
|
|
146
146
|
},
|
|
147
|
-
getInterestedElement: () => {},
|
|
147
|
+
getInterestedElement: () => { },
|
|
148
148
|
hoverOutlineStyle: "",
|
|
149
149
|
});
|
|
150
150
|
|
|
@@ -159,7 +159,7 @@ class BVTRecorder {
|
|
|
159
159
|
};
|
|
160
160
|
},
|
|
161
161
|
getAction: () => null,
|
|
162
|
-
getInterestedElement: () => {},
|
|
162
|
+
getInterestedElement: () => { },
|
|
163
163
|
hoverOutlineStyle: "",
|
|
164
164
|
});
|
|
165
165
|
|
|
@@ -174,7 +174,7 @@ class BVTRecorder {
|
|
|
174
174
|
};
|
|
175
175
|
},
|
|
176
176
|
getAction: () => null,
|
|
177
|
-
getInterestedElement: () => {},
|
|
177
|
+
getInterestedElement: () => { },
|
|
178
178
|
hoverOutlineStyle: "",
|
|
179
179
|
});
|
|
180
180
|
|
|
@@ -390,6 +390,7 @@ class BVTRecorder {
|
|
|
390
390
|
}
|
|
391
391
|
case "input": {
|
|
392
392
|
const target = this.eventUtils.getNearestInteractiveElement(this.eventUtils.deepEventTarget(event));
|
|
393
|
+
|
|
393
394
|
if (target.nodeName === "INPUT" && target.type.toLowerCase() === "file") {
|
|
394
395
|
return {
|
|
395
396
|
details: {
|
|
@@ -434,6 +435,36 @@ class BVTRecorder {
|
|
|
434
435
|
return;
|
|
435
436
|
}
|
|
436
437
|
case "keydown": {
|
|
438
|
+
// override event.preventDefault to capture the value
|
|
439
|
+
|
|
440
|
+
const oldPreventDefault = event.preventDefault.bind(event);
|
|
441
|
+
event.preventDefault = () => {
|
|
442
|
+
if (event.key.length >= 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
443
|
+
const target = this.eventUtils.getNearestInteractiveElement(this.eventUtils.deepEventTarget(event));
|
|
444
|
+
setTimeout(() => {
|
|
445
|
+
if (event.__bvt_recorded !== undefined) return;
|
|
446
|
+
const valueBefore = target.value;
|
|
447
|
+
let newValue = valueBefore;
|
|
448
|
+
|
|
449
|
+
this.recordEvent(
|
|
450
|
+
{
|
|
451
|
+
details: {
|
|
452
|
+
name: "fill",
|
|
453
|
+
text: newValue,
|
|
454
|
+
},
|
|
455
|
+
element: target,
|
|
456
|
+
},
|
|
457
|
+
target,
|
|
458
|
+
"input",
|
|
459
|
+
event
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
event.__bvt_recorded = true;
|
|
463
|
+
}, 20);
|
|
464
|
+
}
|
|
465
|
+
oldPreventDefault();
|
|
466
|
+
};
|
|
467
|
+
|
|
437
468
|
if (!this.eventUtils.shouldGenerateKeyPressFor(event)) return;
|
|
438
469
|
// if (this._actionInProgress(event)) {
|
|
439
470
|
// this._expectProgrammaticKeyUp = true;
|
|
@@ -676,7 +707,8 @@ class BVTRecorder {
|
|
|
676
707
|
|
|
677
708
|
lastInputId = el.dataset.inputId;
|
|
678
709
|
|
|
679
|
-
el.__locators = this.getLocatorsObject(el);
|
|
710
|
+
el.__locators = this.getLocatorsObject(el, { maxLocators: 1 });
|
|
711
|
+
|
|
680
712
|
}
|
|
681
713
|
const role = this.PW.roleUtils.getAriaRole(el);
|
|
682
714
|
const label =
|
|
@@ -684,13 +716,15 @@ class BVTRecorder {
|
|
|
684
716
|
this.PW.roleUtils.getElementAccessibleName(el, true) ||
|
|
685
717
|
"";
|
|
686
718
|
const result = this.getElementProperties(el);
|
|
719
|
+
const elText = this.PW.selectorUtils.elementText(new Map(), el);
|
|
687
720
|
return {
|
|
688
721
|
role,
|
|
689
722
|
label,
|
|
690
723
|
inputID: el.dataset.inputId,
|
|
691
724
|
tagName: el.tagName,
|
|
692
725
|
type: el.type,
|
|
693
|
-
text:
|
|
726
|
+
text: elText.full.trim(),
|
|
727
|
+
textNormalized: elText.normalized.trim(),
|
|
694
728
|
parent: `tagname: ${el.parentElement?.tagName}\ninnerText: ${el.parentElement?.innerText}`,
|
|
695
729
|
attrs: {
|
|
696
730
|
placeholder: el.getAttribute("placeholder"),
|
|
@@ -787,30 +821,31 @@ class BVTRecorder {
|
|
|
787
821
|
return el.matches(closeBtnSelector);
|
|
788
822
|
});
|
|
789
823
|
}
|
|
790
|
-
|
|
791
|
-
|
|
824
|
+
docHasShadowDOM() {
|
|
825
|
+
for (const el of document.all) {
|
|
826
|
+
if (el.tagName.includes("-")) {
|
|
827
|
+
if (["x-bvt-toolbar", "x-bvt-tool"].includes(el.tagName.toLowerCase())) continue;
|
|
828
|
+
if (el.shadowRoot) {
|
|
829
|
+
return true;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
getLocatorsObject(el, options = {}) {
|
|
836
|
+
if (!this.docHasShadowDOM()) {
|
|
837
|
+
console.log("No custom elements detected, skipping locator generation");
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
console.log("Custom elements detected locator generation");
|
|
792
841
|
if (this.contextElement) {
|
|
793
|
-
const
|
|
794
|
-
const contextEl = this.contextElement;
|
|
795
|
-
// const { climb, commonParent } = window.getCommonParent(contextEl, el);
|
|
796
|
-
const commonParent = this.locatorGenerator.dom_Parent.findLowestCommonAncestor([contextEl, el]);
|
|
797
|
-
const climb = this.locatorGenerator.dom_Parent.getClimbCountToParent(contextEl, commonParent);
|
|
798
|
-
const result = this.locatorGenerator.getElementLocators(el, {
|
|
799
|
-
excludeText: true,
|
|
800
|
-
root: commonParent,
|
|
801
|
-
});
|
|
802
|
-
result.locators.forEach((locator) => {
|
|
803
|
-
locator.text = text;
|
|
804
|
-
locator.climb = climb;
|
|
805
|
-
});
|
|
806
|
-
result.allStrategyLocators.context = result.locators;
|
|
807
|
-
result.allStrategyLocators.strategy = "context";
|
|
808
|
-
result.allStrategyLocators.no_text = [];
|
|
842
|
+
const result = this.locatorGenerator.toContextLocators(el, this.contextElement, options);
|
|
809
843
|
return result;
|
|
810
844
|
}
|
|
811
845
|
const isRecordingText = this.#mode === "recordingText";
|
|
812
846
|
return this.locatorGenerator.getElementLocators(el, {
|
|
813
847
|
excludeText: isRecordingText,
|
|
848
|
+
...options,
|
|
814
849
|
});
|
|
815
850
|
}
|
|
816
851
|
addListeners() {
|
|
@@ -847,31 +882,7 @@ class BVTRecorder {
|
|
|
847
882
|
}
|
|
848
883
|
|
|
849
884
|
performance.mark("command-send");
|
|
850
|
-
|
|
851
|
-
mode: this.#mode,
|
|
852
|
-
action: action.details,
|
|
853
|
-
element: this.getElementDetails(actionElement, eventName),
|
|
854
|
-
isPopupCloseClick: this.isPopupCloseEvent(e),
|
|
855
|
-
// ...this.getLocatorsObject(actionElement),
|
|
856
|
-
...(actionElement.__locators ?? this.getLocatorsObject(actionElement)),
|
|
857
|
-
frame: this.getFrameDetails(),
|
|
858
|
-
statistics: {
|
|
859
|
-
time: `${performance.measure("command-received", "command-send").duration.toFixed(2)} ms`,
|
|
860
|
-
},
|
|
861
|
-
};
|
|
862
|
-
const snapshotDetails = {
|
|
863
|
-
id: actionElement.getAttribute("data-blinq-id"),
|
|
864
|
-
contextId: this.contextElement?.getAttribute("data-blinq-context-id"),
|
|
865
|
-
doc: this.snapshotCapturer.createSnapshot({
|
|
866
|
-
excludeSelectors: ["x-bvt-toolbar", "script", "style", "link[rel=stylesheet]"],
|
|
867
|
-
}),
|
|
868
|
-
};
|
|
869
|
-
cmd.snapshotDetails = snapshotDetails;
|
|
870
|
-
// eventQueue.enqueue(async () => {
|
|
871
|
-
// await bvtRecorderBindings.validateLocators(snapshotDetails);
|
|
872
|
-
// });
|
|
873
|
-
// console.log(cmd);
|
|
874
|
-
await bvtRecorderBindings.recordCommand(cmd);
|
|
885
|
+
this.recordEvent(action, actionElement, eventName, e);
|
|
875
886
|
this.handleStateTransition(action.element);
|
|
876
887
|
},
|
|
877
888
|
{ capture: true }
|
|
@@ -879,6 +890,33 @@ class BVTRecorder {
|
|
|
879
890
|
});
|
|
880
891
|
}
|
|
881
892
|
|
|
893
|
+
recordEvent(action, actionElement, eventName, e) {
|
|
894
|
+
const cmd = {
|
|
895
|
+
mode: this.#mode,
|
|
896
|
+
action: action.details,
|
|
897
|
+
element: this.getElementDetails(actionElement, eventName),
|
|
898
|
+
isPopupCloseClick: this.isPopupCloseEvent(e),
|
|
899
|
+
...(actionElement.__locators ?? this.getLocatorsObject(actionElement, { maxLocators: 1 })),
|
|
900
|
+
frame: this.getFrameDetails(),
|
|
901
|
+
statistics: {
|
|
902
|
+
time: `${performance.measure("command-received", "command-send").duration.toFixed(2)} ms`,
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
const snapshotDetails = {
|
|
906
|
+
id: actionElement.getAttribute("data-blinq-id"),
|
|
907
|
+
contextId: this.contextElement?.getAttribute("data-blinq-context-id"),
|
|
908
|
+
doc: this.snapshotCapturer.createSnapshot({
|
|
909
|
+
excludeSelectors: ["x-bvt-toolbar", "script", "style", "link[rel=stylesheet]"],
|
|
910
|
+
}),
|
|
911
|
+
};
|
|
912
|
+
cmd.snapshotDetails = snapshotDetails;
|
|
913
|
+
// eventQueue.enqueue(async () => {
|
|
914
|
+
// await bvtRecorderBindings.validateLocators(snapshotDetails);
|
|
915
|
+
// });
|
|
916
|
+
// console.log(cmd);
|
|
917
|
+
bvtRecorderBindings.recordCommand(cmd);
|
|
918
|
+
}
|
|
919
|
+
|
|
882
920
|
// TODO: implement the corresponding logic for the below methods
|
|
883
921
|
setPopupHandlers(_popopHandlers) {
|
|
884
922
|
this.popupHandlers = _popopHandlers;
|
|
@@ -897,7 +935,7 @@ class BVTRecorder {
|
|
|
897
935
|
this.interestedElements.clear();
|
|
898
936
|
}
|
|
899
937
|
processAriaSnapshot(snapshot) {
|
|
900
|
-
const matchedElements = this.findMatchingElements(snapshot, this.snapshotElements);
|
|
938
|
+
const matchedElements = this.snapshotUtils.findMatchingElements(snapshot, this.snapshotElements);
|
|
901
939
|
for (const el of matchedElements.values()) {
|
|
902
940
|
const element = el;
|
|
903
941
|
if (element) {
|
|
@@ -29,25 +29,18 @@ class SnapshotCapturer {
|
|
|
29
29
|
processStyles(document) {
|
|
30
30
|
if (!this.inlineStyles) return;
|
|
31
31
|
|
|
32
|
-
const stylesheets = Array.from(document.styleSheets);
|
|
32
|
+
const stylesheets = Array.from(window.document.styleSheets);
|
|
33
33
|
for (const sheet of stylesheets) {
|
|
34
34
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
styleEl.textContent = text;
|
|
45
|
-
|
|
46
|
-
// Replace the link with our new style element
|
|
47
|
-
if (sheet.ownerNode && sheet.ownerNode.parentNode) {
|
|
48
|
-
sheet.ownerNode.parentNode.replaceChild(styleEl, sheet.ownerNode);
|
|
49
|
-
} else if (sheet.ownerNode) {
|
|
50
|
-
// If the owner node is not in the document, just append it
|
|
35
|
+
const el = sheet.ownerNode.cloneNode(true);
|
|
36
|
+
if (!el.href) {
|
|
37
|
+
document.head.appendChild(el);
|
|
38
|
+
} else {
|
|
39
|
+
const rules = Array.from(sheet.cssRules)
|
|
40
|
+
.map((rule) => rule.cssText)
|
|
41
|
+
.join("\n");
|
|
42
|
+
const styleEl = document.createElement("style");
|
|
43
|
+
styleEl.textContent = rules;
|
|
51
44
|
document.head.appendChild(styleEl);
|
|
52
45
|
}
|
|
53
46
|
} catch (error) {
|
|
@@ -217,6 +217,59 @@ class LocatorGenerator {
|
|
|
217
217
|
}
|
|
218
218
|
return result;
|
|
219
219
|
}
|
|
220
|
+
getMixedLocators(element, options) {
|
|
221
|
+
const noTextLocators = this.getNoTextLocators(element, options);
|
|
222
|
+
const textLocators = this.getTextLocators(element, options);
|
|
223
|
+
const customLocators = this.getCustomLocators(element, options);
|
|
224
|
+
return [...customLocators, ...textLocators, ...noTextLocators];
|
|
225
|
+
}
|
|
226
|
+
toContextLocators(element, contextElement, options = {}) {
|
|
227
|
+
const commonParent = this.dom_Parent.findLowestCommonAncestor([contextElement, element]);
|
|
228
|
+
const climb = this.dom_Parent.getClimbCountToParent(contextElement, commonParent);
|
|
229
|
+
const text = contextElement.innerText.trim();
|
|
230
|
+
|
|
231
|
+
const prefix = `internal:text="${text}" >> ${this.getXPathSelector(climb)}`;
|
|
232
|
+
const result = this.getElementLocators(element, {
|
|
233
|
+
root: commonParent,
|
|
234
|
+
strategies: {
|
|
235
|
+
[this.locatorStrategies.custom]: true,
|
|
236
|
+
[this.locatorStrategies.text]: true,
|
|
237
|
+
[this.locatorStrategies.no_text]: true,
|
|
238
|
+
},
|
|
239
|
+
prefix,
|
|
240
|
+
...options,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const attachContextToLocators = (locs) => {
|
|
244
|
+
locs.forEach((loc) => {
|
|
245
|
+
loc.climb = climb;
|
|
246
|
+
loc.text = text;
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const allStrategyLocators = result.allStrategyLocators;
|
|
251
|
+
const locators = result.locators;
|
|
252
|
+
if (allStrategyLocators) {
|
|
253
|
+
const allLocators = [];
|
|
254
|
+
for (const strategy in allStrategyLocators) {
|
|
255
|
+
if (strategy === "strategy") continue;
|
|
256
|
+
const locators = allStrategyLocators[strategy];
|
|
257
|
+
if (locators.length === 0) continue;
|
|
258
|
+
allLocators.push(...locators);
|
|
259
|
+
allStrategyLocators[strategy] = [];
|
|
260
|
+
}
|
|
261
|
+
attachContextToLocators(allLocators);
|
|
262
|
+
allStrategyLocators[this.locatorStrategies.context] = allLocators;
|
|
263
|
+
allStrategyLocators.strategy = this.locatorStrategies.context;
|
|
264
|
+
result.locators = allLocators;
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
if (locators) {
|
|
268
|
+
attachContextToLocators(locators);
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
220
273
|
getContextLocators(element, locators) {
|
|
221
274
|
if (!locators || !Array.isArray(locators)) {
|
|
222
275
|
console.error("Locators must be an array");
|
|
@@ -441,12 +494,16 @@ class LocatorGenerator {
|
|
|
441
494
|
categorizeLocators(element, locators, options) {
|
|
442
495
|
const unique = [];
|
|
443
496
|
const nonUnique = [];
|
|
497
|
+
const visible = options?.visible ?? true;
|
|
444
498
|
try {
|
|
445
499
|
for (const locator of locators) {
|
|
446
500
|
if (!locator || !locator.css || typeof locator.css !== "string") {
|
|
447
501
|
console.error("Locator must have a valid css selector found: ", locator);
|
|
448
502
|
continue;
|
|
449
503
|
}
|
|
504
|
+
if (visible === false) {
|
|
505
|
+
locator.visible = false;
|
|
506
|
+
}
|
|
450
507
|
const elements = this.getMatchingElements(locator.css, options);
|
|
451
508
|
if (elements.length === 0) {
|
|
452
509
|
console.warn(`No elements found for locator: ${locator.css}`);
|
|
@@ -571,7 +628,7 @@ class LocatorGenerator {
|
|
|
571
628
|
result.push(_locator);
|
|
572
629
|
} else {
|
|
573
630
|
const index = _elements.indexOf(element);
|
|
574
|
-
if (index !== -1 && index <
|
|
631
|
+
if (index !== -1 && index < 10) {
|
|
575
632
|
// _locator.selector = fullSelector;
|
|
576
633
|
_locator.css = fullSelector;
|
|
577
634
|
_locator.index = index;
|
|
@@ -583,7 +640,7 @@ class LocatorGenerator {
|
|
|
583
640
|
}
|
|
584
641
|
} else {
|
|
585
642
|
const index = elements.indexOf(element);
|
|
586
|
-
if (index !== -1 && index <
|
|
643
|
+
if (index !== -1 && index < 10) {
|
|
587
644
|
locator.index = index;
|
|
588
645
|
locator.priority = 2; // non-unique locators have lower priority
|
|
589
646
|
result.push(locator);
|
|
@@ -591,7 +648,7 @@ class LocatorGenerator {
|
|
|
591
648
|
}
|
|
592
649
|
} else {
|
|
593
650
|
const index = elements.indexOf(element);
|
|
594
|
-
if (index !== -1 && index <
|
|
651
|
+
if (index !== -1 && index < 10) {
|
|
595
652
|
locator.index = index;
|
|
596
653
|
locator.priority = 2; // non-unique locators have lower priority
|
|
597
654
|
result.push(locator);
|
|
@@ -625,7 +682,7 @@ class LocatorGenerator {
|
|
|
625
682
|
|
|
626
683
|
getUniqueLocators2(element, locatorGenerator = this.getNoTextLocators, options = {}) {
|
|
627
684
|
try {
|
|
628
|
-
const { maxLocators = 5, root = window.document.body } = options;
|
|
685
|
+
const { maxLocators = 5, root = window.document.body, prefix } = options;
|
|
629
686
|
|
|
630
687
|
if (!element) {
|
|
631
688
|
return [];
|
|
@@ -645,13 +702,7 @@ class LocatorGenerator {
|
|
|
645
702
|
},
|
|
646
703
|
];
|
|
647
704
|
} else {
|
|
648
|
-
return [
|
|
649
|
-
{
|
|
650
|
-
css: ":root",
|
|
651
|
-
score: 1,
|
|
652
|
-
priority: 1,
|
|
653
|
-
},
|
|
654
|
-
];
|
|
705
|
+
return [];
|
|
655
706
|
}
|
|
656
707
|
}
|
|
657
708
|
|
|
@@ -678,12 +729,13 @@ class LocatorGenerator {
|
|
|
678
729
|
|
|
679
730
|
const elementsCache = new Map();
|
|
680
731
|
|
|
681
|
-
const allAncestors = this.dom_Parent.getFullAncestorChainToRoot(element, root);
|
|
732
|
+
const allAncestors = prefix ? [element] : this.dom_Parent.getFullAncestorChainToRoot(element, root);
|
|
682
733
|
allAncestors.shift(); // remove the element itself from the ancestors
|
|
683
734
|
|
|
684
735
|
const cache = new Map();
|
|
685
736
|
const textToIgnore = this.PW.selectorUtils.elementText(cache, element).full.trim();
|
|
686
737
|
const ancestorLocators = [];
|
|
738
|
+
let uniqueAncestor = null;
|
|
687
739
|
for (const ancestor of allAncestors) {
|
|
688
740
|
const _locators = locatorGenerator(ancestor, { ...options, textToIgnore });
|
|
689
741
|
if (!_locators || !Array.isArray(_locators) || _locators.length === 0) {
|
|
@@ -696,11 +748,15 @@ class LocatorGenerator {
|
|
|
696
748
|
});
|
|
697
749
|
elementsCache.set(ancestor, _categorized);
|
|
698
750
|
if (_categorized.unique.length > 0) {
|
|
751
|
+
uniqueAncestor = {
|
|
752
|
+
element: ancestor,
|
|
753
|
+
locators: _categorized,
|
|
754
|
+
};
|
|
699
755
|
break;
|
|
700
756
|
}
|
|
701
757
|
}
|
|
702
758
|
|
|
703
|
-
const uniqueAncestor = ancestorLocators[ancestorLocators.length - 1];
|
|
759
|
+
// const uniqueAncestor = ancestorLocators[ancestorLocators.length - 1];
|
|
704
760
|
|
|
705
761
|
for (const locator of nonUnique) {
|
|
706
762
|
const selector = locator.css ?? locator.selector;
|
|
@@ -709,6 +765,27 @@ class LocatorGenerator {
|
|
|
709
765
|
console.warn(`No elements found for locator: ${selector}`);
|
|
710
766
|
continue;
|
|
711
767
|
}
|
|
768
|
+
if (!uniqueAncestor) {
|
|
769
|
+
const elements = this.getMatchingElements(selector, options);
|
|
770
|
+
if (elements.length === 1 && elements[0] === element) {
|
|
771
|
+
result.push({
|
|
772
|
+
css: selector,
|
|
773
|
+
score: locator.score,
|
|
774
|
+
priority: 1,
|
|
775
|
+
});
|
|
776
|
+
} else {
|
|
777
|
+
const index = elements.indexOf(element);
|
|
778
|
+
if (index !== -1) {
|
|
779
|
+
result.push({
|
|
780
|
+
css: selector,
|
|
781
|
+
index,
|
|
782
|
+
score: locator.score + 200,
|
|
783
|
+
priority: 2,
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
712
789
|
|
|
713
790
|
for (const unique_locator of uniqueAncestor.locators.unique) {
|
|
714
791
|
const fullSelector = `${unique_locator.css} >> ${selector}`;
|
|
@@ -724,7 +801,7 @@ class LocatorGenerator {
|
|
|
724
801
|
result.push(newLocator);
|
|
725
802
|
} else {
|
|
726
803
|
const index = elements.indexOf(element);
|
|
727
|
-
if (index !== -1 && index <
|
|
804
|
+
if (index !== -1 && index < 10) {
|
|
728
805
|
const effectiveScore = (unique_locator.score + locator.score) / 2;
|
|
729
806
|
const newLocator = {
|
|
730
807
|
...unique_locator,
|
|
@@ -747,9 +824,32 @@ class LocatorGenerator {
|
|
|
747
824
|
return [];
|
|
748
825
|
}
|
|
749
826
|
}
|
|
827
|
+
isElementVisible(element) {
|
|
828
|
+
if (!(element instanceof Element)) return false;
|
|
829
|
+
const style = window.getComputedStyle(element);
|
|
830
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
|
|
831
|
+
const rect = element.getBoundingClientRect();
|
|
832
|
+
if (rect.width === 0 || rect.height === 0) return false;
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
750
835
|
getElementLocators(element, options = {}) {
|
|
836
|
+
const isVisible = this.PW.domUtils.isElementVisible(element);
|
|
837
|
+
if (isVisible === false) {
|
|
838
|
+
console.warn("Element is not visible: ", element);
|
|
839
|
+
options.visible = isVisible;
|
|
840
|
+
}
|
|
751
841
|
try {
|
|
752
|
-
const {
|
|
842
|
+
const {
|
|
843
|
+
excludeText = false,
|
|
844
|
+
strategies = {
|
|
845
|
+
[this.locatorStrategies.custom]: true,
|
|
846
|
+
[this.locatorStrategies.context]: true,
|
|
847
|
+
[this.locatorStrategies.text]: true,
|
|
848
|
+
[this.locatorStrategies.text_with_index]: true,
|
|
849
|
+
[this.locatorStrategies.digitIgnore]: true,
|
|
850
|
+
[this.locatorStrategies.no_text]: true,
|
|
851
|
+
},
|
|
852
|
+
} = options;
|
|
753
853
|
|
|
754
854
|
const allStrategyLocators = {
|
|
755
855
|
[this.locatorStrategies.custom]: [],
|
|
@@ -759,7 +859,12 @@ class LocatorGenerator {
|
|
|
759
859
|
[this.locatorStrategies.digitIgnore]: [],
|
|
760
860
|
[this.locatorStrategies.no_text]: [],
|
|
761
861
|
};
|
|
762
|
-
if (
|
|
862
|
+
if (
|
|
863
|
+
strategies[this.locatorStrategies.custom] &&
|
|
864
|
+
this.options?.customAttributes &&
|
|
865
|
+
Array.isArray(this.options.customAttributes) &&
|
|
866
|
+
this.options.customAttributes.length > 0
|
|
867
|
+
) {
|
|
763
868
|
console.groupCollapsed("Generating Custom locators for element:", element);
|
|
764
869
|
const customLocators = this.getUniqueLocators(element, this.getCustomLocators.bind(this), options);
|
|
765
870
|
if (customLocators.length > 0) {
|
|
@@ -768,43 +873,60 @@ class LocatorGenerator {
|
|
|
768
873
|
}
|
|
769
874
|
console.groupEnd();
|
|
770
875
|
if (!excludeText) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
console.groupEnd();
|
|
774
|
-
if (basicLocators.length > 0) {
|
|
775
|
-
allStrategyLocators[this.locatorStrategies.text] = basicLocators;
|
|
776
|
-
}
|
|
876
|
+
if (strategies[this.locatorStrategies.text]) {
|
|
877
|
+
console.groupCollapsed("Generating Text locators for element:", element);
|
|
777
878
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
879
|
+
const basicLocators = this.getUniqueLocators(element, this.getTextLocators.bind(this), options);
|
|
880
|
+
console.groupEnd();
|
|
881
|
+
if (basicLocators.length > 0) {
|
|
882
|
+
allStrategyLocators[this.locatorStrategies.text] = basicLocators;
|
|
883
|
+
}
|
|
884
|
+
if (strategies[this.locatorStrategies.text_with_index]) {
|
|
885
|
+
const textWithIndexLocators = this.getTextwithIndexLocators(basicLocators);
|
|
886
|
+
if (textWithIndexLocators.length > 0) {
|
|
887
|
+
allStrategyLocators[this.locatorStrategies.text_with_index] = textWithIndexLocators;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (strategies[this.locatorStrategies.digitIgnore]) {
|
|
891
|
+
const digitIgnoreLocators = this.getDigitIgnoreLocators(element, basicLocators);
|
|
892
|
+
if (digitIgnoreLocators.length > 0) {
|
|
893
|
+
allStrategyLocators[this.locatorStrategies.digitIgnore] = digitIgnoreLocators;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (strategies[this.locatorStrategies.context]) {
|
|
897
|
+
const contextLocators = this.getContextLocators(element, basicLocators);
|
|
898
|
+
if (contextLocators.length > 0) {
|
|
899
|
+
allStrategyLocators[this.locatorStrategies.context] = contextLocators;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
789
902
|
}
|
|
790
903
|
}
|
|
791
|
-
|
|
792
|
-
|
|
904
|
+
if (strategies[this.locatorStrategies.no_text]) {
|
|
905
|
+
console.groupCollapsed("Generating No Text locators for element:", element);
|
|
906
|
+
const noTextLocators = this.getUniqueLocators(element, this.getNoTextLocators.bind(this), options);
|
|
793
907
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
908
|
+
if (noTextLocators.length > 0) {
|
|
909
|
+
allStrategyLocators[this.locatorStrategies.no_text] = noTextLocators;
|
|
910
|
+
} else {
|
|
911
|
+
|
|
912
|
+
// try mixed locator strategy as last resort
|
|
913
|
+
const mixedLocators = this.getUniqueLocators(element, this.getMixedLocators.bind(this), options);
|
|
914
|
+
if (mixedLocators.length > 0) {
|
|
915
|
+
allStrategyLocators[this.locatorStrategies.no_text] = mixedLocators;
|
|
916
|
+
} else {
|
|
917
|
+
const _locators = [];
|
|
918
|
+
_locators.push({
|
|
919
|
+
css: this.generateUniqueCSSSelector(element, options),
|
|
920
|
+
score: 500,
|
|
921
|
+
priority: 3,
|
|
922
|
+
});
|
|
923
|
+
if (_locators.length > 0) {
|
|
924
|
+
allStrategyLocators[this.locatorStrategies.no_text] = _locators;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
805
927
|
}
|
|
928
|
+
console.groupEnd();
|
|
806
929
|
}
|
|
807
|
-
console.groupEnd();
|
|
808
930
|
|
|
809
931
|
let bestStrategy = this.getBestStrategy(allStrategyLocators);
|
|
810
932
|
if (!bestStrategy) {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
// Load .env into process.env
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
1
5
|
import {
|
|
2
6
|
After,
|
|
3
7
|
setDefaultTimeout,
|
|
@@ -30,8 +34,8 @@ BeforeStep(async function (step) {
|
|
|
30
34
|
}
|
|
31
35
|
});
|
|
32
36
|
|
|
33
|
-
AfterStep(async function (
|
|
37
|
+
AfterStep(async function ({ result, pickleStep }) {
|
|
34
38
|
if (context) {
|
|
35
|
-
await context.web.afterStep(this,
|
|
39
|
+
await context.web.afterStep(this, pickleStep, result);
|
|
36
40
|
}
|
|
37
41
|
});
|