@checksum-ai/runtime 1.1.49 → 1.1.50-beta
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/checksumlib.js +296 -137
- package/cli.js +76 -68
- package/index.js +50 -50
- package/package.json +1 -1
- package/vtg-build/asset-manifest.json +5 -3
- package/vtg-build/index.html +1 -1
- package/vtg-build/static/js/425.b1a74f40.chunk.js +2 -0
- package/vtg-build/static/js/425.b1a74f40.chunk.js.map +1 -0
- package/vtg-build/static/js/main.1225078e.js +3 -0
- package/vtg-build/static/js/main.1225078e.js.LICENSE.txt +146 -0
- package/vtg-build/static/js/main.1225078e.js.map +1 -0
- package/vtg-build/static/js/main.bb2f02ec.js +3 -0
- package/vtg-build/static/js/main.bb2f02ec.js.LICENSE.txt +146 -0
- package/vtg-build/static/js/main.bb2f02ec.js.map +1 -0
package/checksumlib.js
CHANGED
|
@@ -7509,6 +7509,9 @@
|
|
|
7509
7509
|
return null;
|
|
7510
7510
|
}, "closestExtended");
|
|
7511
7511
|
var childNodesExtended = /* @__PURE__ */ __name((node2, { flattenShadowRootChildren = false } = {}) => {
|
|
7512
|
+
if (!node2) {
|
|
7513
|
+
return [];
|
|
7514
|
+
}
|
|
7512
7515
|
let childNodes2 = Array.from(node2.childNodes);
|
|
7513
7516
|
if (node2.nodeType === Node.ELEMENT_NODE) {
|
|
7514
7517
|
const element = node2;
|
|
@@ -7579,7 +7582,12 @@
|
|
|
7579
7582
|
}
|
|
7580
7583
|
return null;
|
|
7581
7584
|
}, "parentElementExtended");
|
|
7582
|
-
var serializeElement = /* @__PURE__ */ __name((element, {
|
|
7585
|
+
var serializeElement = /* @__PURE__ */ __name((element, {
|
|
7586
|
+
includeComments = true,
|
|
7587
|
+
includeCDATA = true,
|
|
7588
|
+
trim = true,
|
|
7589
|
+
limitAttributeLength = false
|
|
7590
|
+
} = {}) => {
|
|
7583
7591
|
if (!element) {
|
|
7584
7592
|
return "";
|
|
7585
7593
|
}
|
|
@@ -7588,7 +7596,7 @@
|
|
|
7588
7596
|
case Node.ELEMENT_NODE:
|
|
7589
7597
|
return serializeElementNode2(node2);
|
|
7590
7598
|
case Node.TEXT_NODE:
|
|
7591
|
-
return
|
|
7599
|
+
return node2.nodeValue;
|
|
7592
7600
|
case Node.CDATA_SECTION_NODE:
|
|
7593
7601
|
return includeCDATA ? `<![CDATA[${node2.nodeValue}]]>` : "";
|
|
7594
7602
|
case Node.COMMENT_NODE:
|
|
@@ -7615,7 +7623,11 @@
|
|
|
7615
7623
|
let tagName = element2.tagName.toLowerCase();
|
|
7616
7624
|
let serialized = `<${tagName}`;
|
|
7617
7625
|
Array.from(element2.attributes).forEach((attr) => {
|
|
7618
|
-
|
|
7626
|
+
const attributeValue = attr.value.slice(
|
|
7627
|
+
0,
|
|
7628
|
+
limitAttributeLength ? 100 : void 0
|
|
7629
|
+
);
|
|
7630
|
+
serialized += ` ${attr.name}="${attributeValue}"`;
|
|
7619
7631
|
});
|
|
7620
7632
|
serialized += ">";
|
|
7621
7633
|
if (element2.shadowRoot) {
|
|
@@ -32744,6 +32756,39 @@
|
|
|
32744
32756
|
|
|
32745
32757
|
// src/lib/test-generator/selectors/pw-custom-locator-generator.ts
|
|
32746
32758
|
var DEBUG_MODE = false;
|
|
32759
|
+
var LocatorChain = class _LocatorChain {
|
|
32760
|
+
constructor(locators) {
|
|
32761
|
+
this.locators = [];
|
|
32762
|
+
this.elements = [];
|
|
32763
|
+
this.locators = locators;
|
|
32764
|
+
}
|
|
32765
|
+
static {
|
|
32766
|
+
__name(this, "LocatorChain");
|
|
32767
|
+
}
|
|
32768
|
+
/**
|
|
32769
|
+
* Check if any of the locators have the same filter values
|
|
32770
|
+
* For now, we only check for hasText filter
|
|
32771
|
+
*/
|
|
32772
|
+
hasDuplicateFilters() {
|
|
32773
|
+
const hasTextFilters = this.locators.map((l2) => l2.options?.hasText).filter((value) => value);
|
|
32774
|
+
return hasTextFilters.length !== new Set(hasTextFilters).size;
|
|
32775
|
+
}
|
|
32776
|
+
cloneAndAddCandidate(candidate) {
|
|
32777
|
+
return new _LocatorChain([candidate, ...this.locators.slice()]);
|
|
32778
|
+
}
|
|
32779
|
+
toLocatorCandidate() {
|
|
32780
|
+
return {
|
|
32781
|
+
selector: this.getSelector(),
|
|
32782
|
+
locator: this.getLocator()
|
|
32783
|
+
};
|
|
32784
|
+
}
|
|
32785
|
+
getSelector() {
|
|
32786
|
+
return this.locators.map((l2) => l2.selector).join(" >> ");
|
|
32787
|
+
}
|
|
32788
|
+
getLocator() {
|
|
32789
|
+
return this.locators.map((l2) => l2.locator).join(".");
|
|
32790
|
+
}
|
|
32791
|
+
};
|
|
32747
32792
|
var PlaywrightCustomLocatorGenerator = class _PlaywrightCustomLocatorGenerator {
|
|
32748
32793
|
constructor(rootNode = document) {
|
|
32749
32794
|
this.rootNode = rootNode;
|
|
@@ -32763,29 +32808,23 @@
|
|
|
32763
32808
|
* @param cssKeyElements array of css key elements to use as selectors
|
|
32764
32809
|
* @param options
|
|
32765
32810
|
*/
|
|
32766
|
-
async generate(element, cssKeyElements = [], {
|
|
32767
|
-
|
|
32768
|
-
|
|
32769
|
-
|
|
32770
|
-
|
|
32771
|
-
|
|
32772
|
-
|
|
32773
|
-
|
|
32774
|
-
|
|
32775
|
-
|
|
32776
|
-
|
|
32777
|
-
|
|
32778
|
-
|
|
32779
|
-
|
|
32780
|
-
|
|
32781
|
-
|
|
32782
|
-
|
|
32783
|
-
useTextContent = true,
|
|
32784
|
-
/* Whether to add CSS selector from the css-selector-generator library */
|
|
32785
|
-
addCSSSelectorGenerator = true,
|
|
32786
|
-
/* Whether to heuristiically expand CSS key elements to include more selectors */
|
|
32787
|
-
expandCSSKeyElements = true
|
|
32788
|
-
} = {}) {
|
|
32811
|
+
async generate(element, cssKeyElements = [], options = {}) {
|
|
32812
|
+
const {
|
|
32813
|
+
isPartOfListItem,
|
|
32814
|
+
isForContextElement,
|
|
32815
|
+
useTextContent,
|
|
32816
|
+
addCSSSelectorGenerator,
|
|
32817
|
+
expandCSSKeyElements,
|
|
32818
|
+
bruteForceMode
|
|
32819
|
+
} = {
|
|
32820
|
+
isPartOfListItem: false,
|
|
32821
|
+
isForContextElement: false,
|
|
32822
|
+
useTextContent: true,
|
|
32823
|
+
addCSSSelectorGenerator: true,
|
|
32824
|
+
expandCSSKeyElements: true,
|
|
32825
|
+
bruteForceMode: false,
|
|
32826
|
+
...options
|
|
32827
|
+
};
|
|
32789
32828
|
if (isNodeInstanceOf(element, "HTMLOptionElement")) {
|
|
32790
32829
|
const select = element.closest("select");
|
|
32791
32830
|
if (select) {
|
|
@@ -32794,10 +32833,9 @@
|
|
|
32794
32833
|
});
|
|
32795
32834
|
}
|
|
32796
32835
|
}
|
|
32797
|
-
this.usedFilters = { hasText: [], has: [] };
|
|
32798
32836
|
this.targetElement = element;
|
|
32799
32837
|
this.startTimestamp = Date.now();
|
|
32800
|
-
const
|
|
32838
|
+
const locatorsChains = [];
|
|
32801
32839
|
cssKeyElements = cssKeyElements.filter(
|
|
32802
32840
|
(featureElement) => Array.from(document.querySelectorAll(featureElement.selector)).includes(
|
|
32803
32841
|
featureElement.element
|
|
@@ -32807,70 +32845,78 @@
|
|
|
32807
32845
|
this.expandCSSKeyFeatures(element, cssKeyElements);
|
|
32808
32846
|
}
|
|
32809
32847
|
while (element && element !== this.rootNode) {
|
|
32810
|
-
const
|
|
32811
|
-
|
|
32812
|
-
|
|
32848
|
+
const elementCandidates = this.getAllLocators(
|
|
32849
|
+
element,
|
|
32850
|
+
cssKeyElements,
|
|
32851
|
+
{
|
|
32852
|
+
useTextContent,
|
|
32853
|
+
bruteForceMode
|
|
32854
|
+
}
|
|
32855
|
+
);
|
|
32813
32856
|
const validLocators = [];
|
|
32814
|
-
const
|
|
32857
|
+
const testCandidate = /* @__PURE__ */ __name((chain) => {
|
|
32858
|
+
if (chain.hasDuplicateFilters()) {
|
|
32859
|
+
return;
|
|
32860
|
+
}
|
|
32815
32861
|
try {
|
|
32816
|
-
const { elements } = this.getLocatorBase(element).locator(
|
|
32817
|
-
|
|
32818
|
-
|
|
32819
|
-
|
|
32820
|
-
|
|
32821
|
-
|
|
32822
|
-
|
|
32823
|
-
)
|
|
32824
|
-
});
|
|
32862
|
+
const { elements } = this.getLocatorBase(element).locator(
|
|
32863
|
+
chain.getSelector()
|
|
32864
|
+
);
|
|
32865
|
+
if (elements?.some((e2) => e2 === this.targetElement)) {
|
|
32866
|
+
chain.elements = elements.filter(
|
|
32867
|
+
(el) => isInstanceOfHTMLElement(el) || el.constructor.name === this.targetElement.constructor.name
|
|
32868
|
+
), validLocators.push(chain);
|
|
32825
32869
|
}
|
|
32826
32870
|
} catch (error) {
|
|
32827
|
-
console.log(error);
|
|
32871
|
+
console.log(error, chain.getSelector());
|
|
32828
32872
|
}
|
|
32829
|
-
}, "
|
|
32830
|
-
if (!
|
|
32831
|
-
|
|
32873
|
+
}, "testCandidate");
|
|
32874
|
+
if (!locatorsChains.length) {
|
|
32875
|
+
elementCandidates.forEach(
|
|
32876
|
+
(ec) => testCandidate(new LocatorChain([ec]))
|
|
32877
|
+
);
|
|
32832
32878
|
} else {
|
|
32833
|
-
|
|
32834
|
-
|
|
32835
|
-
(sc) =>
|
|
32879
|
+
elementCandidates.forEach((ec) => {
|
|
32880
|
+
locatorsChains.forEach(
|
|
32881
|
+
(sc) => testCandidate(sc.cloneAndAddCandidate(ec))
|
|
32836
32882
|
);
|
|
32837
32883
|
});
|
|
32838
32884
|
}
|
|
32839
|
-
if (validLocators.length === 0 &&
|
|
32885
|
+
if (validLocators.length === 0 && locatorsChains.length === 0) {
|
|
32840
32886
|
const selector = element.tagName.toLowerCase();
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32846
|
-
|
|
32847
|
-
|
|
32887
|
+
locatorsChains.push(
|
|
32888
|
+
new LocatorChain([
|
|
32889
|
+
{
|
|
32890
|
+
selector,
|
|
32891
|
+
locator: this.getSelectorLocator(selector),
|
|
32892
|
+
elements: this.getLocatorBase(element).locator(element.tagName.toLowerCase()).elements.filter(
|
|
32893
|
+
(el) => isInstanceOfHTMLElement(el)
|
|
32894
|
+
)
|
|
32895
|
+
}
|
|
32896
|
+
])
|
|
32897
|
+
);
|
|
32848
32898
|
}
|
|
32849
|
-
|
|
32899
|
+
locatorsChains.push(...validLocators);
|
|
32850
32900
|
if (isForContextElement) {
|
|
32851
|
-
return
|
|
32852
|
-
selector,
|
|
32853
|
-
locator
|
|
32854
|
-
}));
|
|
32901
|
+
return locatorsChains.map((chain) => chain.toLocatorCandidate());
|
|
32855
32902
|
}
|
|
32856
|
-
if (
|
|
32903
|
+
if (locatorsChains.length > this.MAX_TESTING_SELECTORS || locatorsChains.filter((sc) => sc.elements.length === 1).length > this.MAX_SINGLE_ELEMENT_SELECTORS || this.checkTimeout(false)) {
|
|
32857
32904
|
break;
|
|
32858
32905
|
}
|
|
32859
32906
|
element = element.parentElement;
|
|
32860
32907
|
}
|
|
32861
32908
|
if (isPartOfListItem) {
|
|
32862
|
-
const listItemsCandidates = this.filterListItemsSelectors(
|
|
32909
|
+
const listItemsCandidates = this.filterListItemsSelectors(locatorsChains);
|
|
32863
32910
|
if (listItemsCandidates?.length) {
|
|
32864
|
-
return listItemsCandidates.map((
|
|
32865
|
-
|
|
32866
|
-
locator,
|
|
32911
|
+
return listItemsCandidates.map((lic) => ({
|
|
32912
|
+
...lic.toLocatorCandidate(),
|
|
32867
32913
|
isListSelector: true
|
|
32868
32914
|
}));
|
|
32869
32915
|
}
|
|
32870
32916
|
}
|
|
32871
32917
|
let filteredCandidates = [];
|
|
32872
32918
|
try {
|
|
32873
|
-
for (const candidate of
|
|
32919
|
+
for (const candidate of locatorsChains) {
|
|
32874
32920
|
this.checkTimeout();
|
|
32875
32921
|
filteredCandidates.push(
|
|
32876
32922
|
...candidate.elements.length === 1 ? [candidate] : await this.reduceMultiCandidates(candidate)
|
|
@@ -32903,10 +32949,7 @@
|
|
|
32903
32949
|
const { selector, locator } = await new PlaywrightElementSelectorGenerator().getSelectorAndLocator(
|
|
32904
32950
|
this.targetElement
|
|
32905
32951
|
);
|
|
32906
|
-
filteredCandidates.unshift({
|
|
32907
|
-
selector,
|
|
32908
|
-
locator
|
|
32909
|
-
});
|
|
32952
|
+
filteredCandidates.unshift(new LocatorChain([{ selector, locator }]));
|
|
32910
32953
|
}
|
|
32911
32954
|
filteredCandidates = filteredCandidates.concat(
|
|
32912
32955
|
await this.addOptionalParentSelector(
|
|
@@ -32915,10 +32958,14 @@
|
|
|
32915
32958
|
expandCSSKeyElements
|
|
32916
32959
|
)
|
|
32917
32960
|
);
|
|
32918
|
-
return
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
|
|
32961
|
+
return Array.from(
|
|
32962
|
+
new Map(
|
|
32963
|
+
filteredCandidates.map((fc) => [
|
|
32964
|
+
fc.toLocatorCandidate().selector,
|
|
32965
|
+
fc.toLocatorCandidate()
|
|
32966
|
+
])
|
|
32967
|
+
).values()
|
|
32968
|
+
);
|
|
32922
32969
|
}
|
|
32923
32970
|
/**
|
|
32924
32971
|
* Test if a selector can locate an element
|
|
@@ -32986,10 +33033,12 @@
|
|
|
32986
33033
|
2e3
|
|
32987
33034
|
);
|
|
32988
33035
|
if (selector.length < 50) {
|
|
32989
|
-
return
|
|
32990
|
-
|
|
32991
|
-
|
|
32992
|
-
|
|
33036
|
+
return new LocatorChain([
|
|
33037
|
+
{
|
|
33038
|
+
selector,
|
|
33039
|
+
locator: `locator('${selector}')`
|
|
33040
|
+
}
|
|
33041
|
+
]);
|
|
32993
33042
|
}
|
|
32994
33043
|
} catch (error) {
|
|
32995
33044
|
console.error("Error getting CSS selector", error);
|
|
@@ -32999,20 +33048,22 @@
|
|
|
32999
33048
|
* If playwright selector for element points at a different element - generate selectors for that element as well
|
|
33000
33049
|
* i.e - playwright can point at a parent button if element is a child of it
|
|
33001
33050
|
*/
|
|
33002
|
-
addOptionalParentSelector(useTextContent, addCSSSelectorGenerator, expandCSSKeyElements) {
|
|
33051
|
+
async addOptionalParentSelector(useTextContent, addCSSSelectorGenerator, expandCSSKeyElements) {
|
|
33003
33052
|
try {
|
|
33004
33053
|
const playwright = getElementWindowPlaywright(this.targetElement);
|
|
33005
33054
|
const playwrightTargetElement = playwright.locator(
|
|
33006
33055
|
playwright.selector(this.targetElement)
|
|
33007
33056
|
).element;
|
|
33008
33057
|
if (playwrightTargetElement !== this.targetElement && playwrightTargetElement?.contains(this.targetElement)) {
|
|
33009
|
-
return this.generate(playwrightTargetElement, [], {
|
|
33058
|
+
return (await this.generate(playwrightTargetElement, [], {
|
|
33010
33059
|
isPartOfListItem: false,
|
|
33011
33060
|
isForContextElement: false,
|
|
33012
33061
|
useTextContent,
|
|
33013
33062
|
addCSSSelectorGenerator,
|
|
33014
33063
|
expandCSSKeyElements
|
|
33015
|
-
})
|
|
33064
|
+
})).map(
|
|
33065
|
+
({ selector, locator }) => new LocatorChain([{ selector, locator }])
|
|
33066
|
+
);
|
|
33016
33067
|
}
|
|
33017
33068
|
} finally {
|
|
33018
33069
|
return [];
|
|
@@ -33221,23 +33272,27 @@
|
|
|
33221
33272
|
async reduceMultiCandidates(candidate) {
|
|
33222
33273
|
await awaitSleep(100);
|
|
33223
33274
|
const { elements } = candidate;
|
|
33224
|
-
const parts = candidate.
|
|
33275
|
+
const parts = candidate.getSelector().split(" >> ");
|
|
33225
33276
|
const newCandidates = [];
|
|
33226
33277
|
const locatorBase = this.getLocatorBase(this.targetElement);
|
|
33227
33278
|
const addCandidateWithSelector = /* @__PURE__ */ __name((selector) => {
|
|
33228
33279
|
try {
|
|
33229
|
-
newCandidates.push(
|
|
33230
|
-
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33280
|
+
newCandidates.push(
|
|
33281
|
+
new LocatorChain([
|
|
33282
|
+
{
|
|
33283
|
+
selector,
|
|
33284
|
+
locator: this.getSelectorLocator(selector)
|
|
33285
|
+
// options: candidate.options,
|
|
33286
|
+
}
|
|
33287
|
+
])
|
|
33288
|
+
);
|
|
33234
33289
|
return newCandidates.length >= this.MAX_MULTICANDIDATE_PROCESSING;
|
|
33235
33290
|
} catch (error) {
|
|
33236
33291
|
console.error(error);
|
|
33237
33292
|
return false;
|
|
33238
33293
|
}
|
|
33239
33294
|
}, "addCandidateWithSelector");
|
|
33240
|
-
const addCSSFilterToLocator = /* @__PURE__ */ __name((filter) => candidate.
|
|
33295
|
+
const addCSSFilterToLocator = /* @__PURE__ */ __name((filter) => candidate.getSelector() + // if we used css selector with tag name - concatentate it with the filter
|
|
33241
33296
|
(parts[parts.length - 1] === this.targetElement.tagName.toLowerCase() ? filter : (
|
|
33242
33297
|
// else - add as a new part
|
|
33243
33298
|
` >> internal:and=${escapeForAndLocator(
|
|
@@ -33245,7 +33300,7 @@
|
|
|
33245
33300
|
)}`
|
|
33246
33301
|
)), "addCSSFilterToLocator");
|
|
33247
33302
|
const addIfSingularAndCheckLimit = /* @__PURE__ */ __name((selector) => {
|
|
33248
|
-
if (locatorBase.locator(selector
|
|
33303
|
+
if (locatorBase.locator(selector).elements.length === 1) {
|
|
33249
33304
|
return addCandidateWithSelector(selector);
|
|
33250
33305
|
}
|
|
33251
33306
|
return false;
|
|
@@ -33253,7 +33308,7 @@
|
|
|
33253
33308
|
if (elements.length < 5) {
|
|
33254
33309
|
const index2 = elements.indexOf(this.targetElement);
|
|
33255
33310
|
if (index2 !== -1) {
|
|
33256
|
-
addCandidateWithSelector(candidate.
|
|
33311
|
+
addCandidateWithSelector(candidate.getSelector() + ` >> nth=${index2}`);
|
|
33257
33312
|
}
|
|
33258
33313
|
}
|
|
33259
33314
|
const parent = this.targetElement.parentElement;
|
|
@@ -33298,12 +33353,11 @@
|
|
|
33298
33353
|
}
|
|
33299
33354
|
getAllLocators(element, cssKeyElements, options = {}) {
|
|
33300
33355
|
options = {
|
|
33301
|
-
returnLocator: false,
|
|
33302
33356
|
exact: true,
|
|
33303
33357
|
useTextContent: true,
|
|
33304
33358
|
...options
|
|
33305
33359
|
};
|
|
33306
|
-
|
|
33360
|
+
const locators = [
|
|
33307
33361
|
this.getByRoleLocator(element, options),
|
|
33308
33362
|
options.useTextContent ? this.getByTextLocator(element, options) : null,
|
|
33309
33363
|
this.getByLabelLocator(element, options),
|
|
@@ -33311,26 +33365,80 @@
|
|
|
33311
33365
|
this.getByTitleLocator(element, options),
|
|
33312
33366
|
this.getByAltLocator(element, options),
|
|
33313
33367
|
this.getTestIdLocator(element, options),
|
|
33314
|
-
...element === this.targetElement ? [this.getInnerFeaturesLocator(element
|
|
33315
|
-
...cssKeyElements.filter((el) => el.element === element).map((el) =>
|
|
33368
|
+
...element === this.targetElement ? [this.getInnerFeaturesLocator(element)] : [],
|
|
33369
|
+
...cssKeyElements.filter((el) => el.element === element).map((el) => ({
|
|
33370
|
+
selector: el.selector,
|
|
33371
|
+
locator: this.getSelectorLocator(el.selector)
|
|
33372
|
+
}))
|
|
33316
33373
|
].filter((locator) => locator);
|
|
33374
|
+
if (!locators.length && options.bruteForceMode) {
|
|
33375
|
+
return locators.concat(this.findUniqueCSSLocatorForElement(element));
|
|
33376
|
+
}
|
|
33377
|
+
return locators;
|
|
33378
|
+
}
|
|
33379
|
+
/**
|
|
33380
|
+
* Used when in brute force mode to find unique CSS locator for the element
|
|
33381
|
+
*/
|
|
33382
|
+
findUniqueCSSLocatorForElement(element, maxCandidates = 5) {
|
|
33383
|
+
const candidates = [];
|
|
33384
|
+
const addCandidate = /* @__PURE__ */ __name((selector) => {
|
|
33385
|
+
candidates.push([
|
|
33386
|
+
{
|
|
33387
|
+
selector,
|
|
33388
|
+
locator: `locator('${selector}')`
|
|
33389
|
+
}
|
|
33390
|
+
]);
|
|
33391
|
+
return candidates.length >= maxCandidates;
|
|
33392
|
+
}, "addCandidate");
|
|
33393
|
+
for (const attribute of Array.from(element.attributes).filter(
|
|
33394
|
+
({ name, value }) => value.length < 30 && !COVERED_ATTRIBUTES.includes(name)
|
|
33395
|
+
)) {
|
|
33396
|
+
try {
|
|
33397
|
+
const selector = `[${attribute.name}${attribute.value ? `="${attribute.value}"` : ""}]`;
|
|
33398
|
+
if (element.ownerDocument.querySelectorAll(selector).length === 1) {
|
|
33399
|
+
if (addCandidate(selector)) {
|
|
33400
|
+
return candidates;
|
|
33401
|
+
}
|
|
33402
|
+
}
|
|
33403
|
+
} catch (error) {
|
|
33404
|
+
}
|
|
33405
|
+
}
|
|
33406
|
+
for (const className of Array.from(element.classList).filter(
|
|
33407
|
+
(cls) => !CLASS_IGNORE_LIST.includes(cls)
|
|
33408
|
+
)) {
|
|
33409
|
+
try {
|
|
33410
|
+
const selector = `.${escapeSelector(className)}`;
|
|
33411
|
+
if (element.ownerDocument.querySelectorAll(selector).length === 1) {
|
|
33412
|
+
if (addCandidate(selector)) {
|
|
33413
|
+
return candidates;
|
|
33414
|
+
}
|
|
33415
|
+
}
|
|
33416
|
+
} catch (error) {
|
|
33417
|
+
}
|
|
33418
|
+
}
|
|
33419
|
+
return candidates;
|
|
33317
33420
|
}
|
|
33318
|
-
getInnerFeaturesLocator(element
|
|
33421
|
+
getInnerFeaturesLocator(element) {
|
|
33319
33422
|
const tag = element.tagName.toLowerCase();
|
|
33320
33423
|
if (!isInstanceOfHTMLElement(element) || ["html", "body", "div"].includes(tag)) {
|
|
33321
33424
|
return;
|
|
33322
33425
|
}
|
|
33323
|
-
const innerFeatures = new InnerFeaturesExtractor().extract(element).filter((f2) => !f2.isRoot).slice(0, 4).map((feature) => getFeatureSelector(feature));
|
|
33426
|
+
const innerFeatures = new InnerFeaturesExtractor().extract(element).filter((f2) => !f2.isRoot).slice(0, 4).map((feature) => getFeatureSelector(feature).pop());
|
|
33324
33427
|
if (innerFeatures.length < 2) {
|
|
33325
33428
|
return;
|
|
33326
33429
|
}
|
|
33327
|
-
|
|
33430
|
+
const selector = tag + innerFeatures.map((feature) => `:has(${feature})`).join("");
|
|
33431
|
+
return {
|
|
33432
|
+
selector,
|
|
33433
|
+
locator: `locator('${tag}', {has: page.locator('${selector}')})`
|
|
33434
|
+
};
|
|
33328
33435
|
}
|
|
33329
33436
|
getByRoleLocator(element, options = {}) {
|
|
33330
33437
|
try {
|
|
33331
33438
|
if (!isInstanceOfHTMLElement(element)) {
|
|
33332
33439
|
throw new Error("Provided element is not an HTMLElement");
|
|
33333
33440
|
}
|
|
33441
|
+
const usedFilters = {};
|
|
33334
33442
|
const roleMappings = {
|
|
33335
33443
|
button: "button",
|
|
33336
33444
|
input: /* @__PURE__ */ __name((el) => el.type === "checkbox" ? "checkbox" : el.type === "radio" ? "radio" : "textbox", "input"),
|
|
@@ -33348,7 +33456,8 @@
|
|
|
33348
33456
|
table: "table",
|
|
33349
33457
|
tr: "row",
|
|
33350
33458
|
th: "columnheader",
|
|
33351
|
-
td: "cell"
|
|
33459
|
+
td: "cell",
|
|
33460
|
+
img: "img"
|
|
33352
33461
|
};
|
|
33353
33462
|
const tagName = element.tagName.toLowerCase();
|
|
33354
33463
|
const role = element.getAttribute("role") || (roleMappings[tagName] instanceof Function ? roleMappings[tagName](element) : roleMappings[tagName]);
|
|
@@ -33375,22 +33484,35 @@
|
|
|
33375
33484
|
if (attributeVal) {
|
|
33376
33485
|
return normalize(attributeVal);
|
|
33377
33486
|
}
|
|
33378
|
-
const
|
|
33487
|
+
const injText = elementText(element);
|
|
33488
|
+
const text = normalizeWhiteSpace(injText.full);
|
|
33379
33489
|
if (!text?.length) {
|
|
33380
33490
|
return none;
|
|
33381
33491
|
}
|
|
33382
33492
|
const textVal = normalize(text);
|
|
33383
|
-
|
|
33384
|
-
return none;
|
|
33385
|
-
}
|
|
33386
|
-
this.usedFilters.hasText.push(textVal.text);
|
|
33493
|
+
usedFilters.hasText = textVal.text;
|
|
33387
33494
|
return textVal;
|
|
33388
33495
|
}, "getAccessibleName");
|
|
33389
33496
|
const { text: name, exact } = getAccessibleName();
|
|
33390
33497
|
if (name && name.length > 0 && name.length < 30) {
|
|
33391
33498
|
props.push(["name", escapeForAttributeSelector(name, exact)]);
|
|
33392
33499
|
}
|
|
33393
|
-
|
|
33500
|
+
const getSelection = /* @__PURE__ */ __name(() => {
|
|
33501
|
+
return {
|
|
33502
|
+
locator: `getByRole('${role}', { ${props.join(", ")} })`,
|
|
33503
|
+
selector: `internal:role=${role}${props.map(([n2, v2]) => `[${n2}=${v2}]`).join("")}`,
|
|
33504
|
+
options: usedFilters
|
|
33505
|
+
};
|
|
33506
|
+
}, "getSelection");
|
|
33507
|
+
let selection = getSelection();
|
|
33508
|
+
if (name?.length && exact && !this.getLocatorBase(element).locator(selection.selector).elements.includes(element)) {
|
|
33509
|
+
props.find((p) => p[0] === "name")[1] = escapeForAttributeSelector(
|
|
33510
|
+
name,
|
|
33511
|
+
false
|
|
33512
|
+
);
|
|
33513
|
+
selection = getSelection();
|
|
33514
|
+
}
|
|
33515
|
+
return selection;
|
|
33394
33516
|
} catch (error) {
|
|
33395
33517
|
console.error("Error getting role locator", error.message);
|
|
33396
33518
|
}
|
|
@@ -33404,20 +33526,36 @@
|
|
|
33404
33526
|
if (!text?.length || text.length > 100) {
|
|
33405
33527
|
return;
|
|
33406
33528
|
}
|
|
33529
|
+
const usedFilters = {};
|
|
33407
33530
|
if (Array.from(element.children).filter((el) => isInstanceOfHTMLElement(el)).some(
|
|
33408
33531
|
(child) => normalizeWhiteSpace(elementText(child).full) === text
|
|
33409
33532
|
)) {
|
|
33410
|
-
|
|
33411
|
-
|
|
33412
|
-
|
|
33413
|
-
|
|
33414
|
-
|
|
33415
|
-
|
|
33416
|
-
|
|
33417
|
-
|
|
33533
|
+
usedFilters.hasText = text;
|
|
33534
|
+
const selector = `${element.tagName.toLowerCase()} >> internal:has-text=${escapeForTextSelector(
|
|
33535
|
+
text,
|
|
33536
|
+
false
|
|
33537
|
+
)}`;
|
|
33538
|
+
return {
|
|
33539
|
+
selector,
|
|
33540
|
+
locator: this.getSelectorLocator(selector),
|
|
33541
|
+
options: usedFilters
|
|
33542
|
+
};
|
|
33543
|
+
}
|
|
33544
|
+
const getSelection = /* @__PURE__ */ __name((exact) => {
|
|
33545
|
+
const escapedText = escapeForTextSelector(text, exact);
|
|
33546
|
+
const selector = "internal:text=" + escapedText;
|
|
33547
|
+
return {
|
|
33548
|
+
selector,
|
|
33549
|
+
locator: this.getSelectorLocator(selector),
|
|
33550
|
+
options: usedFilters
|
|
33551
|
+
};
|
|
33552
|
+
}, "getSelection");
|
|
33553
|
+
let selection = getSelection(options.exact);
|
|
33554
|
+
if (options.exact && !this.getLocatorBase(element).locator(selection.selector).elements.includes(element)) {
|
|
33555
|
+
selection = getSelection(false);
|
|
33556
|
+
4;
|
|
33418
33557
|
}
|
|
33419
|
-
|
|
33420
|
-
return options.returnLocator ? `getByText('${escapedText}')` : "internal:text=" + escapedText;
|
|
33558
|
+
return selection;
|
|
33421
33559
|
} catch (error) {
|
|
33422
33560
|
}
|
|
33423
33561
|
}
|
|
@@ -33430,7 +33568,13 @@
|
|
|
33430
33568
|
return;
|
|
33431
33569
|
}
|
|
33432
33570
|
const respond = /* @__PURE__ */ __name((label) => {
|
|
33433
|
-
return
|
|
33571
|
+
return {
|
|
33572
|
+
selector: `internal:label=${escapeForTextSelector(
|
|
33573
|
+
label,
|
|
33574
|
+
options.exact
|
|
33575
|
+
)}`,
|
|
33576
|
+
locator: `getByLabel('${label}')`
|
|
33577
|
+
};
|
|
33434
33578
|
}, "respond");
|
|
33435
33579
|
const ariaLabel = element.getAttribute("aria-label");
|
|
33436
33580
|
if (ariaLabel) {
|
|
@@ -33485,10 +33629,13 @@
|
|
|
33485
33629
|
}
|
|
33486
33630
|
const attributeVal = element.getAttribute(attribute);
|
|
33487
33631
|
if (attributeVal) {
|
|
33488
|
-
return
|
|
33489
|
-
|
|
33490
|
-
|
|
33491
|
-
|
|
33632
|
+
return {
|
|
33633
|
+
selector: `internal:attr=[${attribute}=${escapeForAttributeSelector(
|
|
33634
|
+
attributeVal,
|
|
33635
|
+
options.exact
|
|
33636
|
+
)}]`,
|
|
33637
|
+
locator: `getBy${attribute.charAt(0).toUpperCase() + attribute.slice(1)}('${attributeVal}')`
|
|
33638
|
+
};
|
|
33492
33639
|
}
|
|
33493
33640
|
} catch (error) {
|
|
33494
33641
|
}
|
|
@@ -34050,7 +34197,10 @@
|
|
|
34050
34197
|
static {
|
|
34051
34198
|
__name(this, "ParentChainReducer");
|
|
34052
34199
|
}
|
|
34053
|
-
async reduce(element, options = {
|
|
34200
|
+
async reduce(element, options = {
|
|
34201
|
+
useLionTail: true,
|
|
34202
|
+
useChecksumIds: true
|
|
34203
|
+
}) {
|
|
34054
34204
|
console.log("[ParentChainReducer] will reduce", element);
|
|
34055
34205
|
if (!isNodeInstanceOf(element, "HTMLElement") && !isNodeInstanceOf(element, "SVGElement")) {
|
|
34056
34206
|
throw new Error("Element is not an HTMLElement");
|
|
@@ -34073,7 +34223,7 @@
|
|
|
34073
34223
|
* @param useLionTail whether to use LionTail to get the full dom tree close to the edge
|
|
34074
34224
|
* @returns string
|
|
34075
34225
|
*/
|
|
34076
|
-
getAncestorsHTMLSnippet(element, { useLionTail = true } = {}) {
|
|
34226
|
+
getAncestorsHTMLSnippet(element, { useLionTail = true, useChecksumIds = true } = {}) {
|
|
34077
34227
|
this.elementsInSimplifiedDomByChecksumId = {};
|
|
34078
34228
|
let lionTailMaxOuterHTMLLength = this.MAX_LIONTAIL_SIZE;
|
|
34079
34229
|
let attributeMaxSize = this.MAX_ATTRIBUTE_SIZE;
|
|
@@ -34104,11 +34254,15 @@
|
|
|
34104
34254
|
do {
|
|
34105
34255
|
rootClone = ancestors[0].cloneNode(false);
|
|
34106
34256
|
let currentClone = rootClone;
|
|
34257
|
+
const rootCloneSerialized = serializeElement(rootClone);
|
|
34107
34258
|
for (let i2 = 1; i2 < ancestors.length; i2++) {
|
|
34108
34259
|
if (useLionTail) {
|
|
34109
34260
|
const lionTailSize = serializeElement(ancestors[i2]).length;
|
|
34110
|
-
if (lionTailSize <= lionTailMaxOuterHTMLLength &&
|
|
34111
|
-
let newClone2 = this.deepCloneNode(
|
|
34261
|
+
if (lionTailSize <= lionTailMaxOuterHTMLLength && rootCloneSerialized.length + lionTailSize <= this.MAX_SNIPPET_SIZE) {
|
|
34262
|
+
let newClone2 = this.deepCloneNode(
|
|
34263
|
+
ancestors[i2],
|
|
34264
|
+
useChecksumIds ? counter : void 0
|
|
34265
|
+
);
|
|
34112
34266
|
currentClone.appendChild(newClone2);
|
|
34113
34267
|
break;
|
|
34114
34268
|
}
|
|
@@ -34116,9 +34270,11 @@
|
|
|
34116
34270
|
let newClone = normalizeNode(
|
|
34117
34271
|
ancestors[i2].cloneNode(ancestors[i2] === element)
|
|
34118
34272
|
);
|
|
34119
|
-
|
|
34120
|
-
|
|
34121
|
-
|
|
34273
|
+
if (useChecksumIds) {
|
|
34274
|
+
const id = counter.getAndIncrement();
|
|
34275
|
+
newClone.setAttribute("checksumid", id);
|
|
34276
|
+
this.elementsInSimplifiedDomByChecksumId[id] = ancestors[i2];
|
|
34277
|
+
}
|
|
34122
34278
|
currentClone.appendChild(newClone);
|
|
34123
34279
|
currentClone = newClone;
|
|
34124
34280
|
}
|
|
@@ -34130,7 +34286,9 @@
|
|
|
34130
34286
|
} while (rootClone.outerHTML.length > this.MAX_SNIPPET_SIZE && attributeMaxSize > 0);
|
|
34131
34287
|
rootClone.querySelectorAll("*").forEach((el) => {
|
|
34132
34288
|
});
|
|
34133
|
-
let htmlSnippet = serializeElement(rootClone
|
|
34289
|
+
let htmlSnippet = serializeElement(rootClone, {
|
|
34290
|
+
limitAttributeLength: true
|
|
34291
|
+
});
|
|
34134
34292
|
return htmlSnippet;
|
|
34135
34293
|
} catch (e2) {
|
|
34136
34294
|
console.error("Error while getting ancestors HTML snippet", e2);
|
|
@@ -34153,7 +34311,7 @@
|
|
|
34153
34311
|
const child = currentNode.childNodes[i2];
|
|
34154
34312
|
const childClone = child.cloneNode(false);
|
|
34155
34313
|
currentClone.appendChild(childClone);
|
|
34156
|
-
if (isNodeInstanceOf(child, "HTMLElement")) {
|
|
34314
|
+
if (counter && isNodeInstanceOf(child, "HTMLElement")) {
|
|
34157
34315
|
const id = counter.getAndIncrement();
|
|
34158
34316
|
childClone.setAttribute("checksumid", id);
|
|
34159
34317
|
this.elementsInSimplifiedDomByChecksumId[id] = child;
|
|
@@ -34417,7 +34575,6 @@
|
|
|
34417
34575
|
},
|
|
34418
34576
|
"*"
|
|
34419
34577
|
);
|
|
34420
|
-
console.log("selected", this.selected);
|
|
34421
34578
|
}, "onClick");
|
|
34422
34579
|
this.handleSubDocument = /* @__PURE__ */ __name((newRootDocument, defaultView) => {
|
|
34423
34580
|
if (this.subDocumentInspector) {
|
|
@@ -34716,14 +34873,14 @@ ${data.locator}`
|
|
|
34716
34873
|
* Tests a selector for a given element.
|
|
34717
34874
|
*/
|
|
34718
34875
|
testElementSelector(selector, element, testVariables, testUniqueness = true) {
|
|
34719
|
-
|
|
34720
|
-
|
|
34721
|
-
|
|
34722
|
-
|
|
34876
|
+
const testElement = typeof element === "string" ? this.htmlReducer.getElementForChecksumId(element) : element;
|
|
34877
|
+
if (!testElement) {
|
|
34878
|
+
console.error("Element not found", element);
|
|
34879
|
+
return { valid: false, error: "Element not found" };
|
|
34723
34880
|
}
|
|
34724
34881
|
return new PlaywrightCustomLocatorGenerator().testSelector(
|
|
34725
34882
|
selector,
|
|
34726
|
-
|
|
34883
|
+
testElement,
|
|
34727
34884
|
testVariables,
|
|
34728
34885
|
testUniqueness
|
|
34729
34886
|
);
|
|
@@ -34735,16 +34892,18 @@ ${data.locator}`
|
|
|
34735
34892
|
* @param useLionTail whether to use the lion tail for the parent chain.
|
|
34736
34893
|
* Lion tail means that we include the closest HTML surrounding of the element
|
|
34737
34894
|
* to add more context to the parent chain and the target element.
|
|
34895
|
+
* @param useChecksumIds whether to use checksumIds in the reduced HTML
|
|
34738
34896
|
* @returns
|
|
34739
34897
|
*/
|
|
34740
|
-
async getParentChainHTML(element, useLionTail = true) {
|
|
34898
|
+
async getParentChainHTML(element, useLionTail = true, useChecksumIds = true) {
|
|
34741
34899
|
if (typeof element === "string") {
|
|
34742
34900
|
element = this.htmlReducer.getElementForChecksumId(
|
|
34743
34901
|
element
|
|
34744
34902
|
);
|
|
34745
34903
|
}
|
|
34746
34904
|
const response = await this.parentChainReducer.reduce(element, {
|
|
34747
|
-
useLionTail
|
|
34905
|
+
useLionTail,
|
|
34906
|
+
useChecksumIds
|
|
34748
34907
|
});
|
|
34749
34908
|
return response.reducedHTML;
|
|
34750
34909
|
}
|