mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00

Element.checkVisibility is a new browser API that was shipped in chromium 105: https://bugs.chromium.org/p/chromium/issues/detail?id=1309533 Using checkVisibility accounts for the content-visibility:hidden in the user-agent ShadowRoot of the details element, which means we can remove the usage of the AutoExpandDetailsElementFlag (I am trying to remove the flag in chromium). This behavior is covered by the existing "isVisible and isHidden should work with details" test in locator-convenience.spec.ts.
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
/**
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
export function isInsideScope(scope: Node, element: Element | undefined): boolean {
|
|
while (element) {
|
|
if (scope.contains(element))
|
|
return true;
|
|
element = enclosingShadowHost(element);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function parentElementOrShadowHost(element: Element): Element | undefined {
|
|
if (element.parentElement)
|
|
return element.parentElement;
|
|
if (!element.parentNode)
|
|
return;
|
|
if (element.parentNode.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */ && (element.parentNode as ShadowRoot).host)
|
|
return (element.parentNode as ShadowRoot).host;
|
|
}
|
|
|
|
export function enclosingShadowRootOrDocument(element: Element): Document | ShadowRoot | undefined {
|
|
let node: Node = element;
|
|
while (node.parentNode)
|
|
node = node.parentNode;
|
|
if (node.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */ || node.nodeType === 9 /* Node.DOCUMENT_NODE */)
|
|
return node as Document | ShadowRoot;
|
|
}
|
|
|
|
function enclosingShadowHost(element: Element): Element | undefined {
|
|
while (element.parentElement)
|
|
element = element.parentElement;
|
|
return parentElementOrShadowHost(element);
|
|
}
|
|
|
|
export function closestCrossShadow(element: Element | undefined, css: string): Element | undefined {
|
|
while (element) {
|
|
const closest = element.closest(css);
|
|
if (closest)
|
|
return closest;
|
|
element = enclosingShadowHost(element);
|
|
}
|
|
}
|
|
|
|
export function isElementVisible(element: Element): boolean {
|
|
// Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises.
|
|
if (!element.ownerDocument || !element.ownerDocument.defaultView)
|
|
return true;
|
|
const style = element.ownerDocument.defaultView.getComputedStyle(element);
|
|
if (style.display === 'contents') {
|
|
// display:contents is not rendered itself, but its child nodes are.
|
|
for (let child = element.firstChild; child; child = child.nextSibling) {
|
|
if (child.nodeType === 1 /* Node.ELEMENT_NODE */ && isElementVisible(child as Element))
|
|
return true;
|
|
if (child.nodeType === 3 /* Node.TEXT_NODE */ && isVisibleTextNode(child as Text))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// Element.checkVisibility checks for content-visibility and also looks at
|
|
// styles up the flat tree including user-agent ShadowRoots, such as the
|
|
// details element for example.
|
|
// @ts-ignore Typescript doesn't know that checkVisibility exists yet.
|
|
if (Element.prototype.checkVisibility && !element.checkVisibility({ checkOpacity: false, checkVisibilityCSS: false }))
|
|
return false;
|
|
if (!style || style.visibility === 'hidden')
|
|
return false;
|
|
const rect = element.getBoundingClientRect();
|
|
return rect.width > 0 && rect.height > 0;
|
|
}
|
|
|
|
function isVisibleTextNode(node: Text) {
|
|
// https://stackoverflow.com/questions/1461059/is-there-an-equivalent-to-getboundingclientrect-for-text-nodes
|
|
const range = document.createRange();
|
|
range.selectNode(node);
|
|
const rect = range.getBoundingClientRect();
|
|
return rect.width > 0 && rect.height > 0;
|
|
}
|