A Cypress `.click()` fails saying the Borrow button is covered by another element. How do you diagnose it?
answer
- The message names the culprit
- One point is tested, not the box
- A descendant covering is fine
- Fixed and sticky coverers get nudged
- scrollBehavior center moves the landing point
basics
~20 sRead the second element in the message: that is what sat at the point the click would have landed. Cypress tests only that one point, and it has already tried nudge-scrolling if the coverer was fixed or sticky.
solid answer
~40 sThe error prints two nodes: the element you targeted and the element Cypress found at the coordinates the event would use — the centre unless the command passed `position`. Only that single point is tested, so a half-covered button still fails when its centre is the covered part. A descendant of the target does not count as covering it; Cypress simply fires at the descendant. Before failing, Cypress re-checked on every retry and, when the coverer is `position: fixed` or `sticky`, scrolled the page to nudge the target out from under it — so a failure means the coverer was neither, or nudging never cleared it. Usual causes are a modal backdrop, a cookie banner or a toast that has not dismissed. `pointer-events: none` produces its own separate message.
code
javascript · 6 lines// wait for the coverer to go, then act
cy.get('[data-cy=availability-toast]').should('not.exist')
cy.contains('[data-cy=book-row]', 'Dune')
.find('[data-cy=borrow]')
.click({ scrollBehavior: 'center' })go deeper
Know that this failure means something is on top of the element, and that the error message itself names the element that is in the way.
Explain that only the event coordinates are tested, that a descendant does not count, and that Cypress nudges the page past fixed or sticky coverers before giving up.
Show the triage: identify the coverer, decide whether it is transient or a real state problem, and pick between waiting on its removal, scrollBehavior, and treating it as a product defect.
An interviewer at this level expects a view on repeat offenders — chat widgets, banners, debug overlays — and whether the environment should suppress them rather than every spec working around them.
## What the message is actually telling you The coverage failure prints two nodes, and the second one is the whole diagnosis: ``` cy.click() failed because this element: `<button data-cy="borrow">Borrow</button>` is being covered by another element: `<div class="cookie-banner">...</div>` Fix this problem, or use {force: true} to disable error checking. ``` Cypress computed the coordinates the event would use, asked the document what element sits at that point, and found something that is **not your target and not a descendant of it**. The second node is that element. There is a sibling message, `failed because the center of this element is hidden from view`, which is the same check when the point resolves to nothing at all — the target's centre is outside the visible area. ## Four properties of the check that shape the diagnosis - **One point, not the whole box.** Only the coordinates the event will use are tested — the element's centre unless the command passed `position` or explicit `x`/`y`. A Borrow button whose left half is clear still fails if the banner covers its centre. - **Descendants do not count.** If the element at those coordinates is inside your target, that is not coverage; Cypress fires the event there instead. - **Cypress already tried to fix it.** When the covering element is `position: fixed` or `sticky`, Cypress scrolls the page to nudge the target out from underneath it and re-checks, walking up through every scrollable container. A failure therefore means the coverer was not fixed or sticky, or nudging never cleared it. If the command ran with `scrollBehavior: false`, the nudge is not attempted at all. - **`pointer-events: none` is a separate rule.** It produces its own message naming the element and the ancestor the property was inherited from, so a click blocked that way never says "covered". ## Working through it 1. **Read the coverer's markup in the error.** A class name usually identifies it immediately: a modal backdrop, a cookie banner, a loading veil, a toast. 2. **Decide whether it is permanent or transient.** A toast that dismisses itself is a timing problem; a backdrop that is still up is a state problem — the previous step did not finish. 3. **For a transient coverer, wait on the thing that removes it**, not on the button. Asserting the toast is gone before acting makes the failure legible next time. 4. **For a fixed header the nudge did not clear**, change where the element is scrolled to: `{ scrollBehavior: 'center' }` on the command, or globally in the configuration, is the targeted fix. 5. **For a modal that should not be there**, treat the failure as a real defect in the flow the test is walking, not as an actionability problem to work around. ## Mapping the messages | Message | What it means | First move | |---|---|---| | `is being covered by another element` | Something else owns the event point | Identify the printed coverer | | `the center of this element is hidden from view` | Nothing resolves at the point | Check clipping and scroll position | | `has CSS pointer-events: none` | The target itself cannot receive mouse input | Look at the app's CSS, not the layout | | `this element is not visible` | The visibility check failed before coverage was reached | Read the reason line in the error | ## The trap of reaching for force first `{ force: true }` makes the message disappear, and it is the wrong first move here more often than anywhere else, because a coverage failure is the one check that most reliably reports a genuine user-facing problem: something really is on top of the control. A forced click fires at the button underneath the modal and the test goes green while a reader with the same modal open cannot borrow anything. Force is defensible once you have identified the coverer and decided the coverage is an artefact of the test environment — a third-party widget, a chat bubble, a debug overlay — and not of the product. One more note specific to Cypress 16: under the default `visibilityStrategy: 'modern'` the visibility check no longer treats coverage of a fixed element as hidden, so coverage is now reported by the coverage check and its two-node message rather than by a visibility failure.
- Why does the coverage check use only one point?Because that point is where the event will actually be dispatched — the element's centre by default, or whatever `position` or explicit `x`/`y` selected. Testing the whole box would answer a different question from the one that decides where the click lands.
- The message says `has CSS pointer-events: none` instead. What changed?Nothing is on top of the element; the element itself is declared unable to receive mouse input, possibly through inheritance — the error names the ancestor the property came from. That is an application CSS problem, not a layout or timing one, so scrolling and waiting will never clear it.
saying these in an interview costs you the question
- Add force: true and move on
- Cypress checks the whole element for coverage
- A child element covering the target fails the click
- Cypress never scrolls again once the element is in view
- Coverage failures are always test flakiness