Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix getting parents/ancestors for shadow dom elements #8106

Merged
merged 6 commits into from Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/driver/cypress/fixtures/shadow-dom.html
Expand Up @@ -19,6 +19,10 @@
<div class="in-shadow-slot">In Shadow Slot</div>
</cy-test-element>
<cy-test-element id="shadow-element-7" innerClass="in-and-out" content="Shadow Content 7"></cy-test-element>
<div class="focusable" tabindex="1">
<cy-test-element innerClass="inside-focusable" content="Inside Focusable"></cy-test-element>
</div>
<cy-test-element innerClass="inside-non-visible" content="Not Visible" style="transform-style: preserve-3d; backface-visibility: hidden; transform: rotateY(180deg);"></cy-test-element>

<script type="text/javascript">
if (window.customElements) {
Expand All @@ -28,7 +32,7 @@

const root = this.attachShadow({ mode: 'open' })
const para = document.createElement('p')
const content = this.getAttribute('content')
const content = this.getAttribute('content') || 'Shadow Content'
const className = this.hasAttribute('innerClass') ? this.getAttribute('innerClass') : 'shadow-content'

root.innerHTML = `<p class="${className}">${content}</p><input /><slot></slot>`
Expand Down
Expand Up @@ -3707,6 +3707,11 @@ describe('shadow dom', () => {
.rightclick()
})

it('focuses the correct ancestor when it is outside shadow dom', () => {
cy.get('.inside-focusable', { includeShadowDom: true }).click()
cy.get('.focusable').should('be.focused')
})

// https://github.com/cypress-io/cypress/issues/7679
it('does not hang when experimentalShadowDomSupport is false and clicking on custom element', () => {
Cypress.config('experimentalShadowDomSupport', false)
Expand Down
Expand Up @@ -799,6 +799,17 @@ describe('src/cy/commands/actions/scroll', () => {
})
})

describe('shadow dom', () => {
beforeEach(() => {
cy.visit('/fixtures/shadow-dom.html')
})

// https://github.com/cypress-io/cypress/issues/7986
it('does not hang', () => {
cy.get('.shadow-1', { includeShadowDom: true }).scrollIntoView()
})
})

describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
Expand Down
11 changes: 8 additions & 3 deletions packages/driver/cypress/integration/e2e/visibility_spec.js
Expand Up @@ -29,11 +29,16 @@ describe('visibility', () => {
})
})

// https://github.com/cypress-io/cypress/issues/7794
describe('with shadow dom and fixed position ancestor', () => {
it('does not hang when checking visibility', () => {
describe('with shadow dom', () => {
// https://github.com/cypress-io/cypress/issues/7794
it('fixed position ancestor does not hang when checking visibility', () => {
cy.visit('/fixtures/issue-7794.html')
cy.get('.container-2').should('be.visible')
})

it('non-visible ancestor causes element to not be visible', () => {
cy.visit('/fixtures/shadow-dom.html')
cy.get('.inside-non-visible', { includeShadowDom: true }).should('not.be.visible')
})
})
})
2 changes: 1 addition & 1 deletion packages/driver/src/cy/commands/actions/scroll.js
Expand Up @@ -7,7 +7,7 @@ const $utils = require('../../../cypress/utils')
const $errUtils = require('../../../cypress/error_utils')

const findScrollableParent = ($el, win) => {
const $parent = $el.parent()
const $parent = $dom.getParent($el)

// if we're at the body, we just want to pass in
// window into jQuery scrollTo
Expand Down
8 changes: 5 additions & 3 deletions packages/driver/src/dom/document.ts
Expand Up @@ -4,13 +4,15 @@ const docNode = window.Node.DOCUMENT_NODE
const docFragmentNode = window.Node.DOCUMENT_FRAGMENT_NODE

//TODO: make this not allow jquery
const isDocument = (obj: Node): obj is Document => {
const isDocument = (obj: Node | JQuery): obj is Document => {
try {
let node = obj as Node

if ($jquery.isJquery(obj)) {
obj = obj[0]
node = obj[0]
}

return obj?.nodeType === docNode || obj?.nodeType === docFragmentNode
return node?.nodeType === docNode || node?.nodeType === docFragmentNode
} catch (error) {
return false
}
Expand Down
21 changes: 16 additions & 5 deletions packages/driver/src/dom/elements.ts
Expand Up @@ -611,7 +611,7 @@ const isWithinShadowRoot = (node: HTMLElement) => {
return isShadowRoot(node.getRootNode())
}

const getParent = (el) => {
const getParentNode = (el) => {
// if the element has a direct parent element,
// simply return it.
if (el.parentElement) {
Expand All @@ -629,9 +629,13 @@ const getParent = (el) => {
return null
}

const getParent = ($el: JQuery): JQuery => {
return $(getParentNode($el[0]))
}

const getAllParents = (el) => {
const collectParents = (parents, node) => {
const parent = getParent(node)
const parent = getParentNode(node)

if (!parent) return parents

Expand Down Expand Up @@ -899,7 +903,7 @@ const isDescendent = ($el1, $el2) => {

const findParent = (el, condition) => {
const collectParent = (node) => {
const parent = getParent(node)
const parent = getParentNode(node)

if (!parent) return null

Expand All @@ -923,7 +927,7 @@ const getFirstFocusableEl = ($el: JQuery<HTMLElement>) => {
return $el
}

const parent = $el.parent()
const parent = getParent($el)

// if we have no parent then just return
// the window since that can receive focus
Expand All @@ -933,7 +937,7 @@ const getFirstFocusableEl = ($el: JQuery<HTMLElement>) => {
return $(win)
}

return getFirstFocusableEl($el.parent())
return getFirstFocusableEl(getParent($el))
}

const getActiveElByDocument = ($el: JQuery<HTMLElement>): HTMLElement | null => {
Expand Down Expand Up @@ -1245,6 +1249,12 @@ const elementFromPoint = (doc, x, y) => {
return elFromPoint
}

const getShadowRoot = ($el: JQuery): JQuery<Node> => {
const root = $el[0].getRootNode()

return $(root)
}

const findAllShadowRoots = (root: Node): Node[] => {
const collectRoots = (roots, nodes, node) => {
const currentRoot = roots.pop()
Expand Down Expand Up @@ -1359,4 +1369,5 @@ export {
getFirstScrollableParent,
getParent,
getAllParents,
getShadowRoot,
}
3 changes: 2 additions & 1 deletion packages/driver/src/dom/transform.ts
@@ -1,4 +1,5 @@
import _ from 'lodash'
import { getParent } from './elements'
import { isDocument } from './document'

export const detectVisibility = ($el: any) => {
Expand Down Expand Up @@ -39,7 +40,7 @@ const extractTransformInfoFromElements = ($el: any, list: TransformInfo[] = []):
list.push(info)
}

const $parent = $el.parent()
const $parent = getParent($el)

if (!$parent.length || isDocument($parent)) {
return list
Expand Down