-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
Have you read the Contributing Guidelines on issues?
- I have read the Contributing Guidelines on issues.
WebdriverIO Version
8.23.2
Node.js Version
20.9.0
Mode
Standalone Mode
Which capabilities are you using?
[
{
browserName: 'firefox',
browserVersion: '55',
platformName: 'Windows 10'
},
{
browserName: 'internet explorer',
browserVersion: '11.285',
platformName: 'Windows 10'
}
]What happened?
I'm upgrading alphagov/accessible-autocomplete from webdriverio@7.33.0 to webdriverio@8.23.2
But I'm now getting errors in Firefox 55 and Internet Explorer 11 using .waitForExist()
TypeError: invalid 'instanceof' operand window.ShadowRootIt appears to come from the check on line 94 in scripts/isElementDisplayed.ts
webdriverio/packages/webdriverio/src/scripts/isElementDisplayed.ts
Lines 91 to 96 in e233109
| // if document-fragment, skip it and use element.host instead. This happens | |
| // when the element is inside a shadow root. | |
| // window.getComputedStyle errors on document-fragment. | |
| if (element instanceof window.ShadowRoot) { | |
| element = element.host | |
| } |
Where perhaps the fix in 410ea50 didn't go far enough to guard undefined globals in IE11?
- if (element instanceof window.ShadowRoot) {
+ if ('ShadowRoot' in window && element instanceof window.ShadowRoot) {I'd be happy to open a PR
Log output is public below:
SauceLabs via webdriverio@7.33.0
https://github.com/alphagov/accessible-autocomplete/actions/runs/6935370100/job/18865380147
Firefox 55 on Windows 10
✅ https://app.saucelabs.com/tests/2d2579eb2a09406ca29126241d1cef4aInternet Explorer 11 on Windows 10
✅ https://app.saucelabs.com/tests/5d02b7b34ee749709fc6c9e1f23315d3
SauceLabs via webdriverio@8.23.2
https://github.com/alphagov/accessible-autocomplete/actions/runs/6935148161/job/18864740081?pr=612
Firefox 55 on Windows 10
❌ https://app.saucelabs.com/tests/2d2579eb2a09406ca29126241d1cef4aInternet Explorer 11 on Windows 10
❌ https://app.saucelabs.com/tests/5d02b7b34ee749709fc6c9e1f23315d3
What is your expected behavior?
Calls to .waitForExist() to succeed in Firefox 55 and Internet Explorer 11
How to reproduce the bug.
This bug report was recreated from alphagov/accessible-autocomplete#612
I've cloned the branch to package-updates-bug-report with the following commits:
- Test in
webdriverio@7.33.0via alphagov/accessible-autocomplete@927b0fa - Test in
webdriverio@8.23.2via alphagov/accessible-autocomplete@9637469
npm install
npm run wdio:testWith the following minimal example extracted from test/integration/index.js
describe('basic example', () => {
let $input
beforeEach(async () => {
$input = await $('input#autocomplete-default')
})
it('should show the input', async () => {
await $input.waitForExist()
})
})Relevant log output
[
{
"screenshot": null,
"suggestion_values": [],
"start_time": 1700510392.586,
"request": {
"args": [
{
"element-6066-11e4-a52e-4f735466cecf": "d8395e7b-5fd5-456e-ab64-532fb440fbe1",
"ELEMENT": "d8395e7b-5fd5-456e-ab64-532fb440fbe1"
}
],
"script": "return (function isElementDisplayed(element) {\n function nodeIsElement(node) {\n if (!node) {\n return false;\n }\n switch (node.nodeType) {\n case Node.ELEMENT_NODE:\n case Node.DOCUMENT_NODE:\n case Node.DOCUMENT_FRAGMENT_NODE:\n return true;\n default:\n return false;\n }\n }\n function parentElementForElement(element) {\n if (!element) {\n return null;\n }\n return enclosingNodeOrSelfMatchingPredicate(element.parentNode, nodeIsElement);\n }\n function enclosingNodeOrSelfMatchingPredicate(targetNode, predicate) {\n for (let node = targetNode; node && node !== targetNode.ownerDocument; node = node.parentNode) {\n if (predicate(node)) {\n return node;\n }\n }\n return null;\n }\n function enclosingElementOrSelfMatchingPredicate(targetElement, predicate) {\n for (let element = targetElement; element && element !== targetElement.ownerDocument; element = parentElementForElement(element)) {\n if (predicate(element)) {\n return element;\n }\n }\n return null;\n }\n function cascadedStylePropertyForElement(element, property) {\n if (!element || !property) {\n return null;\n }\n // if document-fragment, skip it and use element.host instead. This happens\n // when the element is inside a shadow root.\n // window.getComputedStyle errors on document-fragment.\n if (element instanceof window.ShadowRoot) {\n element = element.host;\n }\n const computedStyle = window.getComputedStyle(element);\n const computedStyleProperty = computedStyle.getPropertyValue(property);\n if (computedStyleProperty && computedStyleProperty !== 'inherit') {\n return computedStyleProperty;\n }\n // Ideally getPropertyValue would return the 'used' or 'actual' value, but\n // it doesn't for legacy reasons. So we need to do our own poor man's cascade.\n // Fall back to the first non-'inherit' value found in an ancestor.\n // In any case, getPropertyValue will not return 'initial'.\n // FIXME: will this incorrectly inherit non-inheritable CSS properties?\n // I think all important non-inheritable properties (width, height, etc.)\n // for our purposes here are specially resolved, so this may not be an issue.\n // Specification is here: https://drafts.csswg.org/cssom/#resolved-values\n const parentElement = parentElementForElement(element);\n return cascadedStylePropertyForElement(parentElement, property);\n }\n function elementSubtreeHasNonZeroDimensions(element) {\n const boundingBox = element.getBoundingClientRect();\n if (boundingBox.width > 0 && boundingBox.height > 0) {\n return true;\n }\n // Paths can have a zero width or height. Treat them as shown if the stroke width is positive.\n if (element.tagName.toUpperCase() === 'PATH' && boundingBox.width + boundingBox.height > 0) {\n const strokeWidth = cascadedStylePropertyForElement(element, 'stroke-width');\n return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);\n }\n const cascadedOverflow = cascadedStylePropertyForElement(element, 'overflow');\n if (cascadedOverflow === 'hidden') {\n return false;\n }\n // If the container's overflow is not hidden and it has zero size, consider the\n // container to have non-zero dimensions if a child node has non-zero dimensions.\n return Array.from(element.childNodes).some((childNode) => {\n if (childNode.nodeType === Node.TEXT_NODE) {\n return true;\n }\n if (nodeIsElement(childNode)) {\n return elementSubtreeHasNonZeroDimensions(childNode);\n }\n return false;\n });\n }\n function elementOverflowsContainer(element) {\n const cascadedOverflow = cascadedStylePropertyForElement(element, 'overflow');\n if (cascadedOverflow !== 'hidden') {\n return false;\n }\n // FIXME: this needs to take into account the scroll position of the element,\n // the display modes of it and its ancestors, and the container it overflows.\n // See Selenium's bot.dom.getOverflowState atom for an exhaustive list of edge cases.\n return true;\n }\n function isElementSubtreeHiddenByOverflow(element) {\n if (!element) {\n return false;\n }\n if (!elementOverflowsContainer(element)) {\n return false;\n }\n if (!element.childNodes.length) {\n return false;\n }\n // This element's subtree is hidden by overflow if all child subtrees are as well.\n return Array.from(element.childNodes).every((childNode) => {\n // Returns true if the child node is overflowed or otherwise hidden.\n // Base case: not an element, has zero size, scrolled out, or doesn't overflow container.\n // Visibility of text nodes is controlled by parent\n if (childNode.nodeType === Node.TEXT_NODE) {\n return false;\n }\n if (!nodeIsElement(childNode)) {\n return true;\n }\n if (!elementSubtreeHasNonZeroDimensions(childNode)) {\n return true;\n }\n // Recurse.\n return isElementSubtreeHiddenByOverflow(childNode);\n });\n }\n // walk up the tree testing for a shadow root\n function isElementInsideShadowRoot(element) {\n if (!element) {\n return false;\n }\n if (element.parentNode && element.parentNode.host) {\n return true;\n }\n return isElementInsideShadowRoot(element.parentNode);\n }\n // This is a partial reimplementation of Selenium's \"element is displayed\" algorithm.\n // When the W3C specification's algorithm stabilizes, we should implement that.\n // If this command is misdirected to the wrong document (and is NOT inside a shadow root), treat it as not shown.\n if (!isElementInsideShadowRoot(element) && !document.contains(element)) {\n return false;\n }\n // Special cases for specific tag names.\n switch (element.tagName.toUpperCase()) {\n case 'BODY':\n return true;\n case 'SCRIPT':\n case 'NOSCRIPT':\n return false;\n case 'OPTGROUP':\n case 'OPTION': {\n // Option/optgroup are considered shown if the containing <select> is shown.\n const enclosingSelectElement = enclosingNodeOrSelfMatchingPredicate(element, (e) => e.tagName.toUpperCase() === 'SELECT');\n return isElementDisplayed(enclosingSelectElement);\n }\n case 'INPUT':\n // <input type=\"hidden\"> is considered not shown.\n if (element.type === 'hidden') {\n return false;\n }\n break;\n // case 'MAP':\n // FIXME: Selenium has special handling for <map> elements. We don't do anything now.\n default:\n break;\n }\n if (cascadedStylePropertyForElement(element, 'visibility') !== 'visible') {\n return false;\n }\n const hasAncestorWithZeroOpacity = !!enclosingElementOrSelfMatchingPredicate(element, (e) => {\n return Number(cascadedStylePropertyForElement(e, 'opacity')) === 0;\n });\n const hasAncestorWithDisplayNone = !!enclosingElementOrSelfMatchingPredicate(element, (e) => {\n return cascadedStylePropertyForElement(e, 'display') === 'none';\n });\n if (hasAncestorWithZeroOpacity || hasAncestorWithDisplayNone) {\n return false;\n }\n if (!elementSubtreeHasNonZeroDimensions(element)) {\n return false;\n }\n if (isElementSubtreeHiddenByOverflow(element)) {\n return false;\n }\n return true;\n}).apply(null, arguments)"
},
"result": {
"message": "TypeError: invalid 'instanceof' operand window.ShadowRoot",
"error": "javascript error"
},
"duration": 0.031000137329101562,
"path": "execute/sync",
"hide_from_ui": false,
"between_commands": 0.21899986267089844,
"visual_command": false,
"HTTPStatus": 500,
"suggestion": null,
"request_id": "f9b0d865-bc67-45ee-a86a-5324b0dd5b01",
"in_video_timeline": 9.75,
"method": "POST",
"statusCode": 1
}
]Code of Conduct
- I agree to follow this project's Code of Conduct
Is there an existing issue for this?
- I have searched the existing issues