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).
Bug?
cy.click() failed because element is not visible
Much of the algorithm used to determine visibility is documented here:
https://docs.cypress.io/guides/core-concepts/interacting-with-elements.html#Actionability
Your issue is very clear which is helpful, thank you. I'm a bit confused when you say flaky though. Flake generally refers to situations where a test sometimes passes or sometimes fails.
It seems that you're saying the test always fails which would not indicate a flaky test - you've just found a bug in our visibility algorithm :-P
While you've done an excellent job giving us a ton of great information, the problem is that there is more than just the styles of this <a>
causing this problem.
Is there any way you would be able to remove all of the excess HTML (or maybe even just do a File -> Save Page As) so you can export the entire DOM including the styles. From there we should be able to internally reproduce this and get it resolved.
Also if this is a public site (or even a private site we could log into) we'd be happy to take a look at this.
We've had a few users complain about this exact scenario - where Cypress considers an element hidden because its effective width and height is 0, but we've been unable to reproduce any situation to trigger that. We have several hundred tests around visibility (and they all pass) so we're going to need help creating the scenario where this happens.
Thanks for responding, I haven't been clear about that, sorry. The test does indeed sometimes passes, and sometimes not. Locally it passes roughly 95% of the time, on our CI environment it passes roughly 10% of the time.
I've done some testing and I'm pretty certain it has nothing to do with the styling of the a
tag, but with API calls happening while the .click()
is being triggered.
Our app currently does a lot of api calls on page load (something we should improve upon :)), and I managed to get the tests more stable by doing the following:
cy.route('/api/order/retailer-orders**').as('getRetailerOrders');
cy.route('api/connection/connections').as('getConnections');
cy.route('/api/connection/relations').as('getRelations');
cy.route('/api/order/basket').as('getBasket');
cy.route('/api/order/retailer-orders/*').as('getRetailerOrder');
cy.route('/api/brand/brands/*').as('getBrand');
// .. some other checks ..
cy
.wait('@getRetailerOrders')
.wait('@getConnections')
.wait('@getRelations')
.wait('@getBasket')
.wait('@getRetailerOrder')
.wait('@getBrand');
cy.get('a[href="/styles"]').click(); // <--- This is where it sometimes fails.
Now it's a bit early to call the test "stable" but it at least a lot more stable.
About your other question, I'm going to try and isolate this issue by making a simpler app, but that will take some time.
Could any of those XHR's affect the re-rendering of the <a>
?
Not to my knowledge, the <a>
is in the header op the page. The XHR's are for content.
Interestingly, if I use ....click({ force: true });
it sometimes fails because the click doesn't trigger a page change. We use react-router for our navigation, so I have a gut feeling it might have to do with that react-router component not being completely initialised as the page is still kind-of loading / waiting on async calls.
I believe the problem here is that it's finding the <a>
but then it's getting detached / wiped from the DOM by the time the .click()
rolls around. If this is the case, there's actually nothing wrong with the visibility algorithm (it's just miscommunicating the root cause).
I can manually recreate this situation and opened a new issue here: https://github.com/cypress-io/cypress/issues/696
There is something causing the <a>
to be detached / rerendered.
Likely what you'll need to do is add more guards prior to interacting with the element.
{ force: true }
is failing because when the element is removed its event handlers are also removed, or the browser will no longer apply the default action to a detached element (such as to follow the link).
So what ends up happening is that by forcing the click event to happen, Cypress issues it, but it's being issued to a detached element, which does nothing.
As per your suggestion - 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.
You could directly communicate with your react app and register an event listener, or you could search for some content on the page that you know becomes available. For instance your react app could add an attribute to the
or to know its completely done.You could also individually wait for all the XHR's although react is going to process them asynchronously so there will still be a small gap even after waiting on them.
Once you establish that it will solve all of your flake. I would likely wrap this up into a custom command or just override visit
so it doesn't resolve until your app is fully loaded.
There are no events in JS that will help you do this, it's completely application specific.
I have the same issue when I am trying to type into an input field, the behaviour is rather flaky with Cypress, 5 out of 10 times the steps works.
I am changing the language of Installation in Joomla, which changes the fields based on the language selected by users. Before I begin Typing into the field, I ensure the field is visible, and then try to type, but I get the same error which says
element is not visible because it has an effective width and height of: '0 x 0' pixels.
Although in the screenshot it is clearly visible.
I am just trying to understand if we have a way of saying, hey Cypress do not proceed with the test until you have xyz element visible and you can interact with it.
I have the same issue. We are using angular. Some times the click registers and the other times does not.
@puneet0191 and @chathuraa did you read my comments above in this thread? I believe both of your situations are caused by the element becoming detached as Cypress begins to type. Can you verify nothing on your end is re-rendering the element.
@puneet0191 in your case you are highlighting the element in dev tools which is not necessarily the element that cypress found because it could have been replaced by being rerendered.
@brian-mann, thanks for the quick response. I believe the page render several times. Unfortunately reducing rerender is all the way at the bottom of our backlog. Sorry for my ignorance, is there a work around?
@chathuraa I worked around it by making sure the re-renders are done before executing the assertions which in my case is when all backend calls are done. See this comment
In my case, I was trying to click on a link (after waiting for the API result) that was being mounted and unmounted when API results came in. Switching it to a React pure component and making sure it wasn't unnecessarily unmounted fixed the problem. FWIW, I'm also using React Router 4 and this was trying to click on a <Link>
element. Thank you Cypress team for such an awesome dev experience
This has also been a real issue for us. We're using React and I'm still struggling to find the right combo of xhr waits for each page to get our tests reliable. But this thread has been helpful to understand the cause 👍
Timeouts because of zero-width elements happens all the time to us; I'm guessing it's because the element is only partially rendered. It would be great if there was a way to wait for an element of non-zero width; then I'd replace get() with a version that checked for both visibility and non-zero size. But I haven't found a way to do this. You can do an assertion on the css width, but that's not the same thing. Also {force: true} doesn't work in this case. The only solution currently I've found is to add wait()s.
@peternye You should be able to write an assertion that checks on non-zero width where the tests will not move forward until it is greater than non-zero:
// these assertions below are the same
cy.get('#query-btn').invoke('width').should('be.gt', 0)
cy.get('#query-btn').invoke('width').should('be.greaterThan', 0)
You could basically assert against any property of an element, like innerWidth
and outerWidth
also.
See Assertions
Thanks, @jennifer-shehane! Exactly what I was looking for.
@jennifer-shehane this is exactly what we needed as well. we are going to implement a custom command to handle these assertions.
Cheers!
@jennifer-shehane thank you for the suggestion! Can you clarify, why cy.get('#query-btn').invoke('width').should('be.gt', 0)
won't fail, but cy.get('#query-btn').click()
fails? What I mean is that if Cypress is smart enough to do retries / reselects of the element when assertion doesn't work, why cannot it do the same for the actions? Of course, if you had an action earlier in a chain, it should fail (no recovery if action was done on element and then it became detached, i.e. if it's cy.get('#query-btn').click().parent()
and after click()
element is detached), but if everything in the chain were just selectors, Cypress could've retried them all if by the time we're doing first action element becomes detached. Or maybe I don't understand how assertions work in Cypress...
Same problem here:
Out of around 12 forms elements, Cypress is unable to "type" in 2 of them. All of them have the same visibility. However, the other 10 are without an issue. {force:true}
does not work, either, no typing is done.
I have the same problem with a flaky behaviour.
I know the element which i try to click is not removed from the dom, but there is an automatic scrolling of the viewport when the element is displayed.
I get the 0x0 pixels error, but even with @jennifer-shehane workaround, the test runner is not able to click it.
With those tests :
cy.get('#candidatsList > li:nth-child(1) > span').should("contain", "Créer RDV");
cy.get('#candidatsList > li:nth-child(1) > span').invoke('width').should('be.gt', 0);
cy.get('#candidatsList > li:nth-child(1) > span').click();
First two "get" succeeds, but not the third one.
I don't think that there is a "scroll end" event i could hook to, so i don't know how i could guard this step without using cy.wait
I agree with @vjau , no matter what we are doing to guard the click, the click fails though the element is clearly visible.
@Bkucera and I have been reworking the visibility algorithms and are also in the process of adding native events. These bugs will be fixed. I don't have a timeline yet, but as we fix the visibility stuff we will release patches.
At the moment you can try setting the input's value programmatically and then manually trigger the change
events (or other stuff) so that your application binds to them correctly.
cy.get('input').invoke('val, 'asdf').trigger('change', { force: true }) // something like this
Just take programmatic shortcuts which will avoid the checking layers
Didn't work for me. I tried to programmatically make them visible, but to
no avail.
On Thu, Jun 14, 2018 at 7:10 PM, Brian Mann notifications@github.com
wrote:
@Bkucera https://github.com/Bkucera and I have been reworking the
visibility algorithms and are also in the process of adding native events.
These bugs will be fixed. I don't have a timeline yet, but as we fix the
visibility stuff we will release patches.At the moment you can try setting the input's value programmatically and
then manually trigger the change events (or other stuff) so that your
application binds to them correctly.cy.get('input').invoke('val, 'asdf').trigger('change', { force: true }) //
something like thisJust take programmatic shortcuts which will avoid the checking layers
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/cypress-io/cypress/issues/695#issuecomment-397369806,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIJ3zabjO0hR7dK3-Gt0IW_TF7QjUkyRks5t8pj3gaJpZM4Poclb
.
I would just like to say I'm experiencing this same problem, am able to verify the the existence of the element via height and width but getting the same "element not visible" when trying the click. I do believe it has to do with timing, if I hardcode a wait it passes fine. This is especially weird because the .should('be.visible').invoke('width').should('be.greaterThan', 0)
and .should('be.visible').invoke('height').should('be.greaterThan', 0)
steps I run before the click command both pass while the click fails.
Try cy.get(...).click({ force: true })
Tried that to no avail.
On Thu, Jun 21, 2018, 22:21 Brian Mann notifications@github.com wrote:
Try cy.get(...).click({ force: true })
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/cypress-io/cypress/issues/695#issuecomment-399230133,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIJ3zUeHjuniqd4kB8tbN5mpAaJCCzOpks5t_AA5gaJpZM4Poclb
.
yeah if it force clicks it works some of the time - I think the issue is it's still clicking before the element loads. I'm noticing this behavior with a dropdown element that loads a list (but doesn't need to fire off an xhr request to do so)
Cypress can't click an element before it loads. It waits until the element exists. It's possible that your actual click
handler isn't wired up even though its visible and available.
I would simply add an assertion about the element (or something else) that can guard Cypress from proceeding until the element is ready to receive the click event. It may be something about its dimensions, or content on the page, or waiting for an XHR, etc.
@brian-mann with the scrolling issue i reported earlier, AFAIK there is no way to know when the scroll has ended. The element exists but the browser is not able to click it while scrolling.
but doesn't the .should('be.visible').invoke('width').should('be.greaterThan', 0)
passing before a click command ensure that the element is visible? Or am I missing an assertion?
cy.get(selectors.addAdminTestEdit).should('be.visible').click();
cy.get(selectors.addAdminTestEditRemoveAdmin).should('be.visible').invoke('width').should('be.greaterThan', 0)
cy.get(selectors.addAdminTestEditRemoveAdmin).should('be.visible').invoke('height').should('be.greaterThan', 0)
cy.wait(300)
cy.get(selectors.addAdminTestEditRemoveAdmin).click();
will pass always - but if I take out the cy.wait(300)
the click()
will fail about half the time
I have same problem with AngularJS website. I need to include cy.wait()
or the click()
fails with:
This element '<a>' is not visible because it has an effective width and height of: '0 x 0' pixels.
Running on Cypress 3.0.1
Had same issue when the click failure in 10% chance.
cy.get('#query-btn').invoke('width').should('be.gt', 0)
Didn`t helped
{ force: true }
and cy.wait(150);
Worked ( kind of )
But its definitely workaround not a solution for me.
But the app which i was testing was not build on react and i was referencing ID locators. Maybe that where it comes from.
Happens to us too, nothing works except cy.wait(100)
.
@brian-mann : Do you have a repro environment where you can see this bug reliably?
I believe it does have to do with XHRs coming in late. In my case React is reflowing a bunch of elements to the DOM that already existed.
I'm not familiar with Cypress internals but perhaps attaching attaching a selector to the click event: cy.click(
We have the same (or similar) issue. Tests run fine on a desktop using chrome or electron but headless fails randomly (platform independent). cy.click()
in particular. While I'm on the bandwagon for Cypress, this seems to be a bit shaky, especially for complex SPAs. Or maybe we're doing it wrong - but I honestly can't see a way to assure a 100% hit rate here. I would love to read how to prevent things like that from happening.
@OmriAharon
Happens to us to, nothing works except
cy.wait(100)
.
Same here - nothing worked other than this workaround. Also using react.
Same issue while testing our Angular app. A better error message as suggested in #696 would have helped indeed, but debugging remained quite hard for me.
I had written a big comment about the whole day I spent experimenting around similar issues, but feel quite stupid now that I've finally realized (after getting away from the code) that it was a caching issue. I had a list of elements displayed first from the cache, that were getting re-rendered later when the xhr came back. So I can now confirm with 100% certainty that it was a bloody asynchronous rendering issue. Quite amazing that it was often happening at the exact same moment, but maybe Cypress was freeing the stack consistently at the same step. Dunno.
I suppose this issue means that Cypress doesn't retry the whole commands chain when the click fails. Would retrying the whole chain, or maybe just going up the chain until finding an element still attached to the DOM, make such commands more stable ? Something like #1548 maybe.
@ArnaudPel I fully agree, Cypress not retrying the entire command chain is a source of these types of confusions and bugs. We have been talking about fixing this for a while now, and will probably be next after retries or cross browser support.
@Bkucera thank you for your quick reply. I've edited my comment after realizing I was all at sea yesterday. We seem to agree that retrying the whole chain would have made the test pass. But now I realize that without this failure I would never have noticed this caching thing :) Which, in my case, was not intended.
Trying to think about a way to avoid this in the future, maybe Cypress could enrich the error message with something like right before this failure, xhr request [url] completed
? But I understand it's already visible in the Live version, and might be too much.
@ArnaudPel
Would retrying the whole chain, or maybe just going up the chain until finding an element still attached to the DOM
Cypress does currently do this. The problem happens when Cypress finds an element, but it's not the one you expected, so the element never gets detached from the dom.
Still failing in 3.1.4
Multiple wait() doesn't work for me (less crashes but still randomly...)
This same with cy.select()
Not to pile on here - but incase it helps, here is an interesting instance of this issue happening, where all suggested fixes above are not helping to work around the issue.
i am running these assertions:
const setupPageAddressListFirstSuggestion = '[data-cypress-test-reference="address-suggestion-first-item"]'
cy.get(setupPageAddressListFirstSuggestion).should('exist').and('not.be.disabled');
cy.get(setupPageAddressListFirstSuggestion).invoke('width').should('be.greaterThan', 0);
cy.get(setupPageAddressListFirstSuggestion).invoke('height').should('be.greaterThan', 0);
cy.get(setupPageAddressListFirstSuggestion).click();
here's how cypress 3.1.4 processes these assertions:
I've also tried aliasing the element, then using the alias. same results.
(the item that i'm clicking is indeed generated/regenerated by multiple react renders - based on an api response, hence the difficulty - though its interesting how every assertion before the click, contradicts the final click assertion's error message. :]).
For now, I've just stubbed out the harmony api calls, and have them constantly return the same data > which minimises the react re-rendering > which seems to stabilise the click event. It would be nice though, to not have to do this.
@jennifer-shehane I don't see any commits in develop branch for solving this issue. Anyone working to fix this?
Hi, I have the same issues. using should("be.visible") also fails because of this problem. It would be good if one can make a clear statement whether or not this will be fixed. Otherwise we also do not know if we should handle this in our code or not.
No work has been done specifically on this issue.
I've tried all of the workarounds mentioned here:
.should("be.visible")
.invoke('width').should('be.greaterThan', 0)
cy.wait(500)
No matter what I try, it seems to fail about 20% of the time with the "effective width and height of 0x0" error.
My project is an AngularJS SPA and while the element being clicked is dynamic in the sense it is conditionally added to the DOM. when running in electron browser mode I can clearly see the element and it still fails!
Please prioritise this bug as it's causing our CI process to regularly fail.
We are experiencing same issue, are there any updates on this?
Similar issue with our project in Vue, hoping for an update soon.
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
There were improvements as part of 3.5.0 release https://github.com/cypress-io/cypress/pull/4945 that should have improved this error message for many instances where one was asserting on a detached DOM element. Now users will see a message saying that the DOM element is attached.
If you're still experiencing this in 3.6.1 - Please provide a fully reproducible example of the bug. I cannot stress this enough. If we cannot write a failing test in Cypress - then we cannot begin work on the issue.
After trying several approaches I'm getting good results with
cy.get("#id")
.should("be.visible")
.then($btn => {
$btn.click();
});
@khayrat version?
cypress/included:3.2.0 and cypress/included:3.6.1 - I'm running it in ci/docker. Natively I got stable results either with cy.get("#id").click()
@khayrat Thanks! works for me!
I have the similar issue. page.startDateButton.click()
doesn't work but page.startDateButton.then($btn => $btn.click())
does work.
Being subscribed to this thread is funny. People come up with workarounds that probably break X% of the time instead of someone resolving the root cause. Does anyone know why this is a problem in the first place and how it could be addressed? <3
The root cause is the app you are testing is changing the dom underneath - so cypress gets an element but before it can click it, the app has removed it from the dom and re rendered. Sometimes this can be solved by asserting state before the click to ensure no more dom changes will occur and the app is stable.
Most solutions here just add a tiny delay which make it more likely that your app under test has finished (the wait for visible may be lucky enough to catch the state and retry or it may miss it - depending on timing)
IMO cypress should retry clicks if the dom element is removed, before it dispatches the event.
I’ve solved this all of the time in our app by proxying every async event (all ASAP implementations, set timeout, promises) and then overriding get element to wait for all async events to finish before getting a element - it makes things very stable. The only problem is that some events like set timeout 200ms or 3000ms are things you should wait for and others you shouldn’t so we had to be careful on the boundaries). With the above, click is 100% stable on a very complex react app.
@lukeapage have a very good description of this problem.
I have been trying to adress something similar; When opening an edit page my application displays cached data while it queries the server for the latest version and then re-renders. Depending on system-load Cypress will manage to start clicking before the re-rendering is complete and I get the above problem or something similar. Of-course this is unusual on my own machine and very frequent on the build-server. The worst part is that the re-rendering in respons to loading from the server causes the effect of the previous clicks to be removed, thus adding a "force" will simply delay the error.
I need a way to make Cypress synchronise with Vue rendering and nothing I have tried have been dependable. I have tried making our "Is loading"-flag we show to the user (that the tests waits for to not be visible) display longer but the race is still there some way.
I proposed a solution at https://github.com/cypress-io/cypress/issues/695#issuecomment-513538039 but it didn't get any feedback. The idea is that get()
would return a proxy object. If it notices that the referenced DOM node has been removed, it would try to find it again using the same selector. If it cannot find it within a given timeout, it would fail (as get()
normally does), but if it does find it, then it would return it. In this case it wouldn't matter how many times the underlying DOM node gets recreated, all that would matter is that the DOM node referenced by the specified selector exists when the given action is applied to it (e.g., click()). The DOM node might change between different actions (e.g., check for visibility and a click) but I wouldn't usually expect this to be an issue.
That said, the fact that the DOM node is getting recreated could be a sign of flakiness in the application (if the component loses its state when the DOM node gets removed). But I think in many cases (especially legacy applications) it's not practically possible to fix these type of issues.
I need a way to make Cypress synchronise with Vue rendering and nothing I have tried have been dependable. I have tried making our "Is loading"-flag we show to the user (that the tests waits for to not be visible) display longer but the race is still there some way.
@saas2813 Is the correct approach here not to alias that call to the backend, then wait for that server request to provide a response...and then proceed accordingly? Perhaps I'm missing something regarding the implementation, but we do this frequently without issue (in a React-based app).
I am not sure if this is useful but i get this issue with chrome but don't seem to get it with firefox.
Chrome
Firefox
At Airbnb we have a React app that uses the Progressive Hydration technique discussed in this presentation: https://www.youtube.com/watch?v=k-A2VfuUROg
As such, we have chunks of our DOM that become hydrated in idle callbacks after the page load event has fired. Due to the way that Cypress works, this has caused quite a bit of flake in our tests because elements often end up getting swapped out between the .get()
and the next chained method (e.g. between .get('button')
and .click()
here cy.get('button').click();
).
The naive approach to work around this is to add cy.wait(n)
calls all over the place with arbitrary timeouts. This is not satisfying because if you pick a large timeout, you make your tests unnecessarily slow, and if you don't pick a high enough timeout then you still end up with flake. As the product changes over time, these timeouts may need to be adjusted, and it is hard to know when to do that.
To make this better, we've developed a custom Cypress command called waitUntilSettled
. This will install a MutationObserver and recursively call requestIdleCallback
on the page. After the idle callback resolves and the DOM has not yet been mutated, we proceed with the test. We are just starting to use this and it seems to be helping us, so I thought I would share the code here.
https://gist.github.com/lencioni/7ba04e0f92558f49454c19c44cf3bc5c
We are currently only testing in browsers that support requestIdleCallback, so it is possible that when we roll this out to other browsers where we have to polyfill that, we will need to adjust this some--e.g. maybe we should wait for a couple of consecutive non-mutated DOM checks before moving forward.
I hope this helps anyone else running into these types of issues. In the future, it might be nice if Cypress was enhanced to do this style of waiting when visiting routes, so we could avoid dropping these cy.waitUntilSettled()
calls in a bunch of places.
Are people still regularly seeing this error in the latest version of Cypress? There have been a lot fewer comments in here since January.
My theory is that people are now more regularly seeing the error:
This element `<div>` is not visible because it is detached from the DOM
which is a more accurate error message than the effective width and height one when the element is no longer in the DOM. We have an issue to address this here: https://github.com/cypress-io/cypress/issues/7306
Sadly i have not been able to upgrade because of performance problems with the latest versions of Cypress that will probably never be fixed, so i'm considering switching to another solution.
Closing this issue and referring to https://github.com/cypress-io/cypress/issues/7306 since there have been no comments on this issue since 4.4.1 of Cypress was released, where we implemented a clearer error message in this case that used to show as the 0x0 error message:
This element `<div>` is not visible because it is detached from the DOM
If you're experiencing a bug similar to this in Cypress, please open a new issue with a fully reproducible example that we can run. There may be a specific edge case with the issue that we need more detail to fix.
Most helpful comment
I've tried all of the workarounds mentioned here:
.should("be.visible")
.invoke('width').should('be.greaterThan', 0)
cy.wait(500)
No matter what I try, it seems to fail about 20% of the time with the "effective width and height of 0x0" error.
My project is an AngularJS SPA and while the element being clicked is dynamic in the sense it is conditionally added to the DOM. when running in electron browser mode I can clearly see the element and it still fails!
Please prioritise this bug as it's causing our CI process to regularly fail.