UNPKG

webdriverio

Version:

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

169 lines (168 loc) 5.53 kB
// src/scripts/isElementDisplayed.ts function isElementDisplayed(element) { function nodeIsElement(node) { if (!node) { return false; } switch (node.nodeType) { case Node.ELEMENT_NODE: case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: return true; default: return false; } } function parentElementForElement(element2) { if (!element2) { return null; } return enclosingNodeOrSelfMatchingPredicate(element2.parentNode, nodeIsElement); } function enclosingNodeOrSelfMatchingPredicate(targetNode, predicate) { for (let node = targetNode; node && node !== targetNode.ownerDocument; node = node.parentNode) { if (predicate(node)) { return node; } } return null; } function enclosingElementOrSelfMatchingPredicate(targetElement, predicate) { for (let element2 = targetElement; element2 && element2 !== targetElement.ownerDocument; element2 = parentElementForElement(element2)) { if (predicate(element2)) { return element2; } } return null; } function cascadedStylePropertyForElement(element2, property) { if (!element2 || !property) { return null; } if ("ShadowRoot" in window && element2 instanceof window.ShadowRoot) { element2 = element2.host; } const computedStyle = window.getComputedStyle(element2); const computedStyleProperty = computedStyle.getPropertyValue(property); if (computedStyleProperty && computedStyleProperty !== "inherit") { return computedStyleProperty; } const parentElement = parentElementForElement(element2); return cascadedStylePropertyForElement(parentElement, property); } function elementHasBoundingBox(element2) { const boundingBox = element2.getBoundingClientRect(); return boundingBox.width > 0 && boundingBox.height > 0; } function elementSubtreeHasNonZeroDimensions(element2) { if (elementHasBoundingBox(element2)) { return true; } const boundingBox = element2.getBoundingClientRect(); if (element2.tagName.toUpperCase() === "PATH" && boundingBox.width + boundingBox.height > 0) { const strokeWidth = cascadedStylePropertyForElement(element2, "stroke-width"); return !!strokeWidth && parseInt(strokeWidth, 10) > 0; } const cascadedOverflow = cascadedStylePropertyForElement(element2, "overflow"); if (cascadedOverflow === "hidden") { return false; } return [].some.call(element2.childNodes, function(childNode) { if (childNode.nodeType === Node.TEXT_NODE) { return true; } if (nodeIsElement(childNode)) { return elementSubtreeHasNonZeroDimensions(childNode); } return false; }); } function elementOverflowsContainer(element2) { const cascadedOverflow = cascadedStylePropertyForElement(element2, "overflow"); if (cascadedOverflow !== "hidden") { return false; } return true; } function isElementSubtreeHiddenByOverflow(element2) { if (!element2) { return false; } if (!elementOverflowsContainer(element2)) { return false; } if (!element2.childNodes.length) { return false; } return [].every.call(element2.childNodes, function(childNode) { if (childNode.nodeType === Node.TEXT_NODE) { return false; } if (!nodeIsElement(childNode)) { return true; } if (!elementSubtreeHasNonZeroDimensions(childNode)) { return true; } return isElementSubtreeHiddenByOverflow(childNode); }); } function isElementInsideShadowRoot(element2) { if (!element2) { return false; } if (element2.parentNode && element2.parentNode.host) { return true; } return isElementInsideShadowRoot(element2.parentNode); } if (!isElementInsideShadowRoot(element) && // IE doesn't support document.contains, therefor check before using (typeof document.contains === "function" ? !document.contains(element) : !document.body.contains(element))) { return false; } switch (element.tagName.toUpperCase()) { case "BODY": return true; case "SCRIPT": case "NOSCRIPT": return false; case "OPTGROUP": case "OPTION": { const enclosingSelectElement = enclosingNodeOrSelfMatchingPredicate(element, function(e) { return e.tagName.toUpperCase() === "SELECT"; }); return isElementDisplayed(enclosingSelectElement); } case "INPUT": if (element.type === "hidden") { return false; } break; // case 'MAP': // FIXME: Selenium has special handling for <map> elements. We don't do anything now. default: break; } if (cascadedStylePropertyForElement(element, "visibility") !== "visible") { return false; } const hasAncestorWithZeroOpacity = !!enclosingElementOrSelfMatchingPredicate(element, function(e) { return Number(cascadedStylePropertyForElement(e, "opacity")) === 0; }); const hasAncestorWithDisplayNone = !!enclosingElementOrSelfMatchingPredicate(element, function(e) { return cascadedStylePropertyForElement(e, "display") === "none"; }); if (hasAncestorWithZeroOpacity || hasAncestorWithDisplayNone) { return false; } if (!elementSubtreeHasNonZeroDimensions(element)) { return false; } if (isElementSubtreeHiddenByOverflow(element) && !elementHasBoundingBox(element)) { return false; } return true; } export { isElementDisplayed as default };