@luminati-io/webdriverio8
Version:
Next-gen browser and mobile automation test framework for Node.js
328 lines • 26.2 kB
JavaScript
import fs from 'node:fs';
import { roleElements } from 'aria-query';
import { DEEP_SELECTOR, ARIA_SELECTOR } from '../constants.js';
const DEFAULT_STRATEGY = 'css selector';
const DIRECT_SELECTOR_REGEXP = /^(id|css selector|xpath|link text|partial link text|name|tag name|class name|-android uiautomator|-android datamatcher|-android viewmatcher|-android viewtag|-ios uiautomation|-ios predicate string|-ios class chain|accessibility id):(.+)/;
const XPATH_SELECTORS_START = [
'/', '(', '../', './', '*/'
];
const NAME_MOBILE_SELECTORS_START = [
'uia', 'xcuielementtype', 'android.widget', 'cyi', 'android.view'
];
const XPATH_SELECTOR_REGEXP = [
// HTML tag
/^([a-z0-9|-]*)/,
// optional . or # + class or id
/(?:(\.|#)(-?[_a-zA-Z]+[_a-zA-Z0-9-]*))?/,
// optional [attribute-name="attribute-selector"]
/(?:\[(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)(?:=(?:"|')([a-zA-z0-9\-_. ]+)(?:"|'))?\])?/,
// optional case insensitive
/(\.)?/,
// *=query or =query
/(\*)?=(.+)$/,
];
const IMAGEPATH_MOBILE_SELECTORS_ENDSWITH = [
'.jpg', '.jpeg', '.gif', '.png', '.bmp', '.svg'
];
const defineStrategy = function (selector) {
// Condition with checking isPlainObject(selector) should be first because
// in case of "selector" argument is a plain object then .match() will cause
// an error like "selector.match is not a function"
// Use '-android datamatcher' or '-android viewmatcher' strategy if selector is a plain object (Android only)
if (typeof selector === 'object') {
if (JSON.stringify(selector).indexOf('test.espresso.matcher.ViewMatchers') < 0) {
return '-android datamatcher';
}
return '-android viewmatcher';
}
const stringSelector = selector;
// Check if user has specified locator strategy directly
if (stringSelector.match(DIRECT_SELECTOR_REGEXP)) {
return 'directly';
}
// Use appium image strategy if selector ends with certain text(.jpg,.gif..)
if (IMAGEPATH_MOBILE_SELECTORS_ENDSWITH.some(path => {
const selector = stringSelector.toLowerCase();
return selector.endsWith(path) && selector !== path;
})) {
return '-image';
}
// Use xPath strategy if selector starts with //
if (XPATH_SELECTORS_START.some(option => stringSelector.startsWith(option))) {
return 'xpath';
}
// Use link text strategy if selector starts with =
if (stringSelector.startsWith('=')) {
return 'link text';
}
// Use partial link text strategy if selector starts with *=
if (stringSelector.startsWith('*=')) {
return 'partial link text';
}
// Use id strategy if the selector starts with id=
if (stringSelector.startsWith('id=')) {
return 'id';
}
// use shadow dom selector
if (stringSelector.startsWith(DEEP_SELECTOR)) {
return 'shadow';
}
// use aria selector
if (stringSelector.startsWith(ARIA_SELECTOR)) {
return 'aria';
}
// Recursive element search using the UiAutomator library (Android only)
if (stringSelector.startsWith('android=')) {
return '-android uiautomator';
}
// Recursive element search using the UIAutomation library (iOS-only)
if (stringSelector.startsWith('ios=')) {
return '-ios uiautomation';
}
// Recursive element search using accessibility id
if (stringSelector.startsWith('~')) {
return 'accessibility id';
}
// Class name mobile selector
// for iOS = UIA...
// for Android = android.widget
if (NAME_MOBILE_SELECTORS_START.some(option => stringSelector.toLowerCase().startsWith(option))) {
return 'class name';
}
// Use tag name strategy if selector contains a tag
// e.g. "<div>" or "<div />"
if (stringSelector.search(/<[0-9a-zA-Z-]+( \/)*>/g) >= 0) {
return 'tag name';
}
// Use name strategy if selector queries elements with name attributes for JSONWP
// or if isMobile is used even when w3c is used
// e.g. "[name='myName']" or '[name="myName"]'
if (stringSelector.search(/^\[name=(?:"(.[^"]*)"|'(.[^']*)')]$/) >= 0) {
return 'name';
}
// Allow to move up to the parent or select current element
if (selector === '..' || selector === '.') {
return 'xpath';
}
// Any element with given class, id, or attribute and content
// e.g. h1.header=Welcome or [data-name=table-row]=Item or #content*=Intro
if (stringSelector.match(new RegExp(XPATH_SELECTOR_REGEXP.map(rx => rx.source).join('')))) {
return 'xpath extended';
}
if (stringSelector.match(/^\[role=[A-Za-z]+]$/)) {
return 'role';
}
};
export const findStrategy = function (selector, isW3C, isMobile) {
const stringSelector = selector;
let using = DEFAULT_STRATEGY;
let value = selector;
switch (defineStrategy(selector)) {
// user has specified locator strategy directly
case 'directly': {
const match = stringSelector.match(DIRECT_SELECTOR_REGEXP);
if (!match) {
throw new Error('InvalidSelectorStrategy'); // ToDo: move error to wdio-error package
}
using = match[1];
value = match[2];
break;
}
case 'xpath': {
using = 'xpath';
break;
}
case 'id': {
using = 'id';
value = stringSelector.slice(3);
break;
}
case 'link text': {
using = 'link text';
value = stringSelector.slice(1);
break;
}
case 'partial link text': {
using = 'partial link text';
value = stringSelector.slice(2);
break;
}
case 'shadow':
using = 'shadow';
value = stringSelector.slice(DEEP_SELECTOR.length);
break;
case 'aria': {
const label = stringSelector.slice(ARIA_SELECTOR.length);
const conditions = [
// aria label is recevied by other element with aria-labelledBy
// https://www.w3.org/TR/accname-1.1/#step2B
`.//*[@aria-labelledby=(//*[normalize-space(text()) = "${label}"]/@id)]`,
// aria label is recevied by other element with aria-labelledBy
// https://www.w3.org/TR/accname-1.1/#step2B
`.//*[@aria-describedby=(//*[normalize-space(text()) = "${label}"]/@id)]`,
// element has direct aria label
// https://www.w3.org/TR/accname-1.1/#step2C
`.//*[@aria-label = "${label}"]`,
// input and textarea with a label
// https://www.w3.org/TR/accname-1.1/#step2D
`.//input[@id = (//label[normalize-space() = "${label}"]/@for)]`,
`.//textarea[@id = (//label[normalize-space() = "${label}"]/@for)]`,
// input and textarea with a label as parent
// https://www.w3.org/TR/accname-1.1/#step2D
`.//input[//ancestor::label[normalize-space(text()) = "${label}"]]`,
`.//textarea[//ancestor::label[normalize-space(text()) = "${label}"]]`,
// aria label is received by a placeholder
// https://www.w3.org/TR/accname-1.1/#step2D
`.//input[@placeholder="${label}"]`,
`.//textarea[@placeholder="${label}"]`,
// aria label is received by a aria-placeholder
// https://www.w3.org/TR/accname-1.1/#step2D
`.//input[@aria-placeholder="${label}"]`,
`.//textarea[@aria-placeholder="${label}"]`,
// aria label is received by a title
// https://www.w3.org/TR/accname-1.1/#step2D
`.//*[not(self::label)][@title="${label}"]`,
// images with an alt tag
// https://www.w3.org/TR/accname-1.1/#step2D
`.//img[@alt="${label}"]`,
// aria label is received from element text content
// https://www.w3.org/TR/accname-1.1/#step2G
`.//*[not(self::label)][normalize-space(text()) = "${label}"]`
];
using = 'xpath';
value = conditions.join(' | ');
break;
}
case '-android uiautomator': {
using = '-android uiautomator';
value = stringSelector.slice(8);
break;
}
case '-android datamatcher': {
using = '-android datamatcher';
value = JSON.stringify(value);
break;
}
case '-android viewmatcher': {
using = '-android viewmatcher';
value = JSON.stringify(value);
break;
}
case '-ios uiautomation': {
using = '-ios uiautomation';
value = stringSelector.slice(4);
break;
}
case 'accessibility id': {
using = 'accessibility id';
value = stringSelector.slice(1);
break;
}
case 'class name': {
using = 'class name';
break;
}
case 'tag name': {
using = 'tag name';
value = stringSelector.replace(/<|>|\/|\s/g, '');
break;
}
case 'name': {
if (isMobile || !isW3C) {
const match = stringSelector.match(/^\[name=(?:"(.[^"]*)"|'(.[^']*)')]$/);
if (!match) {
throw new Error(`InvalidSelectorMatch. Strategy 'name' has failed to match '${stringSelector}'`);
}
using = 'name';
value = match[1] || match[2];
}
break;
}
case 'xpath extended': {
using = 'xpath';
const match = stringSelector.match(new RegExp(XPATH_SELECTOR_REGEXP.map(rx => rx.source).join('')));
if (!match) {
throw new Error(`InvalidSelectorMatch: Strategy 'xpath extended' has failed to match '${stringSelector}'`);
}
const PREFIX_NAME = { '.': 'class', '#': 'id' };
const conditions = [];
const [tag, prefix, name, attrName, attrValue, insensitive, partial, query] = match.slice(1);
if (prefix) {
if (prefix === '.') {
// trick to match a class name exactly
conditions.push(`contains(concat(" ",@${PREFIX_NAME[prefix]}," "), " ${name} ")`);
}
else {
conditions.push(`contains(@${PREFIX_NAME[prefix]}, "${name}")`);
}
}
if (attrName) {
conditions.push(attrValue
? `contains(@${attrName}, "${attrValue}")`
: `@${attrName}`);
}
const partialNot = ` and not(${`.//${tag || '*'}${conditions.length ? `[${conditions.join(' and ')}]` : ''}`})`;
if (insensitive) {
conditions.push(partial
? `contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "${query.toLowerCase()}")${partialNot}`
: `normalize-space(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")) = "${query.toLowerCase()}"`);
}
else {
conditions.push(partial ? `contains(., "${query}")${partialNot}` : `normalize-space(text()) = "${query}"`);
}
const getValue = () => `.//${tag || '*'}[${conditions.join(' and ')}]`;
value = getValue();
if (!partial) {
conditions.pop();
conditions.push(`not(${value})`, `normalize-space() = "${insensitive ? query.toLowerCase() : query}"`);
value = value + ' | ' + getValue();
}
break;
}
case '-image': {
using = '-image';
value = fs.readFileSync(stringSelector, { encoding: 'base64' });
break;
}
case 'role': {
const match = stringSelector.match(/^\[role=(.+)\]/);
if (!match) {
throw new Error(`InvalidSelectorMatch. Strategy 'role' has failed to match '${stringSelector}'`);
}
using = 'css selector';
value = createRoleBaseXpathSelector(match[1]);
break;
}
}
return { using, value };
};
const createRoleBaseXpathSelector = (role) => {
const locatorArr = [];
roleElements.get(role)?.forEach((value) => {
let locator;
let tagAttribute, tagAttributevalue;
const tagname = value.name;
if (value.attributes instanceof Array) {
value.attributes.forEach((val) => {
tagAttribute = val.name;
tagAttributevalue = val.value;
});
}
if (!tagAttribute) {
locator = tagname;
}
else if (!tagAttributevalue) {
locator = `${tagname}[${tagAttribute}]`;
}
else {
locator = `${tagname}[${tagAttribute}="${tagAttributevalue}"]`;
}
locatorArr.push(locator);
});
let xpathLocator = `[role="${role}"]`;
locatorArr.forEach((loc) => {
xpathLocator += ',' + loc;
});
return xpathLocator;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmluZFN0cmF0ZWd5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2ZpbmRTdHJhdGVneS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFFeEIsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUV6QyxPQUFPLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBRTlELE1BQU0sZ0JBQWdCLEdBQUcsY0FBYyxDQUFBO0FBQ3ZDLE1BQU0sc0JBQXNCLEdBQUcsOE9BQThPLENBQUE7QUFDN1EsTUFBTSxxQkFBcUIsR0FBRztJQUMxQixHQUFHLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSTtDQUM5QixDQUFBO0FBQ0QsTUFBTSwyQkFBMkIsR0FBRztJQUNoQyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsZ0JBQWdCLEVBQUUsS0FBSyxFQUFFLGNBQWM7Q0FDcEUsQ0FBQTtBQUNELE1BQU0scUJBQXFCLEdBQUc7SUFDMUIsV0FBVztJQUNYLGdCQUFnQjtJQUNoQixnQ0FBZ0M7SUFDaEMseUNBQXlDO0lBQ3pDLGlEQUFpRDtJQUNqRCw4RUFBOEU7SUFDOUUsNEJBQTRCO0lBQzVCLE9BQU87SUFDUCxvQkFBb0I7SUFDcEIsYUFBYTtDQUNoQixDQUFBO0FBQ0QsTUFBTSxtQ0FBbUMsR0FBRztJQUN4QyxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU07Q0FDbEQsQ0FBQTtBQUlELE1BQU0sY0FBYyxHQUFHLFVBQVUsUUFBMEI7SUFDdkQsMEVBQTBFO0lBQzFFLDRFQUE0RTtJQUM1RSxtREFBbUQ7SUFDbkQsNkdBQTZHO0lBQzdHLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDL0IsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FB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