UNPKG

selenium-webdriver

Version:

The official WebDriver JavaScript bindings from the Selenium project

274 lines (273 loc) 11.8 kB
// GENERATED CODE - DO NOT EDIT module.exports = (function findElements(target, root) { const INVALID_SELECTOR = 'invalid selector'; const INVALID_ARGUMENT = 'invalid argument'; const NO_SUCH_ELEMENT = 'no such element'; function botError(code, message) { const err = new Error(message); err['code'] = code; return err; } function cssEscapeId(s) { return s.replace(/[\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]()]/g, (c) => '\\' + c); } function classNameMany(target, root) { if (!target) { throw botError(INVALID_SELECTOR, 'No class name specified'); } target = target.trim(); if (target.indexOf(' ') !== -1) { throw botError(INVALID_SELECTOR, 'Compound class names not permitted'); } try { return Array.from(root.querySelectorAll('.' + target.replace(/\\/g, '\\\\').replace(/\./g, '\\.'))); } catch (_e) { throw botError(INVALID_SELECTOR, 'An invalid or illegal class name was specified'); } } function cssMany(target, root) { try { return Array.from(root.querySelectorAll(target)); } catch (_e) { throw botError(INVALID_SELECTOR, 'An invalid or illegal CSS selector was specified: ' + target); } } function idMany(target, root) { if (!target) { return []; } if (!/^\d/.test(target)) { try { return Array.from(root.querySelectorAll('#' + cssEscapeId(target))); } catch (_e) { return []; } } return Array.from(root.querySelectorAll('*')).filter(el => el.getAttribute('id') === target); } function getLinkText(el) { const text = el.innerText !== undefined ? el.innerText : el.textContent || ''; return text.replace(/^[\s]+|[\s]+$/g, ''); } function linkTextMany(target, root, partial) { return Array.from(root.querySelectorAll('a')).filter(el => { const text = getLinkText(el); return partial ? text.indexOf(target) !== -1 : text === target; }); } function nameMany(target, root) { return Array.from(root.querySelectorAll('[name="' + target.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]')); } function tagNameMany(target, root) { if (target === '') { throw botError(INVALID_SELECTOR, 'Unable to locate an element with the tagName ""'); } return Array.from(root.getElementsByTagName(target)); } const DEFAULT_NS_RESOLVER = (function () { const ns = { svg: 'http://www.w3.org/2000/svg' }; return (prefix) => ns[prefix] || null; })(); const ORDERED_NODE_SNAPSHOT_TYPE = 7; function xpathMany(target, root) { const doc = root.documentElement ? root : root.ownerDocument; if (!doc.documentElement) { return []; } try { let resolver = null; if (target.indexOf(':') !== -1) { const reversedNs = {}; const allNodes = doc.getElementsByTagName('*'); for (let i = 0; i < allNodes.length; i++) { const n = allNodes[i]; const ns = n.namespaceURI; if (ns && !reversedNs[ns]) { let prefix = n.lookupPrefix(ns); if (!prefix) { const m = ns.match('.*/(\\w+)/?$'); prefix = m ? m[1] : 'xhtml'; } reversedNs[ns] = prefix; } } const namespaces = {}; for (const key in reversedNs) { namespaces[reversedNs[key]] = key; } resolver = (prefix) => namespaces[prefix || ''] || null; } let result = null; try { result = doc.evaluate(target, root, resolver, ORDERED_NODE_SNAPSHOT_TYPE, null); } catch (te) { if (te.name === 'TypeError') { const fallback = doc.createNSResolver ? doc.createNSResolver(doc.documentElement) : DEFAULT_NS_RESOLVER; result = doc.evaluate(target, root, fallback, ORDERED_NODE_SNAPSHOT_TYPE, null); } else { throw te; } } if (!result) { return []; } const results = []; for (let i = 0; i < result.snapshotLength; i++) { const node = result.snapshotItem(i); if (!node || node.nodeType !== Node.ELEMENT_NODE) { throw botError(INVALID_SELECTOR, 'The result of the xpath expression "' + target + '" is: ' + node + '. It should be an element.'); } results.push(node); } return results; } catch (ex) { if (ex.name === 'NS_ERROR_ILLEGAL_VALUE') { return []; } if (ex.code === INVALID_SELECTOR) { throw ex; } throw botError(INVALID_SELECTOR, 'Unable to locate an element with the xpath expression ' + target + ' because of the following error:\n' + ex); } } function getClientRect(element) { const r = element.getBoundingClientRect(); return { left: r.left, top: r.top, width: r.width, height: r.height }; } function resolveAnchor(selector) { if (selector instanceof Element) { return selector; } if (typeof selector === 'function') { return resolveAnchor(selector()); } if (selector && typeof selector === 'object') { const found = findElements(selector); if (!found.length) { throw botError(NO_SUCH_ELEMENT, 'No element has been found by ' + JSON.stringify(selector)); } return found[0]; } throw botError(INVALID_ARGUMENT, 'Selector is of wrong type: ' + JSON.stringify(selector)); } function makeProximityFilter(selector, test) { return (candidate) => test(getClientRect(resolveAnchor(selector)), getClientRect(candidate)); } const RELATIVE_STRATEGIES = { above: (sel) => makeProximityFilter(sel, (a, c) => c.top + c.height <= a.top), below: (sel) => makeProximityFilter(sel, (a, c) => c.top >= a.top + a.height), left: (sel) => makeProximityFilter(sel, (a, c) => c.left + c.width <= a.left), right: (sel) => makeProximityFilter(sel, (a, c) => c.left >= a.left + a.width), near: (sel, distArg) => { const distFromSelector = typeof sel['distance'] === 'number' ? sel['distance'] : 0; const distance = typeof distArg === 'number' && distArg > 0 ? distArg : distFromSelector || 50; return (candidate) => { const anchor = resolveAnchor(sel); if (anchor === candidate) return false; const a = getClientRect(anchor); const c = getClientRect(candidate); const expanded = { left: a.left - distance, top: a.top - distance, width: a.width + distance * 2, height: a.height + distance * 2 }; return (c.left < expanded.left + expanded.width && c.left + c.width > expanded.left && c.top < expanded.top + expanded.height && c.top + c.height > expanded.top); }; }, straightAbove: (sel) => makeProximityFilter(sel, (a, c) => c.left < a.left + a.width && c.left + c.width > a.left && c.top + c.height <= a.top), straightBelow: (sel) => makeProximityFilter(sel, (a, c) => c.left < a.left + a.width && c.left + c.width > a.left && c.top >= a.top + a.height), straightLeft: (sel) => makeProximityFilter(sel, (a, c) => c.top < a.top + a.height && c.top + c.height > a.top && c.left + c.width <= a.left), straightRight: (sel) => makeProximityFilter(sel, (a, c) => c.top < a.top + a.height && c.top + c.height > a.top && c.left >= a.left + a.width), }; function sortByProximity(anchor, elements) { const ar = getClientRect(anchor); const acx = ar.left + Math.max(1, ar.width) / 2; const acy = ar.top + Math.max(1, ar.height) / 2; return elements.slice().sort((a, b) => { const ra = getClientRect(a); const rb = getClientRect(b); const da = Math.sqrt(Math.pow(acx - (ra.left + Math.max(1, ra.width) / 2), 2) + Math.pow(acy - (ra.top + Math.max(1, ra.height) / 2), 2)); const db = Math.sqrt(Math.pow(acx - (rb.left + Math.max(1, rb.width) / 2), 2) + Math.pow(acy - (rb.top + Math.max(1, rb.height) / 2), 2)); return da - db; }); } function relativeMany(target, root) { if (!Object.prototype.hasOwnProperty.call(target, 'root') || !Object.prototype.hasOwnProperty.call(target, 'filters')) { throw botError(INVALID_ARGUMENT, 'Locator not suitable for relative locators: ' + JSON.stringify(target)); } const filters = target['filters']; if (!Array.isArray(filters)) { throw botError(INVALID_ARGUMENT, 'Targets should be an array: ' + JSON.stringify(target)); } let elements; const rootTarget = target['root']; if (rootTarget instanceof Element) { elements = [rootTarget]; } else { elements = findElements(rootTarget, root); } if (!elements.length) { return []; } const matched = elements.filter(el => { if (!el) return false; return filters.every(filter => { const strategy = RELATIVE_STRATEGIES[filter.kind]; if (!strategy) { throw botError(INVALID_ARGUMENT, 'Cannot find filter suitable for ' + filter.kind); } return strategy(...filter.args)(el); }); }); const finalFilter = filters[filters.length - 1]; if (!finalFilter || !RELATIVE_STRATEGIES[finalFilter.kind]) { return matched; } const lastAnchor = resolveAnchor(finalFilter.args[0]); return sortByProximity(lastAnchor, matched); } const actualRoot = root || document; const keys = Object.keys(target).filter(k => Object.prototype.hasOwnProperty.call(target, k)); if (!keys.length) { throw botError(INVALID_ARGUMENT, 'Unsupported locator strategy: (empty)'); } const key = keys[0]; const value = target[key]; switch (key) { case 'className': case 'class name': return classNameMany(value, actualRoot); case 'css': case 'css selector': return cssMany(value, actualRoot); case 'id': return idMany(value, actualRoot); case 'linkText': case 'link text': return linkTextMany(value, actualRoot, false); case 'partialLinkText': case 'partial link text': return linkTextMany(value, actualRoot, true); case 'name': return nameMany(value, actualRoot); case 'tagName': case 'tag name': return tagNameMany(value, actualRoot); case 'xpath': return xpathMany(value, actualRoot); case 'relative': return relativeMany(value, actualRoot); default: throw botError(INVALID_ARGUMENT, 'Unsupported locator strategy: ' + key); } });