Skip to content

Commit 5e30ae1

Browse files
Experimental fast visibility (#6337)
* initial experiment documentation * add default & fast visibility semantics, cross-reference links * add history entry to experiments page * add link to github issue for the experimental fast visibility experiment * add note that scrolling is a future enhancement/fix * edit pass * another edit pass --------- Co-authored-by: Bill Glesias <[email protected]>
1 parent 796c405 commit 5e30ae1

File tree

4 files changed

+208
-40
lines changed

4 files changed

+208
-40
lines changed

docs/app/core-concepts/interacting-with-elements.mdx

Lines changed: 113 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,119 @@ inform your testing strategy, and ship high-quality code with confidence.
2626

2727
:::
2828

29+
## Visibility
30+
31+
### Default Behavior
32+
33+
Cypress checks a lot of things to determine an element's visibility. The
34+
following calculations factor in CSS translations and transforms.
35+
36+
#### An element is considered hidden if:
37+
38+
- Its `width` or `height` is `0`.
39+
- Its CSS property (or ancestors) is `visibility: hidden`.
40+
- Its CSS property (or ancestors) is `display: none`.
41+
- Its CSS property is `position: fixed` and it's offscreen or covered up.
42+
- Any of its ancestors **hides overflow**\*
43+
- AND that ancestor has a `width` or `height` of `0`
44+
- AND an element between that ancestor and the element is `position: absolute`
45+
- Any of its ancestors **hides overflow**\*
46+
- AND that ancestor or an ancestor between it and that ancestor is its offset
47+
parent
48+
- AND it is positioned outside that ancestor's bounds
49+
- Any of its ancestors **hides overflow**\*
50+
- AND the element is `position: relative`
51+
- AND it is positioned outside that ancestor's bounds
52+
53+
\***hides overflow** means it has `overflow: hidden`, `overflow-x: hidden`,
54+
`overflow-y: hidden`, `overflow: scroll`, or `overflow: auto`
55+
56+
:::info
57+
58+
<strong>Opacity</strong>
59+
60+
Elements where the CSS property (or ancestors) is `opacity: 0` are considered
61+
hidden when
62+
[asserting on the element's visibility directly](/app/references/assertions#Visibility).
63+
64+
However elements where the CSS property (or ancestors) is `opacity: 0` are
65+
considered actionable and any commands used to interact with the hidden element
66+
will perform the action.
67+
68+
:::
69+
70+
### Experimental Fast Visibility
71+
72+
You can enable a faster visibility detection algorithm using [`experimentalFastVisibility`](/app/references/experiments#Experimental-Fast-Visibility).
73+
74+
This algorithm is more accurate and less resource-intensive than the default behavior, but it has slightly different semantics.
75+
76+
When `experimentalFastVisibility` is enabled, an element is considered hidden if:
77+
78+
- Its `width` or `height` is `0`
79+
- Its computed CSS properties prevent it from being clicked on or visible:
80+
- `visibility: hidden`
81+
- `opacity: 0`
82+
- `display: none`
83+
- `pointer-events: none`
84+
- It is positioned fully outside of the browser viewport
85+
- _this is a known compatibility issue with the legacy visibility algorithm_ when it comes to asserting visibility. It will be addressed during the course of this experiment.
86+
- It is fully covered or clipped by another element
87+
88+
Keep up-to-date with the the progress of this experiment in the [Cypress repository](https://github.com/cypress-io/cypress/issues/33043).
89+
90+
#### Limitations
91+
92+
Experimental fast visibility is an experimental feature that is still under development. It uses different semantics from the legacy visibility algorithm, and does not yet fully support all testing scenarios.
93+
94+
- Shadow DOM is not yet supported. Tests that interact with Shadow DOM elements may fail or behave incorrectly.
95+
- Elements outside of the browser's viewport are not yet correctly identified as visible. Scroll elements into view before testing.
96+
97+
#### Common Compatibility Issues
98+
99+
When enabling fast visibility, you may encounter differences in how elements are detected. The fast algorithm provides more geometrically accurate visibility detection, which may reveal that elements previously considered visible are actually hidden.
100+
101+
**Elements Outside Viewport**
102+
103+
Elements positioned outside the viewport are now correctly identified as hidden. Scroll elements into view before testing:
104+
105+
```javascript
106+
// Before
107+
cy.get('.off-screen-element').should('be.visible')
108+
109+
// After
110+
cy.get('.off-screen-element').scrollIntoView().should('be.visible')
111+
```
112+
113+
**Covered Elements**
114+
115+
Elements covered by other elements are now correctly identified as hidden. Test the covering element or the user interaction that reveals the covered element:
116+
117+
```javascript
118+
// Test the user action that reveals the element
119+
cy.get('.toggle-button').click()
120+
cy.get('.covered-element').should('be.visible')
121+
```
122+
123+
**Zero-Dimension Containers**
124+
125+
Containers with zero dimensions are now correctly identified as hidden. Test the child element instead of the container:
126+
127+
```javascript
128+
// Test the child element that should be visible
129+
cy.get('.zero-dimension-container .child-element').should('be.visible')
130+
```
131+
132+
**Elements with `pointer-events: none`**
133+
134+
Elements with `pointer-events: none` may be detected as hidden when they are visible. Do not assert visibility on elements with `pointer-events: none`, as they cannot be interacted with.
135+
136+
:::caution
137+
138+
**Important:** If tests fail after enabling fast visibility, the fast algorithm is likely correct and tests should be updated to match the actual behavior.
139+
140+
:::
141+
29142
## Actionability
30143

31144
Some commands in Cypress are for interacting with the DOM such as:
@@ -71,45 +184,6 @@ Whenever Cypress cannot interact with an element, it could fail at any of the
71184
above steps. You will usually get an error explaining why the element was not
72185
found to be actionable.
73186

74-
### Visibility
75-
76-
Cypress checks a lot of things to determine an element's visibility. The
77-
following calculations factor in CSS translations and transforms.
78-
79-
#### An element is considered hidden if:
80-
81-
- Its `width` or `height` is `0`.
82-
- Its CSS property (or ancestors) is `visibility: hidden`.
83-
- Its CSS property (or ancestors) is `display: none`.
84-
- Its CSS property is `position: fixed` and it's offscreen or covered up.
85-
- Any of its ancestors **hides overflow**\*
86-
- AND that ancestor has a `width` or `height` of `0`
87-
- AND an element between that ancestor and the element is `position: absolute`
88-
- Any of its ancestors **hides overflow**\*
89-
- AND that ancestor or an ancestor between it and that ancestor is its offset
90-
parent
91-
- AND it is positioned outside that ancestor's bounds
92-
- Any of its ancestors **hides overflow**\*
93-
- AND the element is `position: relative`
94-
- AND it is positioned outside that ancestor's bounds
95-
96-
\***hides overflow** means it has `overflow: hidden`, `overflow-x: hidden`,
97-
`overflow-y: hidden`, `overflow: scroll`, or `overflow: auto`
98-
99-
:::info
100-
101-
<strong>Opacity</strong>
102-
103-
Elements where the CSS property (or ancestors) is `opacity: 0` are considered
104-
hidden when
105-
[asserting on the element's visibility directly](/app/references/assertions#Visibility).
106-
107-
However elements where the CSS property (or ancestors) is `opacity: 0` are
108-
considered actionable and any commands used to interact with the hidden element
109-
will perform the action.
110-
111-
:::
112-
113187
### Disability
114188

115189
Cypress checks whether the `disabled` property is `true` on a

docs/app/references/assertions.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,14 @@ Watch the short video
258258
["Multiple elements and should('be.visible') assertion"](https://www.youtube.com/watch?v=LxkrhUEE2Qk)
259259
that shows how to correctly check the visibility of elements.
260260

261+
:::info
262+
263+
**Visibility Semantics**
264+
265+
For detailed information about how Cypress determines element visibility, including the default behavior and the experimental fast visibility algorithm, see [Visibility](/app/core-concepts/interacting-with-elements#Visibility).
266+
267+
:::
268+
261269
### Existence
262270

263271
```javascript

docs/app/references/error-messages.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ are not.
238238

239239
You may see a variation of this message for 4 different reasons:
240240

241-
1. The element is not visible
241+
1. The element is not [visible](/app/core-concepts/interacting-with-elements#Visibility)
242242
2. The element is being covered by another element
243243
3. The element's center is hidden from view
244244
4. The element is disabled

docs/app/references/experiments.mdx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ configuration to Cypress.
3838
| `experimentalModifyObstructiveThirdPartyCode` | `false` | Whether Cypress will search for and replace obstructive code in third party `.js` or `.html` files. NOTE: Setting this flag removes [Subresource Integrity (SRI)](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). |
3939
| `experimentalSourceRewriting` | `false` | Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. See [#5273](https://github.com/cypress-io/cypress/issues/5273) for details. |
4040
| `experimentalWebKitSupport` | `false` | Enable experimental support for running tests in WebKit. When set, installs of `playwright-webkit` will be detected and available in Cypress. See [Launching Browsers](/app/references/launching-browsers#WebKit-Experimental) for more information. |
41+
| `experimentalFastVisibility` | `false` | Enables a faster visibility detection algorithm using point sampling. This can significantly improve performance when interacting with complex DOM structures. See [Experimental Fast Visibility](/app/references/experiments#Experimental-Fast-Visibility) for more details. |
4142
| `retries.experimentalStrategy` | N/A | Applies a strategy for test retries according to your "flake tolerance"; options are detect-flake-but-always-fail or detect-flake-and-pass-on-threshold. See [Experimental Retries](/app/references/experiments#Experimental-Flake-Detection-Features) for more details. |
4243
| `retries.experimentalOptions` | N/A | Sets retries strategy-specific options like maxRetries, passesRequired, and stopIfAnyPassed. See [Experimental Retries](/app/references/experiments#Experimental-Flake-Detection-Features) for more details. |
4344

@@ -124,6 +125,90 @@ modified HTML or JS value.
124125

125126
:::
126127

128+
## Experimental Fast Visibility
129+
130+
The `experimentalFastVisibility` option enables a faster visibility detection algorithm that uses point sampling instead of the legacy algorithm. This can significantly improve performance when interacting with complex DOM structures.
131+
132+
### Benefits
133+
134+
The fast visibility algorithm provides several advantages over the legacy algorithm:
135+
136+
- **Better performance**: Constant-time in the best case, and bounded exponential in the worst case (when point sampling on a fully hidden element)
137+
- **Memory efficiency**: Reduces memory usage and prevents browser crashes during visibility checks
138+
- **Reduced layout thrashing**: Avoids repeated access of CSS properties that require layout recalculation
139+
- **Better coverage detection**: Can detect when an element is fully covered by positioned elements outside its ancestor tree
140+
141+
### How It Works
142+
143+
The fast visibility algorithm:
144+
145+
1. Assumes `body` and `html` are always visible (consistent with current visibility behavior)
146+
2. Uses the built-in `checkVisibility` method as a first pass, with all options enabled
147+
3. For `<option>` or `<optgroup>` elements, defers to the visibility of the parent `<select>` element
148+
4. If the element is still considered visible, performs a more comprehensive check with an adaptive point sampling algorithm
149+
150+
The point sampling algorithm:
151+
152+
- Checks the four corners and center of the element's bounding rectangle
153+
- If none of these points are visible, subdivides the bounding rectangle into four sub-rectangles and performs the same sampling recursively
154+
- Returns `true` if any part of the element is visible
155+
- Stops subdividing when rectangles fall below 1px in both dimensions or reach a maximum depth
156+
157+
### Configuration
158+
159+
Enable fast visibility by setting `experimentalFastVisibility` to `true` in your Cypress configuration:
160+
161+
:::cypress-config-example
162+
163+
```js
164+
{
165+
experimentalFastVisibility: true
166+
}
167+
```
168+
169+
`experimentalFastVisibility` can also be enabled or disabled on a per-test or per-suite basis by passing the `experimentalFastVisibility` option to the test or suite function:
170+
171+
:::cypress-config-example
172+
173+
```js
174+
// Enable fast visibility for the entire suite
175+
describe('My Test Suite', { experimentalFastVisibility: true }, () => {
176+
// Disable fast visibility for this test
177+
it(
178+
'Requires legacy visibility to pass',
179+
{ experimentalFastVisibility: false },
180+
() => {
181+
// ...
182+
}
183+
)
184+
})
185+
```
186+
187+
:::
188+
189+
### When to Use
190+
191+
Consider enabling `experimentalFastVisibility` if you:
192+
193+
- Experience performance issues or browser crashes during visibility checks
194+
- Have complex DOM structures with many nested elements
195+
- Need faster test execution times
196+
197+
:::caution
198+
199+
The experimental fast visibility algorithm has slightly different semantics for what it considers visible, when compared with the legacy algorithm. Tests that rely on edge-case behavior of the legacy visibility algorithm may fail or behave incorrectly.
200+
201+
- Shadow DOM is not yet supported. Tests that interact with Shadow DOM elements may fail or behave incorrectly.
202+
- Elements outside of the browser's viewport are not yet correctly identified as visible. Scroll elements into view before testing.
203+
204+
:::
205+
206+
:::info
207+
208+
For detailed information about how the fast visibility algorithm works, its differences from the legacy algorithm, and solutions to common compatibility issues, see the [Experimental Fast Visibility](/app/core-concepts/interacting-with-elements#Experimental-Fast-Visibility) section in the [Interacting with Elements](/app/core-concepts/interacting-with-elements) guide.
209+
210+
:::
211+
127212
## Experimental Flake Detection Features
128213

129214
### Experimental Test Retries
@@ -281,6 +366,7 @@ configuration object:
281366

282367
| Version | Changes |
283368
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
369+
| [15.8.0](/app/references/changelog#15-8-0) | Added support for `experimentalFastVisibility` experiment. |
284370
| [15.4.0](/app/references/changelog) | Removed `experimentalStudio`. Cypress Studio is available as default behavior. |
285371
| [14.0.0](/app/references/changelog#14-0-0) | Removed `experimentalJustInTimeCompile` and `experimentalSkipDomainInjection` and made it default behavior. |
286372
| [13.14.0](/app/references/changelog#13-14-0) | Added support for configuring the Experimental Just-In-Time (JIT) Compiling for component testing via `experimentalJustInTimeCompile`. |

0 commit comments

Comments
 (0)