UNPKG

taiko

Version:

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

112 lines (104 loc) 3.73 kB
const TextBox = require("../elements/textBox"); const ValueWrapper = require("./valueWrapper"); const { $function } = require("../elementSearch"); const { getElementGetter } = require("./helper"); function getTextBoxElementWithLabelOrPlaceholder(searchElement, label) { const textBoxes = []; const inputTypes = [ "text", "password", "search", "number", "email", "tel", "url", undefined, ]; function checkAndPushElement(elem) { if ( (elem.tagName && elem.tagName.toLowerCase() === "textarea") || elem.getAttribute?.("contenteditable") || (elem.tagName && elem.tagName.toLowerCase() === "input" && elem.type && inputTypes.includes(elem.type.toLowerCase())) ) { textBoxes.push(elem); } } const matchingLabels = [...searchElement.querySelectorAll("label")].filter( (labelElem) => { return labelElem.innerText.toLowerCase().includes(label.toLowerCase()); }, ); const matchingPlaceHolders = [ ...searchElement.querySelectorAll("input[placeholder]"), ].filter((labelElem) => { return labelElem.placeholder.toLowerCase().includes(label.toLowerCase()); }); if (matchingLabels.length !== 0) { for (const matchingLabel of matchingLabels) { const labelFor = matchingLabel.getAttribute("for"); if (labelFor) { //check label with attribute for const labelForElement = searchElement.getElementById(labelFor); checkAndPushElement(labelForElement); } else { // check child node of label tag for (const elem of matchingLabel.childNodes) { checkAndPushElement(elem); } } } } if (matchingPlaceHolders.length !== 0) { for (const matchingPlaceHolder of matchingPlaceHolders) { checkAndPushElement(matchingPlaceHolder); } } //check label is inlined const inputRadioElements = [ ...searchElement.querySelectorAll( 'input[type="text" i],input[type="password" i],input[type="search" i],input[type="number" i],input[type="email" i],input[type="tel" i],input[type="url" i],input:not([type]),textarea,*[contenteditable="true"]', ), ]; for (const inputRadioElement of inputRadioElements) { if ( inputRadioElement.nextSibling && inputRadioElement.nextSibling.nodeType === Node.TEXT_NODE && inputRadioElement.nextSibling.wholeText.includes(label) ) { textBoxes.push(inputRadioElement); } } return textBoxes; } /** * Behaves the same as ValueWrapper. * @extends {ValueWrapper} */ class TextBoxWrapper extends ValueWrapper { constructor(attrValuePairs, _options, ...args) { super("TextBox", "label", attrValuePairs, _options, ...args); this._get = getElementGetter( this.selector, async () => await $function( getTextBoxElementWithLabelOrPlaceholder, this.selector.label, ), 'input[type="text" i],input[type="password" i],input[type="search" i],input[type="number" i],input[type="email" i],input[type="tel" i],input[type="url" i],input:not([type]),textarea,*[contenteditable="true"]', ); } /** * Overrides {@link ValueWrapper#elements}, but for TextBox elements. * @param {number} retryInterval Retry Interval in milliseconds (defaults to global settings). * @param {number} retryTimeout Retry Timeout in milliseconds (defaults to global settings). * @returns {TextBox[]} Array of all textBoxes matching the selector. */ async elements(retryInterval, retryTimeout) { const elements = await super.elements(retryInterval, retryTimeout); return elements.map((element) => TextBox.from(element, this._description)); } } module.exports = TextBoxWrapper;