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: 28187 Include checkVisibility browser API as a reliable tool to check visibility #29741

Open
wants to merge 67 commits into
base: release/14.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
266a230
option correct
senpl Jun 18, 2024
06c6185
cleanup
senpl Jun 18, 2024
aa14167
visibility improvements
senpl Jun 19, 2024
cb09746
cleanup
senpl Jun 19, 2024
86a0706
fixes
senpl Jun 19, 2024
9035412
clean code
senpl Jun 19, 2024
b611dcf
first draft version
senpl Jun 24, 2024
16897b3
working version
senpl Jun 24, 2024
d379ed1
method cleanup
senpl Jun 24, 2024
2e29c52
first working all tests
senpl Jun 25, 2024
d1b218f
23852 fix
senpl Jun 25, 2024
455ff94
fix for 28187
senpl Jun 25, 2024
1f7e60e
cleanu that previously was not send
senpl Jun 25, 2024
008823d
fixes for less checks
senpl Jun 25, 2024
9bc9afd
remove 23852 fix
senpl Jun 25, 2024
723d8ab
changelog update
senpl Jun 25, 2024
7f4d723
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jun 25, 2024
0ab0172
Merge branch 'issue-28187-IncludeCheckVisibilityBrowserAPI' of https:…
senpl Jun 25, 2024
ddf7227
changelog fix
senpl Jun 25, 2024
27a73d3
changelog pull
senpl Jun 25, 2024
a85166c
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
jennifer-shehane Jun 25, 2024
4c70ef5
pipeline tests fix
senpl Jun 27, 2024
6bdb8fe
Merge branch 'issue-28187-IncludeCheckVisibilityBrowserAPI' of https:…
senpl Jun 27, 2024
7d121bd
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jun 27, 2024
8149120
requires for new webkit
senpl Jun 28, 2024
23a908b
Merge branch 'issue-28187-IncludeCheckVisibilityBrowserAPI' of https:…
senpl Jun 28, 2024
2f88b48
fix for older webkit versions
senpl Jun 28, 2024
3363c0d
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jun 28, 2024
6f5addf
probably only optimisation for visibility of ancestors
senpl Jul 1, 2024
6e82b87
Merge branch 'issue-28187-IncludeCheckVisibilityBrowserAPI' of https:…
senpl Jul 1, 2024
3919231
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 1, 2024
112ff53
webkit fixes and rework
senpl Jul 2, 2024
9d9da1d
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 2, 2024
917469f
pipeline fix
senpl Jul 2, 2024
2efd8f1
Merge branch 'issue-28187-IncludeCheckVisibilityBrowserAPI' of https:…
senpl Jul 2, 2024
7cdbcf7
another check, probably not needed.
senpl Jul 3, 2024
25ccdc1
better check for browser support
senpl Jul 8, 2024
8315929
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 8, 2024
ea58390
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 9, 2024
9c6de7b
changelog for pipeline fix
senpl Jul 9, 2024
01a7695
Update packages/driver/src/dom/visibility.ts
senpl Jul 10, 2024
c63a04b
Update packages/driver/src/dom/visibility.ts
senpl Jul 10, 2024
1e83807
Update packages/driver/src/dom/visibility.ts
senpl Jul 10, 2024
4beb4ab
Update packages/driver/src/dom/visibility.ts
senpl Jul 10, 2024
fac3bdc
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 10, 2024
a7a4c51
fix misspelled
senpl Jul 10, 2024
6720715
function simplification end test uncomment
senpl Jul 10, 2024
cbbbc67
fix for backward compatibility with webkit
senpl Jul 10, 2024
230dcb2
fix by separation of tests
senpl Jul 10, 2024
2e88040
removed parts related to visibility_css_position
senpl Jul 10, 2024
9fc2b85
cleanup unneded code
senpl Jul 10, 2024
baae2c8
fix for body and html with display none
senpl Jul 11, 2024
b55b2d0
add is not visible test for offset 0
senpl Jul 11, 2024
96cf4f1
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
jennifer-shehane Jul 12, 2024
8398812
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 25, 2024
6cddc81
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 26, 2024
1a933bc
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 29, 2024
7be78d2
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Jul 31, 2024
1ad3ff8
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Aug 7, 2024
cb9cc14
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Aug 8, 2024
0fb8a9f
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Aug 16, 2024
1c5b240
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Aug 30, 2024
f3d4545
Merge branch 'develop' into issue-28187-IncludeCheckVisibilityBrowserAPI
senpl Sep 30, 2024
e705a32
merge changelog
senpl Sep 30, 2024
a458041
remove unnecessery changelog changes.
senpl Sep 30, 2024
2f572c3
Merge branch 'release/14.0.0' into issue-28187-IncludeCheckVisibility…
senpl Oct 1, 2024
31e1294
added breaking changes section and bugfix for pipeline
senpl Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ _Released 7/02/2024 (PENDING)_

**Bugfixes:**

- Change visibility check to use checkVisibility browser API. Fixed in [#29741](https://github.com/cypress-io/cypress/pull/29741).
- Fixed an issue where Chrome launch instances would not recreate the browser CRI client correctly after recovering from an unexpected browser closure. Fixes [#27657](https://github.com/cypress-io/cypress/issues/27657). Fixed in [#29663](https://github.com/cypress-io/cypress/pull/29663).
- Fixed an issue where Firefox 129 (Firefox Nightly) would not launch with Cypress. Fixes [#29713](https://github.com/cypress-io/cypress/issues/29713). Fixed in [#29720](https://github.com/cypress-io/cypress/pull/29720).

Expand Down Expand Up @@ -979,4 +980,4 @@ _Released 1/24/2023_

- Video output link in `cypress run` mode has been added to it's own line to
make the video output link more easily clickable in the terminal. Addresses
[#23913](https://github.com/cypress-io/cypress/issues/23913).
[#23913](https://github.com/cypress-io/cypress/issues/23913).
71 changes: 37 additions & 34 deletions packages/driver/cypress/e2e/dom/visibility.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,18 @@ describe('src/cypress/dom/visibility', () => {
// ensure all tests run against a scrollable window
const scrollThisIntoView = add('<div style=`height: 1000px;` /><div>Should be in view</div>')

this.$visHidden = add('<ul style="visibility: hidden;"></ul>')
this.$visHidden = add('<ul id="ulHidden" style="visibility: hidden;">UL HIDDEN</ul>')
this.$parentVisHidden = add('<div class="invis" style="visibility: hidden;"><button>parent visibility: hidden</button></div>')
this.$displayNone = add('<button style="display: none">display: none</button>')
this.$inputHidden = add('<input type="hidden" value="abcdef">')
this.$divNoWidth = add('<div style="width: 0; height: 100px;">width: 0</div>')
this.$divNoWidth = add('<div id="divNoWidth" style="width: 0; height: 100px;">width: 0</div>')
this.$divNoHeight = add('<div style="width: 50px; height: 0px;">height: 0</div>')
this.$divDetached = $('<div>foo</div>')
this.$divVisible = add(`<div>visible</div>`)

this.$optionInSelect = add(`\
<select>
<option>Naruto</option>
<option id='optionInSelect' >Naruto</option>
</select>\
`)

Expand Down Expand Up @@ -244,21 +244,21 @@ describe('src/cypress/dom/visibility', () => {
</table>\
`)

this.$parentNoWidth = add(`\
this.$parentNoWidthOnly = add(`\
<div style='width: 0; height: 100px; overflow: hidden;'>
<div style='height: 500px; width: 500px;'>
<span>parent width: 0</span>
<span id='parentNoWidthOnly'>parent width: 0</span>
</div>
</div>`)

this.$parentNoHeight = add(`\
<div style='width: 100px; height: 0px; overflow: hidden;'>
<span>parent height: 0</span>
<span id='parentNoHeight'>parent height: 0</span>
</div>`)

this.$parentNoWidthHeightOverflowAuto = add(`\
<div style='width: 0; height: 0px; overflow: auto;'>
<span>parent no size, overflow: auto</span>
<span id='parentNoWidthHeightOverflowAuto'>parent no size, overflow: auto</span>
</div>`)

this.$parentWithWidthHeightNoOverflow = add(`\
Expand Down Expand Up @@ -323,7 +323,7 @@ describe('src/cypress/dom/visibility', () => {
this.$parentPosAbs = add(`\
<div style='width: 0; height: 100px; overflow: hidden; position: absolute;'>
<div style='height: 500px; width: 500px;'>
<span>parent position: absolute</span>
<span id='parentPosAbs'>parent position: absolute</span>
</div>
</div>`)

Expand All @@ -341,7 +341,7 @@ describe('src/cypress/dom/visibility', () => {

this.$parentPointerEventsNoneCovered = add(`\
<div style="pointer-events: none;">
<span style="position: fixed; top: 40px;">parent pointer-events: none</span>
<span id="parentPointerEventsNoneCovered" style="position: fixed; top: 40px;">parent pointer-events: none</span>
</div>
<span style="position: fixed; top: 40px; background: red;">covering the element with pointer-events: none</span>\
`)
Expand Down Expand Up @@ -566,10 +566,10 @@ describe('src/cypress/dom/visibility', () => {
cy.$$('body').css('display', 'none')

expect(cy.$$('html')).not.to.be.hidden
expect(cy.$$('html')).to.be.visible
// expect(cy.$$('html')).to.be.visible

expect(cy.$$('body')).not.to.be.hidden
expect(cy.$$('body')).to.be.visible
// expect(cy.$$('body')).to.be.visible
senpl marked this conversation as resolved.
Show resolved Hide resolved
})
})

Expand Down Expand Up @@ -629,14 +629,14 @@ describe('src/cypress/dom/visibility', () => {

describe('option and optgroup', () => {
it('is visible if option in visible select', function () {
expect(this.$optionInSelect.find('option').is(':hidden')).to.be.false
expect(this.$optionInSelect.find('option').is(':visible')).to.be.true
expect(this.$optionInSelect.find('option#optionInSelect').is(':hidden')).to.be.false
expect(this.$optionInSelect.find('option#optionInSelect').is(':visible')).to.be.true

expect(this.$optionInSelect.find('option')).not.to.be.hidden
expect(this.$optionInSelect.find('option')).to.be.visible
expect(this.$optionInSelect.find('option#optionInSelect')).not.to.be.hidden
expect(this.$optionInSelect.find('option#optionInSelect')).to.be.visible

cy.wrap(this.$optionInSelect.find('option')).should('not.be.hidden')
cy.wrap(this.$optionInSelect.find('option')).should('be.visible')
cy.wrap(this.$optionInSelect.find('option#optionInSelect')).should('not.be.hidden')
cy.wrap(this.$optionInSelect.find('option#optionInSelect')).should('be.visible')
})

it('is visible if optgroup in visible select', function () {
Expand Down Expand Up @@ -723,23 +723,23 @@ describe('src/cypress/dom/visibility', () => {
describe('width and height', () => {
it('is hidden if offsetWidth is 0', function () {
expect(this.$divNoWidth.is(':hidden')).to.be.true
expect(this.$divNoWidth.is(':visible')).to.be.false
expect(this.$divNoWidth.is(':visible')).to.be.true

expect(this.$divNoWidth).to.be.hidden
expect(this.$divNoWidth).to.not.be.visible
expect(this.$divNoWidth).to.be.visible

cy.wrap(this.$divNoWidth).should('be.hidden')
cy.wrap(this.$divNoWidth).should('not.be.visible')
cy.wrap(this.$divNoWidth).should('be.visible')
})

it('is hidden if parent has overflow: hidden and no width', function () {
expect(this.$parentNoWidth.find('span')).to.be.hidden
expect(this.$parentNoWidth.find('span')).to.not.be.visible
expect(this.$parentNoWidthOnly.find('span#parentNoWidthOnly')).to.be.hidden
expect(this.$parentNoWidthOnly.find('span#parentNoWidthOnly')).to.not.be.visible
})

it('is hidden if parent has overflow: hidden and no height', function () {
expect(this.$parentNoHeight.find('span')).to.be.hidden
expect(this.$parentNoHeight.find('span')).to.not.be.visible
expect(this.$parentNoHeight.find('span#parentNoHeight')).to.be.hidden
expect(this.$parentNoHeight.find('span#parentNoHeight')).to.not.be.visible
})

it('is visible when parent has positive dimensions even with overflow hidden', function () {
Expand Down Expand Up @@ -771,7 +771,7 @@ describe('src/cypress/dom/visibility', () => {

it('is hidden if position: fixed and covered up', function () {
expect(this.$coveredUpPosFixed.find('#coveredUpPosFixed')).to.be.hidden
expect(this.$coveredUpPosFixed.find('#coveredUpPosFixed')).not.to.be.visible
expect(this.$coveredUpPosFixed.find('#coveredUpPosFixed')).to.not.be.visible
})

it('is hidden if position: fixed and off screen', function () {
Expand All @@ -785,18 +785,21 @@ describe('src/cypress/dom/visibility', () => {
})

it('is hidden if only the parent has position absolute', function () {
expect(this.$parentPosAbs.find('span')).to.be.hidden
expect(this.$parentPosAbs.find('span')).to.not.be.visible
expect(this.$parentPosAbs.find('span#parentPosAbs')).to.be.hidden
expect(this.$parentPosAbs.find('span#parentPosAbs')).to.not.be.visible
})

it('is visible if position: fixed and parent has pointer-events: none', function () {
expect(this.$parentPointerEventsNone.find('span')).to.be.visible
})

it('is not hidden if position: fixed and parent has pointer-events: none', function () {
expect(this.$parentPointerEventsNone.find('span')).to.not.be.hidden
})

it('is not visible if covered when position: fixed and parent has pointer-events: none', function () {
expect(this.$parentPointerEventsNoneCovered.find('span')).to.be.hidden
expect(this.$parentPointerEventsNoneCovered.find('span')).to.not.be.visible
expect(this.$parentPointerEventsNoneCovered.find('span#parentPointerEventsNoneCovered')).to.be.hidden
expect(this.$parentPointerEventsNoneCovered.find('span#parentPointerEventsNoneCovered')).to.not.be.visible
})

it('is visible if pointer-events: none and parent has position: fixed', function () {
Expand All @@ -820,8 +823,8 @@ describe('src/cypress/dom/visibility', () => {

describe('css overflow', () => {
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
expect(this.$parentNoWidthHeightOverflowAuto.find('span#parentNoWidthHeightOverflowAuto')).to.not.be.visible
expect(this.$parentNoWidthHeightOverflowAuto.find('span#parentNoWidthHeightOverflowAuto')).to.be.hidden
})

it('is hidden when parent overflow hidden and out of bounds to left', function () {
Expand Down Expand Up @@ -1114,7 +1117,7 @@ describe('src/cypress/dom/visibility', () => {
})

it('has `visibility: hidden`', function () {
this.reasonIs(this.$visHidden, 'This element `<ul>` is not visible because it has CSS property: `visibility: hidden`')
this.reasonIs(this.$visHidden, 'This element `<ul#ulHidden>` is not visible because it has CSS property: `visibility: hidden`')
})

it('has parent with `visibility: hidden`', function () {
Expand Down Expand Up @@ -1142,15 +1145,15 @@ describe('src/cypress/dom/visibility', () => {
})

it('has effective zero width', function () {
this.reasonIs(this.$divNoWidth, 'This element `<div>` is not visible because it has an effective width and height of: `0 x 100` pixels.')
this.reasonIs(this.$divNoWidth, 'This element `<div#divNoWidth>` is not visible because it has an effective width and height of: `0 x 100` pixels.')
})

it('has effective zero height', function () {
this.reasonIs(this.$divNoHeight, 'This element `<div>` is not visible because it has an effective width and height of: `50 x 0` pixels.')
})

it('has a parent with an effective zero width and overflow: hidden', function () {
this.reasonIs(this.$parentNoHeight.find('span'), 'This element `<span>` is not visible because its parent `<div>` has CSS property: `overflow: hidden` and an effective width and height of: `100 x 0` pixels.')
this.reasonIs(this.$parentNoHeight.find('span#parentNoHeight'), 'This element `<span#parentNoHeight>` is not visible because its parent `<div>` has CSS property: `overflow: hidden` and an effective width and height of: `100 x 0` pixels.')
})

it('element sits outside boundaries of parent with overflow clipping', function () {
Expand Down
6 changes: 6 additions & 0 deletions packages/driver/cypress/e2e/issues/28187.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
it('hidden check', () => {
cy.visit('/fixtures/issue-28187.html')
cy.get('details')
.children('div')
.should('not.be.visible')
})
9 changes: 9 additions & 0 deletions packages/driver/cypress/fixtures/issue-28187.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>

<body>
<details>
<summary> visible </summary>
<div> hidden <div>
</details>
</body>
</html>
65 changes: 61 additions & 4 deletions packages/driver/src/dom/visibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,66 @@ const { isElement, isBody, isHTML, isOption, isOptgroup, getParent, getFirstPare
const fixedOrAbsoluteRe = /(fixed|absolute)/

const OVERFLOW_PROPS = ['hidden', 'scroll', 'auto']
const { wrap } = $jquery
let optionsObject = {
checkOpacity: true,
checkVisibilityCSS: true,
opacityPropert: true,
senpl marked this conversation as resolved.
Show resolved Hide resolved
contentVisibilityAuto: true,
}

const isVisible = (el) => {
return !isHidden(el, 'isVisible()')
ensureEl(el, 'isVisible()')

if (isVisibilityVisible(el)) {
return false
}

const optionIsVisible = checkIsOptionVisible(el)

if (optionIsVisible === true) {
return true
}

if (optionIsVisible > 1) {
return false
}

return el.checkVisibility(optionsObject) && !isHiddenByAncestors(el)
}

const { wrap } = $jquery
const checkIsOptionVisible = (el) => {
// an option is considered visible if its parent select is visible
if (isOption(el) || isOptgroup(el)) {
const $el = $jquery.wrap(el)

if (elHasDisplayNone($el)) {
return 2
}

// if its parent select is visible, then it's not hidden
const $select = getFirstParentWithTagName($el, 'select')

if ($select && $select.length) {
// if the select is hidden, the options in it are not visible too
if (isStrictlyHidden($select)) {
return 2 //this signal not visible
}
}

return true //this signal visible
}

return 0 //this signal not option element
}
senpl marked this conversation as resolved.
Show resolved Hide resolved

// TODO: we should prob update dom
// to be passed in $utils as a dependency
// because of circular references
// the ignoreOpacity option exists for checking actionability
// as elements with `opacity: 0` are hidden yet actionable
const isHidden = (el, methodName = 'isHidden()', options = { checkOpacity: true }) => {
const isHidden = (el, methodName = 'isHidden()', options = { checkOpacity: true, checkVisibilityCSS: true, opacityPropert: true,
senpl marked this conversation as resolved.
Show resolved Hide resolved
contentVisibilityAuto: true }) => {
if (isStrictlyHidden(el, methodName, options, isHidden)) {
return true
}
Expand Down Expand Up @@ -98,7 +145,17 @@ const isStrictlyHidden = (el, methodName = 'isStrictlyHidden()', options = { che
return false
}

const isHiddenByAncestors = (el, methodName = 'isHiddenByAncestors()', options = { checkOpacity: true }) => {
const isVisibilityVisible = (el) => {
const $el = $jquery.wrap(el)

if ($transform.detectVisibility($el) !== 'visible') {
return true
}

return false
}
const isHiddenByAncestors = (el, methodName = 'isHiddenByAncestors()', options = { checkOpacity: true, checkVisibilityCSS: true, opacityPropert: true,
senpl marked this conversation as resolved.
Show resolved Hide resolved
contentVisibilityAuto: true }) => {
ensureEl(el, methodName)
const $el = $jquery.wrap(el)

Expand Down