cypress: After upgrade to version 3.6.1 tests start failing with " CypressError: Timed out retrying: cy.click() failed because this element is detached from the DOM"
Current behavior:
After upgrade to 3.6.1 from 3.4.1 our cypress tests start to fail for the following constructs.
cy.get('fieldset > legend > i').should('be.visible').click()
fails with
CypressError: Timed out retrying: cy.click() failed because this element is detached from the DOM.
<i expander="" id-to-expand="login-username-password-content" expanded="false" class="fa fa-plus-square ng-isolate-scope"></i>
Cypress requires elements be attached in the DOM to interact with them.
The previous command that ran was:
> cy.should()
This DOM element likely became detached somewhere between the previous and current command.
Common situations why this happens:
- Your JS framework re-rendered asynchronously
- Your app code reacted to an event firing and removed the element
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands.
https://on.cypress.io/element-has-detached-from-dom
at Object.cypressErr (/__cypress/runner/cypress_runner.js:104968:11)
at Object.throwErr (/__cypress/runner/cypress_runner.js:104923:18)
at Object.throwErrByPath (/__cypress/runner/cypress_runner.js:104955:17)
at Object.retry (/__cypress/runner/cypress_runner.js:96290:16)
at retryActionability (/__cypress/runner/cypress_runner.js:85007:19)
at tryCatcher (/__cypress/runner/cypress_runner.js:139045:23)
at Function.Promise.attempt.Promise.try (/__cypress/runner/cypress_runner.js:136320:29)
at tryFn (/__cypress/runner/cypress_runner.js:96748:21)
at whenStable (/__cypress/runner/cypress_runner.js:96783:12)
at /__cypress/runner/cypress_runner.js:96333:16
at tryCatcher (/__cypress/runner/cypress_runner.js:139045:23)
at Promise._settlePromiseFromHandler (/__cypress/runner/cypress_runner.js:136981:31)
at Promise._settlePromise (/__cypress/runner/cypress_runner.js:137038:18)
at Promise._settlePromise0 (/__cypress/runner/cypress_runner.js:137083:10)
at Promise._settlePromises (/__cypress/runner/cypress_runner.js:137162:18)
at Promise._fulfill (/__cypress/runner/cypress_runner.js:137107:18)
We followed the link from the error message and tried to change code to:
cy.get('fieldset > legend > i').should('be.visible');
cy.get('fieldset > legend > i').click()
But this didn’t change the behaviour. Also the element should be visibe from the beginning so not code is changing it in an async way.
Desired behavior:
Should not fail.
Steps to reproduce: (app code and test code)
For now we don’t have them because not everytime the same tests fail. The only common thing between them is the same or similar click behavior.
These constructs were not failing with version 3.4.1.
Versions
Ubuntu 19.04 cypress 3.6.1 Electron 73
We run the tests via yarn cypress run
Any help to find the reason would be appreciated.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 23
- Comments: 27 (4 by maintainers)
Commits related to this issue
- Attempt to fix cypress bug https://github.com/cypress-io/cypress/issues/5743 — committed to OWASP/SSO_Project by JamesCullum 4 years ago
- potential fix for cypress bug where it randomly fails clicking a react link (https://github.com/cypress-io/cypress/issues/5743) — committed to luciad/typescript-documentation by TijlB99 4 years ago
@jennifer-shehane I think it’s really sad that this was closed. Cypress docs mention in so many places how important it is to have tests not be flaky, and in this case a very simple
cy.get('button').click()
gets very flaky just because you use e.g. React.Frameworks like React might chose to re-render buttons for various reasons, and you even acknowledged this in your error message “Common situations why this happens: - Your JS framework re-rendered asynchronously” but then you fail to provide a solution for this situation, let me explain:
The reasons for a re-render can be many, and e.g. occur in a completely different feature on the same page, while it is still loading or running animations. And in your conditional testing guide you write yourself:
So I think your proposed solutions “Re-query for newly added DOM elements” or “Guard Cypress from running commands until a specific condition is met” will fall short for a reasonably complex application. In fact I ran into this problem on a big SPA with dozens of developers working on it, and some component from another team finishes loading in the middle of our test.
To solve the problem I think Cypress should just re-try and look for a new button that matches the selector. Or there should be at least a simple example code snippet on how to solve the generic problem of having a framework re-render between selector and click.
@jennifer-shehane Please read the description of the issue. I wrote that we followed the advice and it didn’t solve the issue. So if you close the issue I expect from you that you can show me where we didn’t follow this advice. Thank you.
The comment from @roblevintennis seems unrelated and closing the issue because of it is wrong IMO.
@Aeolun We’re open to PRs to implement this retry mechanism. That would be a pretty large change to our current implementation. I’ve opened an issue requesting this feature here: https://github.com/cypress-io/cypress/issues/7306
Yes, please read the recommendations from the error message for this error here: https://on.cypress.io/error-messages#cy-failed-because-the-element-you-are-chaining-off-of-has-become-detached-or-removed-from-the-dom
There’s not much Cypress can do with an element that no longer exists in the DOM, so you will need to restructure your tests to account for this is your framework mounts/unmounts DOM elements.
If anyone is encountering this issue with .click(), I found that
.click({force: true})
circumvented the problem in my case.I’m using Angular 9 and encountered the issue when trying to access a menu item of a Mat Menu (angular material) component.
The menu rendering is all js, so I would not be surprised if the issue resulted from some tricky re-rendering behavior. I first tried to simplify the command (removing commands between the .get() and .click(), as suggested in the simplified example in the docs) but that didn’t seem to help, in the end I was still seeing the issue with just
cy.get(.my-class-name).click()
; as with other posters, the test runner showed get() logging the expected element, but click() failed with the detached element issue.Following because we are having the same issue. Hoping this gets reopened and resolved. As with @leogoesger, our tests pass every time locally but fail on CI intermittently
same issue, the element does exist on the dom, but cannot find it. It works well in local, but fails in CI.
I had a problem similar to what @chopraapooja described. The following solved it for me.
This works because
cy.should
retries, and the element is re-queried each time.Example usage:
The second form lets you use arbitrary logic for finding the element.
Faced the same problem with latest version 3.8.1 on React SPA,
Initially cy.get() is able to find the element, in-between React kicks rendering cycle and click() is not able to see the reference of the element. Any clue ?
Agh. I’m experiencing this a lot with the React app I’m testing too. 😦
Just for the record. I think it’s absolutely insane that the last version of Cypress that actually works for us is 3.4. How is it possible that this flow is completely broken on newer version of Cypress?
Why doesn’t Cypress just re-query the element if it finds it detached? That’s literally what it does for every other form of ‘get’ and apparently did previously.
@wintonpc Solution didn’t work for us, but was the basis for this:
Sadly one cannot override the click command (
Cypress.Commands.overwrite('click', ...)
) because this breaks.type()
(see issue).Thanks you @wintonpc, your solution nearly fixed the issue I have been experiencing. The only addition that I needed to make for an Ember app was to add an optional
contains
parameter to thegetAttached
method. And then I added a second expectation:expect($el).to.contain(contains)
That way, I was able to call
getAttached()
and pass in expected text. This replaced my faultycy.get().should().click()
chain that would always fail on theshould()
for a DOM element that was occasionally detaching and reattaching after Cypress entered a search query and the DOM was updated, potentially multiple times, before clicking.Faced this issue on 3.8.0 with React
Not sure if it’s helpful at all, but I found the error docs for this error quite enlightening and helpful. I literally fixed it by heeding the recommendations.
I’m sure your selectors will be different, but here’s the diff that fixed my same issue:
When I read the docs it talked about how frameworks will mount/unmount behind the scenes in a way that’s not visible to our naked eyes, and that really hit home. We too are using React. But probably would be same issue for Angular, Ember, etc., maybe.
Finding a way to simplify and break apart my query’s made the tests pass for this one.
Hope it’s somehow helpful even if pretty high level and hand wavy 😃
I don’t expect this to work for everyone but what improved flakiness for me is a simple assertion:
Same here. We tried the
getAttached
solution but it didn’t work for us. It seems that the element is detached between the check and the click which suggests that it happens very quickly.The only workaround for us is a
cy.wait(50)
.