UNPKG

@luminati-io/webdriverio8

Version:

Next-gen browser and mobile automation test framework for Node.js

453 lines 35.9 kB
import fs from 'node:fs/promises'; import path from 'node:path'; import { URL } from 'node:url'; import cssValue from 'css-value'; import rgb2hex from 'rgb2hex'; import GraphemeSplitter from 'grapheme-splitter'; import logger from '@wdio/logger'; import isPlainObject from 'is-plain-obj'; import { UNICODE_CHARACTERS } from '@wdio/utils'; import * as browserCommands from '../commands/browser.js'; import * as elementCommands from '../commands/element.js'; import querySelectorAllDeep from './thirdParty/querySelectorShadowDom.js'; import { ELEMENT_KEY, DEEP_SELECTOR, Key } from '../constants.js'; import { findStrategy } from './findStrategy.js'; const log = logger('webdriverio'); const INVALID_SELECTOR_ERROR = 'selector needs to be typeof `string` or `function`'; const IGNORED_COMMAND_FILE_EXPORTS = ['SESSION_MOCKS', 'CDP_SESSIONS']; const scopes = { browser: browserCommands, element: elementCommands }; const applyScopePrototype = (prototype, scope) => { Object.entries(scopes[scope]) .filter(([exportName]) => !IGNORED_COMMAND_FILE_EXPORTS.includes(exportName)) .forEach(([commandName, command]) => { prototype[commandName] = { value: command }; }); }; /** * enhances objects with element commands */ export const getPrototype = (scope) => { const prototype = { /** * used to store the puppeteer instance in the browser scope */ puppeteer: { value: null, writable: true } }; /** * register action commands */ applyScopePrototype(prototype, scope); prototype.strategies = { value: new Map() }; return prototype; }; /** * get element id from WebDriver response * @param {?Object|undefined} res body object from response or null * @return {?string} element id or null if element couldn't be found */ export const getElementFromResponse = (res) => { /** * a function selector can return null */ if (!res) { return null; } /** * deprecated JSONWireProtocol response */ if (res.ELEMENT) { return res.ELEMENT; } /** * W3C WebDriver response */ if (res[ELEMENT_KEY]) { return res[ELEMENT_KEY]; } return null; }; /** * traverse up the scope chain until browser element was reached */ export function getBrowserObject(elem) { const elemObject = elem; return elemObject.parent ? getBrowserObject(elemObject.parent) : elem; } function sanitizeCSS(value) { /* istanbul ignore next */ if (!value) { return value; } return value.trim().replace(/'/g, '').replace(/"/g, '').toLowerCase(); } /** * parse css values to a better format * @param {string} cssPropertyValue result of WebDriver call * @param {string} cssProperty name of css property to parse * @return {object} parsed css property */ export function parseCSS(cssPropertyValue, cssProperty) { const parsedValue = { property: cssProperty, value: cssPropertyValue.toLowerCase().trim(), parsed: {} }; if (parsedValue.value?.indexOf('rgb') === 0) { /** * remove whitespaces in rgb values */ parsedValue.value = parsedValue.value.replace(/\s/g, ''); /** * parse color values */ const color = parsedValue.value; parsedValue.parsed = rgb2hex(parsedValue.value); parsedValue.parsed.type = 'color'; const colorType = /[rgba]+/g.exec(color) || []; parsedValue.parsed[colorType[0]] = color; } else if (parsedValue.property === 'font-family') { const font = cssValue(cssPropertyValue); const string = parsedValue.value; const value = cssPropertyValue.split(/,/).map(sanitizeCSS); parsedValue.value = sanitizeCSS(font[0].value || font[0].string); parsedValue.parsed = { value, type: 'font', string }; } else { /** * parse other css properties */ try { const value = cssValue(cssPropertyValue); if (value.length === 1) { parsedValue.parsed = value[0]; } if (parsedValue.parsed.type && parsedValue.parsed.type === 'number' && parsedValue.parsed.unit === '') { parsedValue.value = parsedValue.parsed.value; } } catch (err) { // TODO improve css-parse lib to handle properties like // `-webkit-animation-timing-function : cubic-bezier(0.25, 0.1, 0.25, 1) } } return parsedValue; } /** * check for unicode character or split string into literals * @param {string} value text * @return {Array} set of characters or unicode symbols */ export function checkUnicode(value, isDevTools = false) { /** * "Ctrl" key is specially handled based on OS in action class */ if (value === Key.Ctrl) { return [value]; } /** * when sending emoji characters like 😄 or a value that is not a special character defined * by the WebDriver protocol */ if (!Object.prototype.hasOwnProperty.call(UNICODE_CHARACTERS, value)) { return new GraphemeSplitter().splitGraphemes(value); } if (isDevTools) { return [value]; } return [UNICODE_CHARACTERS[value]]; } function fetchElementByJSFunction(selector, scope, referenceId) { if (!('elementId' in scope)) { return scope.execute(selector, referenceId); } /** * use a regular function because IE does not understand arrow functions */ const script = (function (elem, id) { return selector.call(elem, id); }).toString().replace('selector', `(${selector.toString()})`); const args = [scope]; if (referenceId) { args.push(referenceId); } return getBrowserObject(scope).execute(`return (${script}).apply(null, arguments)`, ...args); } export function isElement(o) { return (typeof HTMLElement === 'object' ? o instanceof HTMLElement : o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'); } /** * logic to find an element */ export async function findElement(selector) { const browserObject = getBrowserObject(this); /** * check if shadow DOM integration is used */ if (!this.isDevTools && typeof selector === 'string' && selector.startsWith(DEEP_SELECTOR)) { const notFoundError = new Error(`shadow selector "${selector.slice(DEEP_SELECTOR.length)}" did not return an HTMLElement`); let elem = await browserObject.execute(`return (${querySelectorAllDeep}).apply(null, arguments)`, false, selector.slice(DEEP_SELECTOR.length), // hard conversion from element id to Element is done by browser driver (this.elementId ? this : undefined)); elem = Array.isArray(elem) ? elem[0] : elem; return getElementFromResponse(elem) ? elem : notFoundError; } /** * fetch element using custom strategy function */ if (selector && typeof selector === 'object' && typeof selector.strategy === 'function') { const { strategy, strategyName, strategyArguments } = selector; const notFoundError = new Error(`Custom Strategy "${strategyName}" did not return an HTMLElement`); let elem = await browserObject.execute(strategy, ...strategyArguments); elem = Array.isArray(elem) ? elem[0] : elem; return getElementFromResponse(elem) ? elem : notFoundError; } /** * fetch element using regular protocol command */ if (typeof selector === 'string' || isPlainObject(selector)) { const { using, value } = findStrategy(selector, this.isW3C, this.isMobile); return this.elementId // casting to any necessary given weak type support of protocol commands ? this.findElementFromElement(this.elementId, using, value) : this.findElement(using, value); } /** * fetch element with JS function */ if (typeof selector === 'function') { const notFoundError = new Error(`Function selector "${selector.toString()}" did not return an HTMLElement`); let elem = await fetchElementByJSFunction(selector, this); elem = Array.isArray(elem) ? elem[0] : elem; return getElementFromResponse(elem) ? elem : notFoundError; } /** * handle DOM element transformation * Note: this runs in the browser */ if (isElement(selector)) { if (!window.__wdio_element) { window.__wdio_element = {}; } const notFoundError = new Error('DOM Node couldn\'t be found anymore'); const uid = Math.random().toString().slice(2); window.__wdio_element[uid] = selector; selector = ((id) => window.__wdio_element[id]); let elem = await fetchElementByJSFunction(selector, this, uid); elem = Array.isArray(elem) ? elem[0] : elem; return getElementFromResponse(elem) ? elem : notFoundError; } throw new Error(INVALID_SELECTOR_ERROR); } /** * logic to find a elements */ export async function findElements(selector) { const browserObject = getBrowserObject(this); /** * check if shadow DOM integration is used */ if (!this.isDevTools && typeof selector === 'string' && selector.startsWith(DEEP_SELECTOR)) { const elems = await browserObject.execute(`return (${querySelectorAllDeep}).apply(null, arguments)`, true, selector.slice(DEEP_SELECTOR.length), // hard conversion from element id to Element is done by browser driver (this.elementId ? this : undefined)); const elemArray = Array.isArray(elems) ? elems : [elems]; return elemArray.filter((elem) => elem && getElementFromResponse(elem)); } /** * fetch elements using custom strategy function */ if (isPlainObject(selector) && typeof selector.strategy === 'function') { const { strategy, strategyArguments } = selector; const elems = await browserObject.execute(strategy, ...strategyArguments); const elemArray = Array.isArray(elems) ? elems : [elems]; return elemArray.filter((elem) => elem && getElementFromResponse(elem)); } /** * fetch element using regular protocol command */ if (typeof selector === 'string' || isPlainObject(selector)) { const { using, value } = findStrategy(selector, this.isW3C, this.isMobile); return this.elementId // casting to any necessary given weak type support of protocol commands ? this.findElementsFromElement(this.elementId, using, value) : this.findElements(using, value); } /** * fetch element with JS function */ if (typeof selector === 'function') { const elems = await fetchElementByJSFunction(selector, this); const elemArray = Array.isArray(elems) ? elems : [elems]; return elemArray.filter((elem) => elem && getElementFromResponse(elem)); } throw new Error(INVALID_SELECTOR_ERROR); } /** * Strip element object and return w3c and jsonwp compatible keys */ export function verifyArgsAndStripIfElement(args) { function verify(arg) { if (arg && typeof arg === 'object' && arg.constructor.name === 'Element') { const elem = arg; if (!elem.elementId) { throw new Error(`The element with selector "${elem.selector}" you are trying to pass into the execute method wasn't found`); } return { [ELEMENT_KEY]: elem.elementId, ELEMENT: elem.elementId }; } return arg; } return !Array.isArray(args) ? verify(args) : args.map(verify); } /** * getElementRect */ export async function getElementRect(scope) { const rect = await scope.getElementRect(scope.elementId); const defaults = { x: 0, y: 0, width: 0, height: 0 }; /** * getElementRect workaround for Safari 12.0.3 * if one of [x, y, height, width] is undefined get rect with javascript */ if (Object.keys(defaults).some((key) => rect[key] === undefined)) { /* istanbul ignore next */ const rectJs = await getBrowserObject(scope).execute(function (el) { if (!el || !el.getBoundingClientRect) { return; } const { left, top, width, height } = el.getBoundingClientRect(); return { x: left + this.scrollX, y: top + this.scrollY, width, height }; }, scope); // try set proper value Object.keys(defaults).forEach((key) => { if (typeof rect[key] !== 'undefined') { return; } if (rectJs && typeof rectJs[key] === 'number') { rect[key] = Math.floor(rectJs[key]); } else { log.error('getElementRect', { rect, rectJs, key }); throw new Error('Failed to receive element rects via execute command'); } }); } return rect; } export function getAbsoluteFilepath(filepath) { return filepath.startsWith('/') || filepath.startsWith('\\') || filepath.match(/^[a-zA-Z]:\\/) ? filepath : path.join(process.cwd(), filepath); } /** * check if directory exists */ export async function assertDirectoryExists(filepath) { const exist = await fs.access(path.dirname(filepath)).then(() => true, () => false); if (!exist) { throw new Error(`directory (${path.dirname(filepath)}) doesn't exist`); } } /** * check if urls are valid and fix them if necessary * @param {string} url url to navigate to * @param {Boolean} [retryCheck=false] true if an url was already check and still failed with fix applied * @return {string} fixed url */ export function validateUrl(url, origError) { try { const urlObject = new URL(url); return urlObject.href; } catch (err) { /** * if even adding http:// doesn't help, fail with original error */ if (origError) { throw origError; } return validateUrl(`http://${url}`, new Error(`Invalid URL: ${url}`)); } } export async function hasElementId(element) { /* * This is only necessary as isDisplayed is on the exclusion list for the middleware */ if (!element.elementId) { const command = element.isReactElement ? element.parent.react$.bind(element.parent) : element.isShadowElement ? element.parent.shadow$.bind(element.parent) : element.parent.$.bind(element.parent); element.elementId = (await command(element.selector)).elementId; } /* * if element was still not found it also is not displayed */ if (!element.elementId) { return false; } return true; } export function addLocatorStrategyHandler(scope) { return (name, func) => { if (scope.strategies.get(name)) { throw new Error(`Strategy ${name} already exists`); } scope.strategies.set(name, func); }; } /** * Enhance elements array with data required to refetch it * @param {object[]} elements elements * @param {object} parent element or browser * @param {string|Function} selector string or function, or strategy name for `custom$$` * @param {string} foundWith name of the command elements were found with, ex `$$`, `react$$`, etc * @param {Array} props additional properties required to fetch elements again * @returns {object[]} elements */ export const enhanceElementsArray = (elements, parent, selector, foundWith = '$$', props = []) => { /** * if we have an element collection, e.g. `const elems = $$([elemA, elemB])` * we cna't assign a common selector to the element array */ if (!Array.isArray(selector)) { elements.selector = selector; } elements.parent = parent; elements.foundWith = foundWith; elements.props = props; return elements; }; /** * is protocol stub * @param {string} automationProtocol */ export const isStub = (automationProtocol) => automationProtocol === './protocol-stub.js'; /** * compare if an object (`base`) contains the same values as another object (`match`) * @param {object} base object to compare to * @param {object} match object that needs to match thebase */ export const containsHeaderObject = (base, match) => { for (const [key, value] of Object.entries(match)) { if (typeof base[key] === 'undefined' || base[key] !== value) { return false; } } return true; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUE7QUFDakMsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFBO0FBQzVCLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFFOUIsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFBO0FBQ2hDLE9BQU8sT0FBTyxNQUFNLFNBQVMsQ0FBQTtBQUM3QixPQUFPLGdCQUFnQixNQUFNLG1CQUFtQixDQUFBO0FBQ2hELE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQTtBQUNqQyxPQUFPLGFBQWEsTUFBTSxjQUFjLENBQUE7QUFDeEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sYUFBYSxDQUFBO0FBR2hELE9BQU8sS0FBSyxlQUFlLE1BQU0sd0JBQXdCLENBQUE7QUFDekQsT0FBTyxLQUFLLGVBQWUsTUFBTSx3QkFBd0IsQ0FBQTtBQUN6RCxPQUFPLG9CQUFvQixNQUFNLHdDQUF3QyxDQUFBO0FBQ3pFLE9BQU8sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLEdBQUcsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQ2pFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUloRCxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUE7QUFDakMsTUFBTSxzQkFBc0IsR0FBRyxvREFBb0QsQ0FBQTtBQUNuRixNQUFNLDRCQUE0QixHQUFHLENBQUMsZUFBZSxFQUFFLGNBQWMsQ0FBQyxDQUFBO0FBTXRFLE1BQU0sTUFBTSxHQUFHO0lBQ1gsT0FBTyxFQUFFLGVBQWU7SUFDeEIsT0FBTyxFQUFFLGVBQWU7Q0FDM0IsQ0FBQTtBQUVELE1BQU0sbUJBQW1CLEdBQUcsQ0FDeEIsU0FBNkMsRUFDN0MsS0FBNEIsRUFBRSxFQUFFO0lBQ2hD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