Closed peterklijn closed 4 years ago
Same here, we create a modal with a form and 2 out of 10 times Cypress can not find the required elements
I'm also having this problem. I just came here to say I hope to see it prioritized. Cypress is great, and many thanks to everyones hard work. However, things like this are critical to sustainable test architecture.
Hi everyone, we're still working on better error recovery and messaging. We're also working on test retry support, so even if a test fails 10% of the time, it will retry and not fail the whole suite.
In the meantime you can try out a plugin that enables retries: https://github.com/Bkucera/cypress-plugin-retries
let me know if this helps, and open any issues you find in that repo. Thanks!
@Bkucera - retrying tests is not a solution!!
@Saibamen correct, it isn't a solution. But it's relevant to this issue, and hopefully it helps you out.
I have the same issue here.
.invoke('width').should('be.gt', 0)
.should(be.visible)
cy.wait(300)
Tried all above, still failed. The plugin also didn't help my case: https://github.com/Bkucera/cypress-plugin-retries
@indrolie can you provide more context or a reproducible example of the failure? Are you using a framework, what does your cypress code look like?
We are facing somewhat similar issue where cypress finds 2 input elements on page although after final rendering there's only one such element. So as the first instance of element become detached, cypress throws the error as we try to type in that input box:
CypressError: cy.type() can only be called on a single element. Your subject contained 2 elements.
we have 2 solutions working for us:
cy.get(selector).wait(500)
cy.get(selector).last()
I reconfirm that after final rendering is done, there's only element on the page with above selector but may be cypress is not waiting enough for the page to render finally
I think the thread already found the problem (not retrying the whole chain / holding onto elements which are supposed to have been detached)
The most horribly robust is solution is to make your wait long enough for things to settle. I mean a hard wait that's much longer then 500
to give it a good time to settle, before you attempt to get your element. This means cypress will not get the original item that was then detached, it won't be cached, and it and can pass.
Hopefully one day the chain retryability / memory leak issue will get resolved. Otherwise if a long enough hard wait doesn't fix the issue for you guys, perhaps a reproducible example can be supplied other sources of error could be identified.
PS: my hard wait was probably at least a second, but you might need 2 or 3. I've only encountered this twice as we use primarily custom shadow commands at my work and don't use the cypress get as much.
Another potentially better solution (and faster if you require too many waits) is a custom get
command which mimics the native command but completely lets the element be garbage collected and retries the query selection against the latest Dom. I have a custom shadow get command which behaves this way and it is reliable despite me knowing there are definitely rerenders occurring at times. I only need to add very short hard waits for promises for async handles to be applied since they're not always there by the time the click goes through, but I don't need to add arbitrary large waits for the Dom rendering to settle.
By making a custom command which retries the whole chain, it's reasonable to expect that the issue would be resolved temporarily. Of course such a core change to the basic command will take time to resolve, so a better work around would be nice for most people. You can always use the overwrite or override command I think too, to simplify the transition. The get command is actually pretty basic once you understand it.
If work wasn't so busy I'd gladly try to make this command but will not have time for a few days
Element visible, works well, test fails randomly.
Added assertion for width and wait(100)
as suggested workaround. Worked locally, but fails when running in CI: either get same error about "effective width and height of 0x0..." or proceeds next to click(), but the click itself doesn't work (in my case dropdown should appear)
I had this problem as well.
How I fix is that don't use cypress selector playground
. Insteand, using chrome dev tool and copy selector
of the element.
If use this copied selector in cypress test, It works fine. e.g
cy.get('#react-root > div > div > div > div.theme__my__cyeme > div.app_content > div:nth-child(2) > div > div > section > div > div.my_layout > div > div > button').click()
Some element emits same error, even though use chrome dev tools.
I had a similar problem, but in my case i found out that in onclick function i set condition: if (event.originalEvent instanceof MouseEvent) which caused cypress click() was not successful.
We had similar issues. We discovered that an API call was basically reloading our component in the background - and it was happening so fast that the tests "usually" didn't even notice. That produced the "flake" of it periodically failing. Once we understood that, we resolved the tests.
I had this problem quite a lot. I use cypress for react project. Those are what I had
.click()
will be timed out.loading indicator
and it caused re-render and cy.get()
get not fully rendered component.Above cases, .invoke('width').should('be.greaterThan', 0);
might solve prolems, But some tries will be failed which is worse than not working.
The solution will be that to make sure render is fully finished which I couldn't find
Below are what I tried
cy.wait(1000)
cy.get(loading indicator).should('not.be.visible') // If you have loading indicator
cy.get(target).invoke('width').should('be.greaterThan', 0)
cy.get(target)
.should('be.visible') // To make sure, selected element is caught when not during re-render
.click()
should('be.visible') solved many problems for me. It prevents to get element during re-render after Check width
It seems cypress will not re-select element whenget()
is pedning,
It would be wonderful, if cypress re-select element when click() or type()
has these problems before timed out
e.g get -> if click failed due to those problem -> get agian -> try click -> if failed, get -> .....
To pile on here, I'm also having this issue on a specific page. I tried asserting the width, which was returned correctly, but click (even with the force flag) fails to work.
There's a good chance I wouldn't be able to make an MVP of this since this works across the rest of an app I'm working on. There are no pending requests and it seems like Cypress is picking up on a partial detached element (might be some app JS didn't fully finish running, haven't dug through everything yet 🤯).
The only "solution" so far is an arbitrary timeout after each cy.visit
which leads me to believe the DOM is just not ready fast enough. (v3.2.0)
Having this issue too.
I'm using Angular and the element is definitely visible.
Test is flaky too on my side but even weirder: Locally with Chromium it seems to be fine whereas on CI with the headless browser it's never passing (either never or very not often, not quite sure).
I need to click on that element as it's an important path to my navigation and I'm really stuck here.
I was targeting it using contains
and the text, I tried a proper selector with data-
and cy.get
but no luck either.
@maxime1992 So far this is the best way I could come up with to reduce flakyness. Basically every line of my test looks like this:
cy.get('#my-selector' ).should('be.visible').click(x,y, { force: true });
We use the assertion to make sure the element is visible so Cypress can actually click it. Also we define specific coordinates (you need to calculate the coordinates relative to the elements and parents position yourself there are helper functions for this) and finally we force the click without checking it again, because we asserted before, that it is visible. The coordinates are very helpful because if you click on an element Cypress defaults the clicks position to the center (which could be hidden by a another element). This reduces errors thrown by cy.click()
and the tests actually work without too much flaky behavior for me, because every line is using that workaround, since I record most of my testing code with an extension.
The only times I need to have wait statements is for my XHR's. With this workaround I do not have the need for manual cy.wait()
statements, but I would hope this issue will be solved soon. This workaround is by no means pretty, but at least you get stability until the issue is solved.
@DennisLoska thanks, I tried what you said and indeed it's enough (for now).
Thanks for unlocking this situation until a proper fix comes out :)
I tried doing a force click twice and that worked for me.
cy.get('.checkbox').click({ force: true });
cy.get('.checkbox').click({ force: true });
Edit: A more robust and reliable solution would be something like
cy.get('.checkbox').check({ force: true });
cy.get('.checkbox').then(
(el) => !el.checked && cy.get('.checkbox').check({ force: true })
);
I tried doing a force click twice and that worked for me.
cy.get('.checkbox').click({ force: true }); cy.get('.checkbox').click({ force: true });
Worked for me. Thanks so much!
I have a similar problem using vue.js. A modal dialog opens after a button click, but cypress never sees the dialog or any of its contents. I tried going all the way back to the enclosing div, but cypress did not see it, the form, or any of the controls.
I even added a hard wait before trying to click. Here's the amended code:
cy.get('#tabCollectContent > div > #toolbarCollectSites > .btn-group > #cmdInitiateSurvey').click()
.wait(1000)
cy.get('div#modalNormal.modal.hid.fade-in').click()
cy.get('div#modalNormal.modal.hid.fade-in > form#surveyInit').click()
cy.get('#surveyInit > div').click()
cy.get('#surveyInit > div > div.span9').click()
cy.get('#surveyInit > div > div.span9 > div.hidden.panel.panel1').click()
@bsell93 that seems really risky.
If you click twice, and if for some reason the first one is working at some point, the second click will do the opposite of what you want (on your checkbox example)
@maxime1992 you're right. I've noticed inconsistencies with it. So a better solution would be to attempt to click it then check if it the checkbox is checked and if not attempt to click it a second time. Good catch.
Perhaps this solution will help you https://stackoverflow.com/a/56451395/2846252
you will need to come up with some mechanism to 'guard' Cypress from executing its commands too soon until you know your application has completely initialized.
I explicitly assert that the element is visible, yet the test fails claiming afterwards that this very element is not visible.
Got this issue with select2 This element '<li.select2-results__option>' is not visible because it has an effective width and height of: '0 x 0' pixels.
I tested with cy.get('#query-btn').invoke('width').should('be.gt', 0)
but not working 🤷♂
Also, when inspecting the element my element has width and height > 0
It works with force: true
I'm getting the same error:
cy.click() failed because this element is not visible: although it's visible.
I also tried with force: true
but still getting the same error. I saw lots of entries and opened issues related to this topic, (or cy.type() failed because this element is detached from the DOM. this) and couldn't figure out the reason.
Is this directly related to React or it's something Cypress doesn't support currently?
Same error with Vue.js.
cy.get("#EMAIL").invoke('width').should('be.greaterThan', 0) // -> true
cy.get("#EMAIL").type("Bob"); // -> "...element is not visible"
Cypress 3.3.1 Vue 2.6
We are facing this issue too (a lot) and this is a basic requirement for a UI testing tool to avoid this kind of issue.
Seems like lot of people are facing the same issue too and can't find a reliable solution to be confident with cypress tests.
What are the plan to troubleshoot and fix this ?
No, using wait or force:true are not acceptable solutions. We need that a .click() works fine when the should('be.visible') is OK.
Here the code that fails randomly and that should not be possible to have this use case :
cy.get(`.sample-selector`)
.should('be.visible')
.click();
This element [...] is not visible because it has an effective width and height of: '0 x 0' pixels.
experiencing this on our test.
I have hte same on a table cell and doing the below does not solve it. The innerwidth test passes the other do not. There is no change or remount between those 2 iteractions
cy.get('@cell')
.invoke('innerWidth')
.should('be.greaterThan', 0);
cy.get('@cell').click();
The force click "works" but then the onClick is not fired for some reason. The cy.get().wait() seems to improve the success rate but not always solve it
out team is also experiencing this issue/bug.
we've tried adding additional browser waits, which does alleviate some of the issues locally, however in CI
it doesn't make much of a difference.
Would it be possible to get a better error message than has an effective width and height of: '0 x 0'
when click()
is attempted on an element that has been removed from the DOM tree?
Another idea: should there be a kind of a "proxying" version of get()
, which is able to re-evaluate the selector (with a timeout) if the previously found DOM element has been removed? It seems that DOM element aliases already do something like this, but it probably doesn't solve the issue of the element disappearing between the get()
and the click()
?
We still see this issue when using Cypress 3.4.1
.
Our test (in pseudo code) is as easy as:
describe(description, () => {
it(expectation, () => {
cy.server();
cy.route(
method,
url1,
response, // this is a fixture
).as(alias);
cy.visit(url2);
cy.wait(@alias);
cy.get('[test-id="<test id name>"]').should('be.visible'); // this assert is OK
cy.get('[test-id="<test id name>"]').click(); // this line fails
});
});
Are there any Cypress
flags that I might enable in order to produce a richer log which might give us more input on why
click()
fails?
@brian-mann / @jennifer-shehane
Not sure if my problem is the same as the above posts as I am starting web development in general but I too am getting an error when trying to click on a selected element.
cy.get('.leaflet-draw-draw-circle').then((el)=>{
console.log(Cypress.dom.isDetached(el))
}).then((el)=>{
cy.get(el).click();
});
My apologies for my horrible formatting.
My target is visible on the page.
From reading the above thread, I am first checking if the element is detached, which yields a 'false' in my console, then use the same element to click.
I still get the effective width and height fo 0 x 0
error.
Have I something different going on here?
@flurosatbrad that appears to be the same issue. One of the issues here is during the actionability
check before commands, we scroll the element. In some cases this can cause certain frameworks to re-render/detach dom nodes. The issue being the error message does not check for detached state, so you get the wrong error message, specific issue here #4946
@Bkucera Thanks for the clarification. Atleast I can try a work around if this is my issue.
I got the same error-message, but since our tests have had a lot of timing issues I added a check that some other control, that is conditionally rendered (added to the DOM) at the same time, was visible before the input command and I have not seen the problem since. (I have not run the test-case very much since but thought it might give you ideas.) I also get the impression that Cypress checks that the page have finished loading, but have no way to check that it is done rendering. Sprinkling my test-code with checks for element visibility seems to have made it stable. Before this there was a distinct correlation between computer load and failing tests.
This code-smells like hell, but calling cy.wait(0)
prior to cy.get('…')
helps in my case. Want more code smell? Then use const zero = 0; cy.wait(zero)
to keep the linter happy.
I guess this is effectively the same as the good old setTimeout(fn(), 0)
, waiting for the call stack to empty (e.g. until all re-renders ran) so that cy.get
grabs the element(s) from the last render?!
@saas2813 @kkkrist At my company, we patched click
to execute after a window.requestIdleCallback
, which helps significantly. We don't use React, and Brian's earlier point makes sense:
There are no events in JS that will help you do this, it's completely application specific.
When we run tests against our static pages, we don't need to do this patch since the client doesn't custom refresh.
Perhaps there could be adapters/libraries for Cypress to work w/ the most popular frameworks, or more tutorials on how to properly detect render complete? This definitely seems to be a very common gotcha.
Or maybe simply, to enable retries when Cypress tries to act on a stale DOM element?
We had a similar issue on a React app. It was caused by a coding mistake.
Something like:
const MyComponent = () => {
const MyInnerComponent = () => <div/>;
return <MyInnerComponent/>;
}
The issue here is that MyInnerComponent
is a new component at each render so react will remove the div element and re-create new one at each render.
But still, I think Cypress could provide a better error message. The actual issue is that the element is detached from the DOM, talking about dimensions 0x0 is misleading.
Also, could we avoid this pitfall by checking the element state synchronously before performing the click? After all, this is the promise of Cypress:
Cypress knows and understands everything that happens in your application synchronously.[...] It's impossible for Cypress to miss elements when it fires events
But still, I think Cypress could provide a better error message. The actual issue is that the element is detached from the DOM, talking about dimensions 0x0 is misleading.
@testerez Reading https://github.com/cypress-io/cypress/pull/4945, I think that'll fix this issue in the next release!
thanks @hdavidzhu, you are correct. Should have thrown a comment on here.
This is especially depressing 😞
Any update on this issue? It's still reproducible in Cypress v3.6.1
I am experiencing exactly the same in 3.6.1
Hi there,
I'm encountering some flaky tests due to an element not being considered visible by Cypress, I don't understand what makes an element visible or not visible by Cypress and was hoping someone could clarify it for me.
Since version 0.20 tests randomly decide to fail with this message:
This element '<a.sc-cqpYsc.cmkxre>' is not visible because it has an effective width and height of: '0 x 0' pixels.
. However, when I inspect it is definitely has a size bigger then 0 by 0, as you can see in the screenshots.Failing test due to click timeout:
Item highlighted by Cypress:
Chrome inspect of the element:
Now, I can "fix" this by adding
{ force: true }
to the click, but I would like to understand why this results in flaky behaviour (both headless and using the cypress UI).Is this a Feature or Bug?
Bug?
Current behavior:
cy.click() failed because element is not visible