microsoft / microsoft/playwright
[Feature]: Extend isVisible/toBeVisible API to include overlap detection
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 96.3k
- Forks
- 6.5k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 180
Description
🚀 Feature Request
Extend the isVisible and toBeVisible calls to have an optional flag checkOverlaps (or enforceNotOverlapped if we want to be more precise), which would check if the element is overlapped by another one and thus not visible. For backward-compatibility, the flag would have a default value of false, in which case isVisible/toBeVisible would work exactly the same way as it does now.
If there is a strong argument for keeping these existing APIs unchanged, a new method could also be introduced (e.g. isNotOverlapped/toBeNotOverlapped).
Here's a possible implementation (does not consider transformations yet) that we use in our codebase to check overlaps.
function isElementNotOverlapped (element: Locator): Promise<boolean> {
return element.evaluate((el) => {
// pointer-events: none interferes with elementFromPoint so we temporarily set the property to all
const originalPointerEvents = el.style.pointerEvents
el.style.pointerEvents = 'all'
const rect = el.getBoundingClientRect()
const getStyleValueAsNumber = (styleProperty: string) => {
return Number(window.getComputedStyle(el, null).getPropertyValue(styleProperty)
.replace('px', ''))
}
const paddingLeft = getStyleValueAsNumber('padding-left')
const paddingRight = getStyleValueAsNumber('padding-right')
const paddingTop = getStyleValueAsNumber('padding-top')
const paddingBottom = getStyleValueAsNumber('padding-bottom')
const borderRadiusTopLeft = getStyleValueAsNumber('border-top-left-radius')
const borderRadiusTopRight = getStyleValueAsNumber('border-top-right-radius')
const borderRadiusBottomLeft = getStyleValueAsNumber('border-bottom-left-radius')
const borderRadiusBottomRight = getStyleValueAsNumber('border-bottom-right-radius')
const isPointVisible = (x: number, y: number) => {
const elementAtPoint = document.elementFromPoint(x, y)
return el.contains(elementAtPoint) || elementAtPoint === el
}
const pointsToCheckOffset = 2
const leftEdgeToCheck = rect.left + paddingLeft + pointsToCheckOffset
const topEdgeToCheck = rect.top + paddingTop + pointsToCheckOffset
const rightEdgeToCheck = rect.right - paddingRight - pointsToCheckOffset
const bottomEdgeToCheck = rect.bottom - paddingBottom - pointsToCheckOffset
const pointsToCheck = [
// top-left corner
{x: leftEdgeToCheck + (borderRadiusTopLeft / 3), y: topEdgeToCheck + (borderRadiusTopLeft / 3)},
// top-right corner
{x: rightEdgeToCheck - (borderRadiusTopRight / 3), y: topEdgeToCheck + (borderRadiusTopRight / 3)},
// bottom-left corner
{x: leftEdgeToCheck + (borderRadiusBottomLeft / 3), y: bottomEdgeToCheck - (borderRadiusBottomLeft / 3)},
// bottom-right corner
{x: rightEdgeToCheck - (borderRadiusBottomRight / 3), y: bottomEdgeToCheck - (borderRadiusBottomRight / 3)},
// center
{x: rect.left + (rect.width / 2), y: rect.top + (rect.height / 2)}
]
const result = pointsToCheck.every((point) => isPointVisible(point.x, point.y))
el.style.pointerEvents = originalPointerEvents
return result
})
Example
await page.goto('data:text/html,<html><body style="margin: 0;">' +
'<div id="small-box" style="background-color: green; height: 50px; width: 50px; position: fixed; top: 80px">' +
'</div><div id="big-box" style="background-color: brown; height: 100px; width: 100px; position: fixed;">' +
'</div></html>')
const smallBox = page.locator('#small-box')
// Passes
await expect(smallBox).toBeVisible()
// Passes
await expect(smallBox).toBeInViewport()
// Does not pass, since overlapped by `#big-box`.
await expect(smallBox).toBeVisible({checkOverlaps: true})
Motivation
As described in this previous issue (as well as this one), isVisible currently does not consider overlaps at all, so if an element is fully behind another one, isVisible may still return true for it. It would be useful to be able to test that an element is actually visible on the screen, since some visual bugs can offer due to overlapping elements and these are currently hard to write tests for using Playwright. Modern web apps often feature a lot of popups, modals etc. that may overlap other content on the screen.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the isVisible and toBeVisible APIs and reviewing the linked issues 9923 and 15924. Compare the proposed optional overlap flag with the alternative API, then validate the example with overlapping fixed-position elements; done means the chosen API consistently distinguishes ordinary visibility from overlap detection without changing the default behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100