diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md
index eabda4019d..4cb0cd8cf5 100644
--- a/cli/CHANGELOG.md
+++ b/cli/CHANGELOG.md
@@ -1,4 +1,12 @@
+## 13.4.1
+
+_Released 11/7/2023 (PENDING)_
+
+**Bugfixes:**
+
+- Fixed an issue determining visibility when an element is hidden by an ancestor with a shared edge. Fixes [#27514](https://github.com/cypress-io/cypress/issues/27514).
+
## 13.4.0
_Released 10/30/2023_
diff --git a/packages/driver/cypress/e2e/dom/visibility.cy.ts b/packages/driver/cypress/e2e/dom/visibility.cy.ts
index adaa51f99b..5dc7495afc 100644
--- a/packages/driver/cypress/e2e/dom/visibility.cy.ts
+++ b/packages/driver/cypress/e2e/dom/visibility.cy.ts
@@ -356,25 +356,25 @@ describe('src/cypress/dom/visibility', () => {
this.$elOutOfParentBoundsToLeft = add(`\
- position: absolute, out of bounds left
+ position: absolute, out of bounds left
\
`)
this.$elOutOfParentBoundsToRight = add(`\
- position: absolute, out of bounds right
+ position: absolute, out of bounds right
\
`)
this.$elOutOfParentBoundsAbove = add(`\
- position: absolute, out of bounds above
+ position: absolute, out of bounds above
\
`)
this.$elOutOfParentBoundsBelow = add(`\
- position: absolute, out of bounds below
+ position: absolute, out of bounds below
\
`)
@@ -819,10 +819,9 @@ describe('src/cypress/dom/visibility', () => {
})
describe('css overflow', () => {
- it('is visible when parent doesnt have overflow hidden', function () {
- expect(this.$parentNoWidthHeightOverflowAuto.find('span')).to.be.visible
-
- expect(this.$parentNoWidthHeightOverflowAuto.find('span')).to.not.be.hidden
+ it('is hidden when parent overflow auto and no width/height', function () {
+ expect(this.$parentNoWidthHeightOverflowAuto.find('span')).to.not.be.visible
+ expect(this.$parentNoWidthHeightOverflowAuto.find('span')).to.be.hidden
})
it('is hidden when parent overflow hidden and out of bounds to left', function () {
@@ -897,7 +896,7 @@ describe('src/cypress/dom/visibility', () => {
})
describe('css clip-path', () => {
- // TODO: why is this skipped?
+ // TODO: handle clip path 'hidden' equivalents
it.skip('is hidden when outside of parents clip-path', function () {
expect(this.$parentWithClipPathAbsolutePositionElOutsideClipPath.find('span')).to.be.hidden
})
diff --git a/packages/driver/cypress/e2e/dom/visibility_shadow_dom.cy.ts b/packages/driver/cypress/e2e/dom/visibility_shadow_dom.cy.ts
index f34df529e9..5d1b6779e4 100644
--- a/packages/driver/cypress/e2e/dom/visibility_shadow_dom.cy.ts
+++ b/packages/driver/cypress/e2e/dom/visibility_shadow_dom.cy.ts
@@ -303,7 +303,7 @@ describe('src/cypress/dom/visibility - shadow dom', () => {
`
`,
- `position: absolute, out of bounds left`,
+ `position: absolute, out of bounds left`,
'#el-out-of-parent-bounds-to-left',
)
@@ -316,7 +316,7 @@ describe('src/cypress/dom/visibility - shadow dom', () => {
`
`,
- `position: absolute, out of bounds right`,
+ `position: absolute, out of bounds right`,
'#el-out-of-parent-bounds-to-right',
)
@@ -329,7 +329,7 @@ describe('src/cypress/dom/visibility - shadow dom', () => {
`
`,
- `position: absolute, out of bounds above`,
+ `position: absolute, out of bounds above`,
'#el-out-of-parent-bounds-above',
)
@@ -342,7 +342,7 @@ describe('src/cypress/dom/visibility - shadow dom', () => {
`
`,
- `position: absolute, out of bounds below`,
+ `position: absolute, out of bounds below`,
'#el-out-of-parent-bounds-below',
)
diff --git a/packages/driver/src/dom/elements/find.ts b/packages/driver/src/dom/elements/find.ts
index 9e657450ea..16f95fa201 100644
--- a/packages/driver/src/dom/elements/find.ts
+++ b/packages/driver/src/dom/elements/find.ts
@@ -176,7 +176,13 @@ export const isAncestor = ($el, $maybeAncestor) => {
}
export const isChild = ($el, $maybeChild) => {
- return $el.children().index($maybeChild) >= 0
+ let children = $el.children()
+
+ if (children.length && children[0].nodeName === 'SHADOW-ROOT') {
+ return isDescendent($el, $maybeChild)
+ }
+
+ return children.index($maybeChild) >= 0
}
export const isDescendent = ($el1, $el2) => {
diff --git a/packages/driver/src/dom/visibility.ts b/packages/driver/src/dom/visibility.ts
index 9a1be1ac7f..2a92fae2b9 100644
--- a/packages/driver/src/dom/visibility.ts
+++ b/packages/driver/src/dom/visibility.ts
@@ -316,24 +316,27 @@ const elIsOutOfBoundsOfAncestorsOverflow = function ($el, $ancestor = getParent(
return false
}
- const elProps = $coordinates.getElementPositioning($el)
-
if (canClipContent($el, $ancestor)) {
+ const elProps = $coordinates.getElementPositioning($el)
const ancestorProps = $coordinates.getElementPositioning($ancestor)
+ if (elHasPositionAbsolute($el) && (ancestorProps.width === 0 || ancestorProps.height === 0)) {
+ return elIsOutOfBoundsOfAncestorsOverflow($el, getParent($ancestor))
+ }
+
// target el is out of bounds
if (
// target el is to the right of the ancestor's visible area
- (elProps.fromElWindow.left > (ancestorProps.width + ancestorProps.fromElWindow.left)) ||
+ (elProps.fromElWindow.left >= (ancestorProps.width + ancestorProps.fromElWindow.left)) ||
// target el is to the left of the ancestor's visible area
- ((elProps.fromElWindow.left + elProps.width) < ancestorProps.fromElWindow.left) ||
+ ((elProps.fromElWindow.left + elProps.width) <= ancestorProps.fromElWindow.left) ||
// target el is under the ancestor's visible area
- (elProps.fromElWindow.top > (ancestorProps.height + ancestorProps.fromElWindow.top)) ||
+ (elProps.fromElWindow.top >= (ancestorProps.height + ancestorProps.fromElWindow.top)) ||
// target el is above the ancestor's visible area
- ((elProps.fromElWindow.top + elProps.height) < ancestorProps.fromElWindow.top)
+ ((elProps.fromElWindow.top + elProps.height) <= ancestorProps.fromElWindow.top)
) {
return true
}
@@ -555,5 +558,11 @@ export const getReasonIsHidden = function ($el, options = { checkOpacity: true }
/* eslint-enable no-cond-assign */
export default {
- isVisible, isHidden, isStrictlyHidden, isHiddenByAncestors, getReasonIsHidden, isW3CFocusable, isW3CRendered,
+ isVisible,
+ isHidden,
+ isStrictlyHidden,
+ isHiddenByAncestors,
+ getReasonIsHidden,
+ isW3CFocusable,
+ isW3CRendered,
}