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(
`
`,
'#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(
`