-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Re-query elements that are found 'detached' from the DOM #7306
Comments
Is there a workaround available in the meantime? Perhaps writing a custom |
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
Per #5743 (comment): .click({force: true}) ... is a decent enough solution, but it's vague without a comment. |
@stevenvachon I tried that several times in several of the scripts and it still hasn't worked for me. |
We ran into this issue so many times it was making our tests very flakey. We've started using the |
|
@FayP how can you use waitUntil with an each loop? here is one of my each loops that I like to loop through. cy.get(".mx-auto > .checkbox-col > .form-check")
.each(($el, index, $list) => {
cy.wrap($el).click({force: true});
cy.get(".col-md-4 > .actions-contianer > .btn-secondary").click();
cy.get(".mb-0 > :nth-child(2) > .btn").click();
cy.get(".jobs-list-nav > :nth-child(1) > .nav-link").click();
}) |
@eabusharkh0 I'm not sure which element you're trying to target in that code snippet. But in theory you could target anything with a |
So I managed to get it working (at least in few of my tests) with wait-until plugin: cy.waitUntil(() =>
cy.get('.someSelector')
.as('someAlias')
.wait(10) // for some reason this is needed, otherwise next line returns `true` even if click() fails due to detached element in the next step
.then($el => Cypress.dom.isAttached($el)),
{ timeout: 1000, interval: 10 })
.get('@someAlias')
.click() |
This comment was marked as duplicate.
This comment was marked as duplicate.
Unfortunately, it does not work for me. I solved my issue using the cy
.get('[data-cy="user_row"]')
.find('[data-cy="user_row_cell"]')
.should('be.visible')
.then((e) => {
Cypress.$(e).click();
}) It does not work for all cases but it works fine for my case. Maybe it will be useful to someone. |
All solutions here are at best hacky workarounds. IMO, This re-query should be done systematically, without option to enforce the Retry-ability core-concept of Cypress. For now, my only not-100%-safe workaround is a cy.wait(xxx) before any risky selector. It goes against core-concepts of this project. |
AngularJS and Angular users can use https://angular.io/api/core/Testability We have something like this in our legacy AngularJS tests. I don't know if React has an equivilent api. Cypress.Commands.add('waitUntilAngularStable', () => {
const getAngular = () => cy.window().its('angular', {log: false});
getAngular().then((angular) => {
return new Cypress.Promise(resolve => {
angular.getTestability('body').whenStable(() => {
resolve();
});
});
});
}); Used like cy.get('.back-btn').click();
cy.waitUntilAngularStable();
cy.get('.container').should('not.be.visible'); |
One viable alternative is to use Copy-paste the following to your Cypress.Commands.add("get$", (selector) => {
return cy.wrap(Cypress.$(selector)).should("have.length.gte", 1);
}); And now you should be able to click the button using the cy.get$("#btnId").click(); This works because |
The time of a machine's interactions with elements on a site differ significantly than of a regular user's interactions. By a lot, in fact! That is why such things happen. I am currently having the same issue with a As the user is typing into the searchbox, the suggestions refresh rather quickly on the server, probably on every character input, but does not manage to update in real-time for the user as well, which is why it takes a bit of time for the suggestions to refresh after the user is done typing. A user would typically wait a bit before the different suggestions calm down and stay in place, then click on the right one to advance. This is why As the machine is done typing, the suggestions list might not have updated for the cilent (machine) instantly, however, the particular suggestion that meets the criteria is found at the bottom of the list, but would appear at the top right after the suggestions list finally updates for the client. The machine should now wait a bit until it is assumed that the list has been updated for the client and ready for its elements to be interacted with. Sorry for my bad english, but I'm hoping I covered some benefits of |
@uberwire that is not a valid use of Cy.wait. Cy.get has a built in wait so if your css selector is correct it will wait till it becomes the first element in the list. |
I was not sure that my Cy ran synchronously, so I deleted my previous comment and rebuilt my tests. I separated actions and asserts to different describe/it sections and now it must work synchronously. But I get the error from time to time. |
According to my context, |
@uberwire in your example, you should be able to wait for the element and position within the container since it should be deterministic that once you've entered X characters should it appear at the top of the list. |
Safer and more practical approach in that case. Unless, the element an actual user is looking for gets found at the bottom of the list while another element that meets the same criteria gets found at the top of the list before the list fully refreshes, getting caught in the The user can stop typing mid-way after seeing the desired result at the bottom of the list and interact, but that is not what is being tested in my case. |
more on this behaviour here |
This comment was marked as duplicate.
This comment was marked as duplicate.
Same problem even if using the waitUntil library: cy.get('[data-cy=acceptButton]').click({ force: true })
cy.waitUntil( () => cy.get('[data-cy=acceptButton]') ).click()
cy.waitUntil( () => cy.get('[data-cy=acceptButton]') ).click({ force: true }) element is DETACHED from the DOM (not always, but randomly) |
How can we trust the cypress test if it doesn't work as expected? cy.get('[data-cy=acceptOfferButton]').as('acceptOfferButton')
cy.get('@acceptOfferButton').should('be.visible')
cy.get('@acceptOfferButton').click({ force: true }) all commands pass successfully except last, sometimes is clicking but most of the time not. How can we work with react component if the framework re-rendendered asynchronously? |
Yeah, if not so many tests to rewrite many people will do that. |
Haahh)) true |
Hahahaha, really? |
This work has been completed! Thank you @BlueWinds!!! 🎉 These changes will be making its debut in the Cypress 12 release. |
It's been so long I've forgotten why this is good / needed 😅 Is there a blog post / docs about why this is needed and how to enable it? (or maybe it's just automatically enabled?) |
Maybe this will happen when a framework such as Next.js / another React metaframework switches pages asynchronously and Cypress does not know about this change? We are continuously having problems still with this in Cypress, definitely. Currently we usually just add an intercept for this network request to cy.intercept('GET', '/_next/data/**').as('nextPageJson');
cy.contains('a', 'Upgrade').should('be.visible').click();
// Wait for page JSON to load to avoid Cypress continuing too quickly
// with next steps before Next.js page navigation has completed
cy.wait('@nextPageJson').then((getInterception) => {
expect(getInterception.response?.statusCode).to.equal(200, 'response.statusCode');
});
// Without the intercept above, Cypress does not know that something has
// changed and this pathname check will fail
cy.location('pathname').should('equal', '/p/cohort-access-levels'); |
@karlhorky This has been a fundamental change to how Cypress stores and managed DOM element resolution. No changes are needed on your end. Before these changes, Cypress stored a single DOM element (or window) reference that is passed between a set of commands to assert on, which could become stale as the App under test updated. With these changes, Cypress will now re-query for the DOM element (window object) to ensure its always the most up-to-date reference to verify. Blue has done a great job of re-vamp our Retry-ability guide to explain how this works. This is currently out for review - but will be live very soon. |
Nice, so I guess I'm understanding correctly that after Cypress 12, we will be able to remove these intercepts related to clicking on a link that will cause a navigation and async page update eg. using -cy.intercept('GET', '/_next/data/**').as('nextPageJson');
cy.contains('a', 'Upgrade').should('be.visible').click();
-// Wait for page JSON to load to avoid Cypress continuing too quickly
-// with next steps before Next.js page navigation has completed
-cy.wait('@nextPageJson').then((getInterception) => {
- expect(getInterception.response?.statusCode).to.equal(200, 'response.statusCode');
-});
-
cy.location('pathname').should('equal', '/p/cohort-access-levels'); |
Ok great, I guess it's this file here: https://github.com/cypress-io/cypress-documentation/blob/5501639a14a5778e25da776893a9c14e7a106434/content/guides/core-concepts/retry-ability.md |
I believe so, but I'll def to @BlueWinds . |
There will be a blog post - I'll link here when it goes up. It will be automatically enabled - no changes needed to get the functionality.
Yep, exactly so. @emilyrohrbough explained it a bit above; I'll leave a fuller description to the docs. You found the right page, https://github.com/cypress-io/cypress-documentation/blob/5501639a14a5778e25da776893a9c14e7a106434/content/guides/core-concepts/retry-ability.md. I'll also link this again once we merge in the docs.
Exactly so! |
Released in This comment thread has been locked. If you are still experiencing this issue after upgrading to |
Current status
Update 12/06/2022: This work as been complete and will be released in Cypress v12 today.
This is currently in progress. See #7306 (comment), #7306 (comment) and #7306 (comment) for a brief description of the plan and updates on current status.
Current behavior:
Currently when Cypress queries an element that was passed from a parent element (say in
.get()
,.click()
or.find()
), if the element at some point becomes detached from the DOM, we throw an error:Desired behavior:
Really users just want to re-query the DOM for the previously found element and click on the new element. Because it's more often than not that the framework underneath has created a completely new DOM element and inserted it into the DOM.
The proposal is to re-query the previously found element and continue the command with any newfound elements. Perhaps this could be an option passed to the command to allow this or not.
Reproducible Example
Provided in #9032
index.html
spec.js
Related issues
The text was updated successfully, but these errors were encountered: