UNPKG

taiko

Version:

Taiko is a Node.js library for automating Chromium based browsers

225 lines (206 loc) 8.3 kB
const { firstElement, desc, prepareParameters, elementTypeToSelectorName, } = require("./helper"); const { descEvent } = require("../eventBus"); const { getIfExists } = require("../elementSearch"); const runtimeHandler = require("../handlers/runtimeHandler"); /** * Wrapper object of all found elements. This list mimics the behaviour of {@link Element} * by exposing similar methods. The call of these methods gets delegated to first element. * By default, the `ElementWrapper` acts as a proxy to the first matching element and hence * it forwards function calls that belong to {@link Element} */ class ElementWrapper { constructor(elementType, query, attrValuePairs, _options, ...args) { if (attrValuePairs instanceof ElementWrapper) { const selectorName = elementTypeToSelectorName(elementType); throw new TypeError( `You are passing a \`ElementWrapper\` to a \`${selectorName}\` selector. Refer https://docs.taiko.dev/api/${selectorName.toLowerCase()}/ for the correct parameters`, ); } const { selector, options } = prepareParameters( attrValuePairs, _options, ...args, ); this.selector = selector; this._options = options; this._description = desc(selector, query, elementType, options); } /** * @deprecated Deprecated from version `1.0.3`. DOM element getter. Implicitly wait for the element to appears with timeout of 10 seconds. * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {Element[]} All elements mathing the selector. */ async get(retryInterval, retryTimeout) { console.warn("DEPRECATED use .elements()"); return this.elements(retryInterval, retryTimeout); } /** * @property * @description Describes the operation performed. The description is the same that is printed when performing the operation in REPL. * @returns {string} Description of the current command that fetched this element(wrapper). * @example * link('google').description // prints "'Link with text google'" */ get description() { return this._description; } /** * @description Checks existence for element. `exists()` waits for `retryTimeout` before deciding that the page is loaded. * (NOTE: `exists()` returns boolean from version `0.4.0`) * @since 0.4.0 * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {boolean} true if exists, else false. * @example * // To 'short-circuit' non existence. However this should be done only if there is no network calls/reloads. * element.exists(0,0) * @example * link('google').exists() * @example * link('google').exists(1000) */ async exists(retryInterval, retryTimeout) { try { await firstElement.apply(this, [retryInterval, retryTimeout]); } catch (e) { if (e.message === `${this._description} not found`) { descEvent.emit("success", "Does not exist"); return false; } throw e; } descEvent.emit("success", "Exists"); return true; } /** * @description Gets the [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) of the element * @returns {string} [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) of the element */ async text() { const elem = await firstElement.apply(this); return await elem.text(); } /** * @description Checks if element is visually visible. `isVisible()` is false when the element is overshadowed by another element, * or if the element is outside the viewport. * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {boolean} true if visible, else false. */ async isVisible(retryInterval, retryTimeout) { const elem = await firstElement.apply(this, [retryInterval, retryTimeout]); async function isVisible() { const visibilityRatio = await new Promise((resolve) => { const elem = this; const observer = new IntersectionObserver((entries) => { resolve(entries[0].intersectionRatio); observer.disconnect(); }); observer.observe(elem); }); return visibilityRatio === 1; } const objectId = elem.get(); const { result } = await runtimeHandler.runtimeCallFunctionOn( isVisible, null, { objectId: objectId, awaitPromise: true, }, ); if (result.value) { descEvent.emit("success", "Element is Visible"); return true; } descEvent.emit("success", "Element is not Visible"); return false; } /** * @description Checks if element is disabled * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {boolean} true if disabled, else false. */ async isDisabled(retryInterval, retryTimeout) { const elem = await firstElement.apply(this, [retryInterval, retryTimeout]); return await elem.isDisabled(); } /** * @description Checks if element is [draggable](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable). * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {boolean} true if disabled, else false. */ async isDraggable(retryInterval, retryTimeout) { const elem = await firstElement.apply(this, [retryInterval, retryTimeout]); return await elem.isDraggable(); } /** * @description Read attribute value of the element found. * @param {string} name * @returns {string} value of attribute * @example * link('google').attribute('alt') */ async attribute(name) { const elem = await firstElement.apply(this); return await elem.getAttribute(name); } /** * @description DOM element getter. Implicitly wait for the element to appears with timeout of 10 seconds. * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {Element[]} Array of all elements matching the selector. * @example * // To loop over all the elements * let elements = await $('a').elements(); * for (element of elements) { * console.log(await element.text()); * } * @example * textBox('username').value() * (await textBox('username').elements())[0].value() # same as above * @example * $('.class').text() * (await $('.class').elements())[0].text() # same as above * @example * let element = await $('a').element(0); * console.log(await element.text()); */ async elements(retryInterval, retryTimeout) { return await getIfExists(this._get, this._description)( null, retryInterval, retryTimeout, ); } /** * @description DOM element getter. Implicitly wait for the element to appears with timeout of 10 seconds. * @alias elements()[0] * @param {number} index Zero-based index of element to return * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {Element} First element that matches the selector. */ async element(index, retryInterval, retryTimeout) { const results = await getIfExists(this._get, this._description)( null, retryInterval, retryTimeout, ); if (index > results.length - 1) { throw new Error( `Element index is out of range. There are only ${results.length} element(s)`, ); } return results[index]; } } module.exports = ElementWrapper;