Skip to content

Commit ee74c01

Browse files
sainthkhbrian-mann
andauthored
Ignore dangling node in DOM tree. (#6787)
* Fix * content -> p for clarity. * Fix comment. * Add comments for future reference. * Simplify condition. * Update packages/driver/src/dom/transform.ts cleanup ternary stylistically and cache `backface-visibility` value * fix backfacevisibility types Co-authored-by: Brian Mann <[email protected]>
1 parent 095e2a3 commit ee74c01

File tree

3 files changed

+80
-4
lines changed

3 files changed

+80
-4
lines changed

packages/driver/src/dom/transform.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const detectVisibility = ($el: any) => {
1111
return elIsTransformedToZero(list) ? 'transformed' : 'visible'
1212
}
1313

14-
type BackfaceVisibility = 'hidden' | 'visible'
14+
type BackfaceVisibility = 'hidden' | 'visible' | ''
1515
type TransformStyle = 'flat' | 'preserve-3d'
1616
type Matrix2D = [
1717
number, number, number,
@@ -33,7 +33,11 @@ interface TransformInfo {
3333
}
3434

3535
const extractTransformInfoFromElements = ($el: any, list: TransformInfo[] = []): TransformInfo[] => {
36-
list.push(extractTransformInfo($el))
36+
const info = extractTransformInfo($el)
37+
38+
if (info) {
39+
list.push(info)
40+
}
3741

3842
const $parent = $el.parent()
3943

@@ -44,12 +48,23 @@ const extractTransformInfoFromElements = ($el: any, list: TransformInfo[] = []):
4448
return extractTransformInfoFromElements($parent, list)
4549
}
4650

47-
const extractTransformInfo = ($el): TransformInfo => {
51+
const extractTransformInfo = ($el): TransformInfo | null => {
4852
const el = $el[0]
4953
const style = getComputedStyle(el)
5054

55+
const backfaceVisibility = style.getPropertyValue('backface-visibility') as BackfaceVisibility
56+
57+
// When an element is not in the DOM tree, getComputedStyle() returns empty string.
58+
// In an edge case from frameworks like `vue-fragment`
59+
// `parentNode` is modified and out of the DOM tree.
60+
// @see https://github.com/cypress-io/cypress/pull/6787
61+
// @see https://github.com/cypress-io/cypress/issues/6745
62+
if (backfaceVisibility === '') {
63+
return null
64+
}
65+
5166
return {
52-
backfaceVisibility: style.getPropertyValue('backface-visibility') as BackfaceVisibility,
67+
backfaceVisibility,
5368
transformStyle: style.getPropertyValue('transform-style') as TransformStyle,
5469
transform: style.getPropertyValue('transform'),
5570
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Dangling element</title>
5+
</head>
6+
<body>
7+
<div>Some content</div>
8+
</body>
9+
<script>
10+
// Simplified version of what vue-fragment do.
11+
// https://github.com/y-nk/vue-fragment/blob/master/src/component.js
12+
13+
const freeze = (object, property, value) => {
14+
Object.defineProperty(object, property, {
15+
configurable: true,
16+
get () {
17+
return value
18+
},
19+
set (v) {
20+
// eslint-disable-next-line no-console
21+
console.warn(`tried to set frozen property ${property} with ${v}`)
22+
},
23+
})
24+
}
25+
26+
// If you want to see what's going on here visually,
27+
// check https://github.com/cypress-io/cypress/pull/6787
28+
29+
// 1. What vue does by default:
30+
// Create component and append it to the DOM tree.
31+
const parent = document.getElementsByTagName('body')[0]
32+
33+
const container = document.createElement('div')
34+
35+
container.classList.add('container')
36+
37+
const p = document.createElement('p')
38+
39+
p.innerText = 'hello world'
40+
p.classList.add('hello')
41+
42+
parent.appendChild(container)
43+
container.appendChild(p)
44+
45+
// 2. vue-fragment adds the p to the parent.
46+
// but forcefully set the p's parentNode to container.
47+
parent.appendChild(p)
48+
freeze(p, 'parentNode', container)
49+
50+
// 3. vue-fragment removes container from the parent.
51+
// but forcefully set the container's parentNode to parent.
52+
parent.removeChild(container)
53+
freeze(container, 'parentNode', parent)
54+
</script>
55+
</html>

packages/driver/test/cypress/integration/dom/visibility_spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,12 @@ describe('src/cypress/dom/visibility', () => {
835835
expect(el).to.be.visible
836836
})
837837

838+
// https://github.com/cypress-io/cypress/issues/6745
839+
it('is visible even if there is a dangling element in the tree', () => {
840+
cy.visit('/fixtures/dangling-element.html')
841+
cy.get('.hello')
842+
})
843+
838844
it('is hidden when an element is scaled to X axis in 0', () => {
839845
const el = add(`<div style="transform: scaleX(0)">ScaleX(0)</div>`)
840846

0 commit comments

Comments
 (0)