cypress.io: contains() not waiting for element

Ash picture Ash · Nov 22, 2018 · Viewed 7.1k times · Source

We are writing UI tests with cypress, which is usually quite simple to use. But again and again I stumble over a tedious waiting problem.

The scenario is pretty simple. The user clicks on the search button. Then he selects one of the elements with a certain text. Here's the code:

cy.get('#search-button').click();
cy.contains('Test item 1').click();
cy.get('#cheapest-offer-button').click();

The third click event fails, because already cy.contains('Test item 1') doesn't wait for the page and the element to be rendered. From what I can see in the test steps, it simply clicks in the middle of the page, which does essentially nothing. So all subsequent steps fail of course.

However if I add a wait() between the calls like this:

cy.get('#search-button').click();
cy.wait(2000);
cy.contains('Test item 1').click();
cy.get('#cheapest-offer-button').click();    

The page is rendered correctly, Test item 1 appears, is clicked and all subsequent steps succeed.

According the best practices the wait() call should not be necessary and therefore should be avoided. What am I doing wrong here?

Answer

totymedli picture totymedli · Jun 27, 2019

tl;dr

Give a bigger timeout for contains:

cy.get('#search-button').click();
cy.contains('Test item 1', { timeout: 4000 }).click();
cy.get('#cheapest-offer-button').click();  

Explanation

As many Cypress commands, contains have a second argument which accepts an option object. You can pass the amount of milliseconds the command should wait in the timeout key like this:

.contains('Stuff', { timeout: 5000 }) // Timeout after 5 secs

This way the command will behave like if you added a wait before it, but if the command was successful it won't wait out all the time like wait does.

The official Cypress docs on timeouts explains this technique: how it works, how it should be done and how it affects chained assertions.

If the reason why you can't click the item is visibility then you can try .click({ force: true }), although this should be a last resort since it might hide actual bugs.