export {} // make typescript see this as a module const { $ } = Cypress describe('src/cypress/dom/visibility - shadow dom', () => { let add beforeEach(() => { cy.visit('/fixtures/empty.html').then((win) => { win.customElements.define('shadow-root', class extends win.HTMLElement { constructor () { super() // @ts-ignore this.attachShadow({ mode: 'open' }) this.style.display = 'block' } }) add = (el, shadowEl, rootIdentifier) => { const $el = $(el).appendTo(cy.$$('body')) // @ts-ignore $(shadowEl).appendTo(cy.$$(rootIdentifier)[0].shadowRoot) return $el } // ensure all tests run against a scrollable window const scrollThisIntoView = $(`
Should be in view
`).appendTo(cy.$$('body')) // scroll the 2nd element into view so that // there is always a scrollTop so we ensure // its factored in (window vs viewport) calculations scrollThisIntoView.get(1).scrollIntoView() }) }) describe('css visibility', () => { it('is hidden if parent is shadow root and has .css(visibility) hidden', () => { const $shadowRootVisHidden = add( ``, ``, '#shadow-root-vis-hidden', ) cy.wrap($shadowRootVisHidden).find('button', { includeShadowDom: true }).should('be.hidden') cy.wrap($shadowRootVisHidden).find('button', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if parent outside of shadow dom has .css(visibility) hidden', () => { const $outsideParentVisHidden = add( `
`, ``, '#outside-parent-vis-hidden', ) cy.wrap($outsideParentVisHidden).find('button', { includeShadowDom: true }).should('be.hidden') cy.wrap($outsideParentVisHidden).find('button', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if parent outside of shadow dom has visibility collapse', () => { const $outsideParentVisCollapse = add( `
Naruto Sakura
`, `Sasuke`, '#outside-parent-vis-collapse', ) cy.wrap($outsideParentVisCollapse).find('#collapse-span', { includeShadowDom: true }).should('be.hidden') cy.wrap($outsideParentVisCollapse).find('#collapse-span', { includeShadowDom: true }).should('not.be.visible') }) }) describe('width and height', () => { it('is hidden if parent is shadow root and has overflow: hidden and no width', () => { const $shadowRootNoWidth = add( ``, `
parent width: 0
`, '#shadow-root-no-width', ) cy.wrap($shadowRootNoWidth).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($shadowRootNoWidth).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if parent outside of shadow dom has overflow: hidden and no width', () => { const $outsideParentNoWidth = add( `
`, `
parent width: 0
`, '#outside-parent-no-width', ) cy.wrap($outsideParentNoWidth).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($outsideParentNoWidth).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if parent is shadow root and has overflow: hidden and no height', () => { const $shadowRootNoHeight = add( ``, `
parent height: 0
`, '#shadow-root-no-height', ) cy.wrap($shadowRootNoHeight).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($shadowRootNoHeight).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if parent outside of shadow dom has overflow: hidden and no height', () => { const $outsideParentNoHeight = add( `
`, `
parent height: 0
`, '#outside-parent-no-height', ) cy.wrap($outsideParentNoHeight).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($outsideParentNoHeight).find('span', { includeShadowDom: true }).should('not.be.visible') }) }) describe('css position', () => { it('is visible if child has position: absolute', () => { const $childPosAbs = add( `
`, `
position: absolute
`, '#child-pos-absolute', ) cy.wrap($childPosAbs).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($childPosAbs).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible if child has position: fixed', () => { const $childPosFixed = add( `
`, `
`, '#child-pos-fixed', ) cy.wrap($childPosFixed).find('button', { includeShadowDom: true }).should('be.visible') cy.wrap($childPosFixed).find('button', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible if descendent from parent has position: absolute and descendent is outside shadow dom', () => { const $descendentPosAbsOutside = add( `
`, `no width, descendant position: absolute`, '#descendent-pos-abs-outside', ) cy.wrap($descendentPosAbsOutside).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($descendentPosAbsOutside).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible if descendent from parent has position: absolute and descendent is inside shadow dom', () => { const $descendentPosAbsInside = add( `
`, `
no width, descendant position: absolute
`, '#descendent-pos-abs-inside', ) cy.wrap($descendentPosAbsInside).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($descendentPosAbsInside).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible if descendent from parent has position: fixed and descendent is outside shadow dom', () => { const $descendentPosFixedOutside = add( `
`, ``, '#descendent-pos-fixed-outside', ) cy.wrap($descendentPosFixedOutside).find('button', { includeShadowDom: true }).should('be.visible') cy.wrap($descendentPosFixedOutside).find('button', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible if descendent from parent has position: fixed and descendent is inside shadow dom', () => { const $descendentPosFixedInside = add( `
`, `
`, '#descendent-pos-fixed-inside', ) cy.wrap($descendentPosFixedInside).find('button', { includeShadowDom: true }).should('be.visible') cy.wrap($descendentPosFixedInside).find('button', { includeShadowDom: true }).should('not.be.hidden') }) it('is hidden if position: fixed and covered by element outside of shadow dom', () => { const $coveredUpByOutsidePosFixed = add( `
on top
`, `
underneath
`, '#covered-up-by-outside-pos-fixed', ) cy.wrap($coveredUpByOutsidePosFixed).find('#inside-underneath', { includeShadowDom: true }).should('be.hidden') cy.wrap($coveredUpByOutsidePosFixed).find('#inside-underneath', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden if outside of shadow dom with position: fixed and covered by element inside of shadow dom', () => { const $coveredUpByShadowPosFixed = add( `
underneath
`, `
on top
`, '#covered-up-by-shadow-pos-fixed', ) cy.wrap($coveredUpByShadowPosFixed).find('#outside-underneath', { includeShadowDom: true }).should('be.hidden') cy.wrap($coveredUpByShadowPosFixed).find('#outside-underneath', { includeShadowDom: true }).should('not.be.visible') }) it('is visible if position: fixed and parent outside shadow dom has pointer-events: none', () => { const $parentPointerEventsNone = add( `
`, `parent pointer-events: none`, '#parent-pointer-events-none', ) cy.wrap($parentPointerEventsNone).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($parentPointerEventsNone).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is hidden if covered when position: fixed and parent outside shadow dom has pointer-events: none', () => { const $parentPointerEventsNoneCovered = add( `
covering the element with pointer-events: none`, `parent pointer-events: none`, '#parent-pointer-events-none-covered', ) cy.wrap($parentPointerEventsNoneCovered).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($parentPointerEventsNoneCovered).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is visible if pointer-events: none and parent outside shadow dom has position: fixed', () => { const $childPointerEventsNone = add( `
`, `child pointer-events: none`, '#child-pointer-events-none-covered', ) cy.wrap($childPointerEventsNone).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($childPointerEventsNone).find('span', { includeShadowDom: true }).should('not.be.hidden') }) }) describe('css overflow', () => { it('is hidden when parent outside of shadow dom overflow hidden and out of bounds to left', () => { const $elOutOfParentBoundsToLeft = add( `
`, `position: absolute, out of bounds left`, '#el-out-of-parent-bounds-to-left', ) cy.wrap($elOutOfParentBoundsToLeft).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentBoundsToLeft).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom overflow hidden and out of bounds to right', () => { const $elOutOfParentBoundsToRight = add( `
`, `position: absolute, out of bounds right`, '#el-out-of-parent-bounds-to-right', ) cy.wrap($elOutOfParentBoundsToRight).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentBoundsToRight).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom overflow hidden and out of bounds above', () => { const $elOutOfParentBoundsAbove = add( `
`, `position: absolute, out of bounds above`, '#el-out-of-parent-bounds-above', ) cy.wrap($elOutOfParentBoundsAbove).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentBoundsAbove).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom overflow hidden and out of bounds below', () => { const $elOutOfParentBoundsBelow = add( `
`, `position: absolute, out of bounds below`, '#el-out-of-parent-bounds-below', ) cy.wrap($elOutOfParentBoundsBelow).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentBoundsBelow).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom overflow hidden-y and out of bounds', () => { const $elOutOfParentWithOverflowYHiddenBounds = add( `
`, `position: absolute, out of bounds below`, '#el-out-of-parent-with-overflow-y-hidden-bounds', ) cy.wrap($elOutOfParentWithOverflowYHiddenBounds).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentWithOverflowYHiddenBounds).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom overflow hidden-x and out of bounds', () => { const $elOutOfParentWithOverflowXHiddenBounds = add( `
`, `position: absolute, out of bounds below`, '#el-out-of-parent-with-overflow-x-hidden-bounds', ) cy.wrap($elOutOfParentWithOverflowXHiddenBounds).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfParentWithOverflowXHiddenBounds).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is visible when parent overflow hidden but el in a closer parent outside of shadow dom with position absolute', () => { const $elOutOfParentWithOverflowHiddenBoundsButCloserPositionAbsoluteParent = add( `
`, `Hello`, '#el-out-of-parent-with-overflow-hidden-bounds-but-closer-position-absolute-parent', ) cy.wrap($elOutOfParentWithOverflowHiddenBoundsButCloserPositionAbsoluteParent).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elOutOfParentWithOverflowHiddenBoundsButCloserPositionAbsoluteParent).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is hidden when parent is wide and ancestor outside shadow dom is overflow auto', () => { const $elOutOfAncestorOverflowAutoBoundsOutside = add( `
`, `out of bounds, parent wide, ancestor overflow: auto`, '#el-out-of-ancestor-overflow-auto-bounds-outside', ) cy.wrap($elOutOfAncestorOverflowAutoBoundsOutside).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfAncestorOverflowAutoBoundsOutside).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent is wide and ancestor inside shadow dom is overflow auto', () => { const $elOutOfAncestorOverflowAutoBoundsInside = add( `
`, `
out of bounds, parent wide, ancestor overflow: auto
`, '#el-out-of-ancestor-overflow-auto-bounds-inside', ) cy.wrap($elOutOfAncestorOverflowAutoBoundsInside).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfAncestorOverflowAutoBoundsInside).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent outside of shadow dom has overflow scroll and out of bounds', () => { const $elOutOfScrollingParentBounds = add( `
`, `out of scrolling bounds, position: absolute`, '#el-out-of-scrolling-parent-bounds', ) cy.wrap($elOutOfScrollingParentBounds).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfScrollingParentBounds).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when parent absolutely positioned and overflow hidden and out of bounds', () => { const $elOutOfPosAbsParentBounds = add( `
`, `out of bounds, position: absolute`, '#el-out-of-pos-abs-parent-bounds', ) cy.wrap($elOutOfPosAbsParentBounds).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elOutOfPosAbsParentBounds).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is visible when parent absolutely positioned and overflow hidden and not out of bounds', () => { const $elInPosAbsParentsBounds = add( `
`, `in bounds, parent position: absolute`, '#el-in-pos-abs-parent-bounds', ) cy.wrap($elInPosAbsParentsBounds).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elInPosAbsParentsBounds).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible when parent overflow hidden and not out of bounds', () => { const $elInParentBounds = add( `
`, `in bounds, position: absolute`, '#el-in-parent-bounds', ) cy.wrap($elInParentBounds).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elInParentBounds).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible when ancestor outside shadow dom is overflow hidden but more distant ancestor is the offset parent', () => { const $elIsOutOfBoundsOfOutsideAncestorsOverflowButWithinRelativeAncestor = add( `
`, `in bounds of ancestor, position: absolute, parent overflow: hidden`, '#el-is-out-of-bounds-of-outside-ancestors-overflow-but-within-relative-ancestor', ) cy.wrap($elIsOutOfBoundsOfOutsideAncestorsOverflowButWithinRelativeAncestor).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elIsOutOfBoundsOfOutsideAncestorsOverflowButWithinRelativeAncestor).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible when ancestor inside shadow dom is overflow hidden but more distant ancestor is the offset parent', () => { const $elIsOutOfBoundsOfInsideAncestorsOverflowButWithinRelativeAncestor = add( `
`, `
in bounds of ancestor, position: absolute, parent overflow: hidden
`, '#el-is-out-of-bounds-of-inside-ancestors-overflow-but-within-relative-ancestor', ) cy.wrap($elIsOutOfBoundsOfInsideAncestorsOverflowButWithinRelativeAncestor).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elIsOutOfBoundsOfInsideAncestorsOverflowButWithinRelativeAncestor).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is hidden when relatively positioned outside of ancestor outside shadow dom with overflow hidden', () => { const $elIsRelativeAndOutOfBoundsOfAncestorOverflow = add( `
`, `out of bounds, position: relative`, '#el-is-relative-and-out-of-bounds-of-ancestor-overflow', ) cy.wrap($elIsRelativeAndOutOfBoundsOfAncestorOverflow).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($elIsRelativeAndOutOfBoundsOfAncestorOverflow).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is visible when relatively positioned outside of ancestor outside shadow dom that does not hide overflow', () => { const $elIsRelativeAndOutOfBoundsOfAncestorButAncestorShowsOverflow = add( `
`, `out of bounds but visible, position: relative`, '#el-is-relative-and-out-of-bounds-of-ancestor-but-ancestor-shows-overflow', ) cy.wrap($elIsRelativeAndOutOfBoundsOfAncestorButAncestorShowsOverflow).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($elIsRelativeAndOutOfBoundsOfAncestorButAncestorShowsOverflow).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible when parent inside shadow dom is relatively positioned out of bounds but el is relatively positioned back in bounds', () => { const $insideParentOutOfBoundsButElInBounds = add( `
`, `
in bounds of ancestor, parent out of bounds
`, '#inside-parent-out-of-bounds-but-el-in-bounds', ) cy.wrap($insideParentOutOfBoundsButElInBounds).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($insideParentOutOfBoundsButElInBounds).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is visible when parent outside shadow dom is relatively positioned out of bounds but el is relatively positioned back in bounds', () => { const $outsideParentOutOfBoundsButElInBounds = add( `
`, `in bounds of ancestor, parent out of bounds`, '#outside-parent-out-of-bounds-but-el-in-bounds', ) cy.wrap($outsideParentOutOfBoundsButElInBounds).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($outsideParentOutOfBoundsButElInBounds).find('span', { includeShadowDom: true }).should('not.be.hidden') }) }) describe('css transform', () => { it('is hidden when outside parent outside of shadow dom transform scale', () => { const $parentWithTransformScaleElOutsideScale = add( `
`, `TRANSFORMERS`, '#parent-with-transform-scale-el-outside-scale', ) cy.wrap($parentWithTransformScaleElOutsideScale).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($parentWithTransformScaleElOutsideScale).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is visible when inside parent outside of shadow dom transform scale', () => { const $parentWithTransformScaleElInsideScale = add( `
`, `TRANSFORMERS`, '#parent-with-transform-scale-el-inside-scale', ) cy.wrap($parentWithTransformScaleElInsideScale).find('span', { includeShadowDom: true }).should('be.visible') cy.wrap($parentWithTransformScaleElInsideScale).find('span', { includeShadowDom: true }).should('not.be.hidden') }) it('is hidden when out of ancestor bounds due to ancestor within shadow dom transform', () => { const $ancestorInsideTransformMakesElOutOfBoundsOfAncestor = add( `
`, `
out of ancestor's bounds due to ancestor translate
`, '#ancestor-inside-transform-makes-el-out-of-bounds-of-ancestor', ) cy.wrap($ancestorInsideTransformMakesElOutOfBoundsOfAncestor).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($ancestorInsideTransformMakesElOutOfBoundsOfAncestor).find('span', { includeShadowDom: true }).should('not.be.visible') }) it('is hidden when out of ancestor bounds due to ancestor outside shadow dom transform', () => { const $ancestorOutsideTransformMakesElOutOfBoundsOfAncestor = add( `
`, `
out of ancestor's bounds due to ancestor translate
`, '#ancestor-outside-transform-makes-el-out-of-bounds-of-ancestor', ) cy.wrap($ancestorOutsideTransformMakesElOutOfBoundsOfAncestor).find('span', { includeShadowDom: true }).should('be.hidden') cy.wrap($ancestorOutsideTransformMakesElOutOfBoundsOfAncestor).find('span', { includeShadowDom: true }).should('not.be.visible') }) }) })