Reputation: 5571
I know it works most of the time, but on a web page I tested, on following line
cy.get('button[title="Query"]').click()
Cypress runner passed get
but hung on the click
action till timeout. If I modify the code to
cy.get('button[title="Query"]').as('query')
cy.wait(500)
cy.get('@query').click()
The test succeeds.
Obviously arbitrary wait is a bad practice. Automatic waiting, retry-ability - as claimed by Cypress as features, are suppose to prevent above scenario from happening but failed in my case.
Furthermore, Cypress has no should('be.clickable')
assertion. How could I instruct Cypress to wait for a button to be clickable before clicking it, or if clicking hung, then retry with a interval until success?
Upvotes: 7
Views: 25136
Reputation: 5571
The issue could be caused by race condition between cypress script and app code before cy.get
in a way that, at the time of clicking, the app page state is still in transition such that the button is visible and enabled but the intended event listener hasn't been attached to the button yet. It is more likely so if following code also works
cy.wait(500).get('button[title="Query"]').click()
A better alternative than waiting for arbitrary length is to add assertions for other DOM elements to make sure button is in a click-ready state. In my case, the button is in a tab. I clicked the tab before clicking the button. So make sure the tab is selected and elements in the tab are rendered first
cy.get('li[aria-label="Service Selected"]')
cy.get('table.xxx thead tr :nth-child(3)').should('include.text', 'foo')
cy.get('button[title="Query"]').click()
Upvotes: 2
Reputation: 75
It is frustrating to use cy.wait() on a hanging button and hardcode wait seconds. There has to be a solution where Cypress sees the button no longer greyed out and continues the test. This was my solution: I used an if statement:
cy.waitForStableDOM //this is a plug-in that helps to be sure your DOM is stable
if(cy.get('your element/button').should('be.disabled'))
{
cy.get('your element/button').wait(6000).click({force:true})
}
else if(cy.get('your element/button').should('be.not.disabled'))
{
cy.get('your element/button').click({force:true})
}
Maybe this may help, BTW the waitForStableDOM is a Mutation Observer. This is a great tool when waiting for Elements to load. Hope this helps.
Upvotes: -1
Reputation:
The problem is not that the button is not clickable, it's getting stuck in the click handler.
Every <button>
element has a click handler by default. For instance a very simple page
<button>Click me</button>
running this
it('shows the click handler', () => {
cy.visit('../app/clickable.html')
cy.get('button')
.then($button => {
console.log($button[0].click)
})
.click()
})
logs this
ƒ click() { [native code] }
Even if the click handler is removed, it doesn't hang or error
<button id="thebutton">Click me</button>
<script>
const button = document.getElementById('thebutton')
button.click = undefined;
</script>
logs this
undefined
but does not error.
You need to find out what's happening in the click handler to cause the hang.
Upvotes: 4