Reputation: 99
I have been searching for a solution to the .wait() problems I have been having in Cypress. Our application does not load all the data at the same time. The most common answer given to requests like this is as follows:
cy.server();
cy.route('**/api/getData').as('getData');
cy.visit('/home');
cy.wait('@getData');
This simply does not work. You will arrive at /customer/12345 and yet it may be several more seconds before the entirety of the data has loaded, even some UI elements. So, checking for the route doesn't help at all. I tried to create an alias but when I do something like this:
cy.get('#submitButton').as('submit')
And then do:
cy.wait('@submit')
It fails the alias check because the button is not present yet.
I need to wait until all the resources are loaded before I can start testing the elements. A developer on my team who does not have any understanding of Cypress asked if I could use the Window load event or the GlobalEventHandler.onload, but I couldn't find any documentation on using these in Cypress.
One thing I would like any answerers to understand, I am a total newbie. I have experience with Java, Selenium, Unirest, and Junit. I am having to completely change my mindset with Cypress and I do not know Javascript, so I'm still in the learning phase on this. In Selenium I would have
wait.until(ExpectedConditions.elementToBeClickable(By.id<submitButton>));
So, is there a way for me to make sure the entirety of the resources are loaded before it starts running the tests?
Upvotes: 4
Views: 5995
Reputation: 111
There are three options that we prefer mostly to avoid implicit waits as adding delays in your application is considered a bad practice 99% of the time we should not do it.
Option-1:
You can use the following should
approach in order to avoid the explicit wait commands
cy.get('put-locator-class/ID-here').should('be.visible').then(($el)=>{ $el.click()})
You need to make sure that your element is going to definitely appear.
Option-2:
Custom Wait -> We can wait for certain elements to disappear. That seems more like a reverse approach.
Example:
cy.get(`span[title="${text}"][role="button"]`).click();
while (cy.get('div[class="g3-single-select-filter"]').find('i[class="fas fa-spinner fa-pulse"]').length > 0){
cy.wait(1000);
}
Option-3: We can wait for the API to finish calling by using the intercept command
cy.intercept('http://example.com/settings').as('getSettings')
// once a request to get settings responds, 'cy.wait' will resolve
cy.wait('@getSettings')
Concluding this discussion, Option-1 and Option-3 are most widely used in the cypress community
Upvotes: 7
Reputation: 2548
You probably want to be more specific on what kind of server request response you are waiting. Do not use general:
cy.route('**/api/getData').as('getData');
be more specific:
cy.route('api/for/list').as('getList');
cy.route('api/for/header-data').as('getHeader');
cy.route('api/for/footer-data').as('getFooter');
cy.route('api/for/whatever').as('getElse');
...
and expect that some specific endpoints will be requested and return data
cy.wait(['@getHeader']).then(([responseHeader]) => // check header elements)
cy.wait(['@getHeader', '@getList']).then(([responseHeader, responseList]) => // check list and header elements)
You can expect more than one endpoint to finish before expecting some results.
Wait method is important for checking if correct communication with server occurred after navigation or other application interaction and before you do your check your expectations. In some cases you may avoid it but it seems that you have some other issue.
If above is not enough you can try to trigger events (from within your app code) or use some global value to mark application rendering or processing state.
function render() {
window.cypressReady = false; // use some global values
// some rendering logic
window.cypressReady = true;
window.postMessage(); // or trigger event that can be catched by Cypress
}
and in Cypress you can observe it as follow:
Cypress.Commands.add('appReady', ({ timeout = 10000 } = {}) => {
return cy
.window({ timeout })
.should((win) => {
// when expecting is in callback we are retrying until assertion pass or timeouts
// without callback it will just check once when the variable will be available
expect(win['cypressReady'], 'app ready').to.be.eq(true);
});
});
And when you write tests:
// simulate some actions
cy.appReady()
// some expectations
You can combine both approaches first wait for specific endpoints that are required to be called for specific page and than run appReady() before running expectations.
Upvotes: 2
Reputation: 18650
You can add specific timeouts for specific elements throughout the webpage followed by should(be.visible)
. It will make sure that the assertion is automatically retried until they time out.[Cypress Docs]
cy.get('#submitButton', {
timeout: 10000
}).should('be.visible').click()
Another way can be to increase the timeout for all cy.get()
commands. For this write the below in the cypress.json
file.[Cypress Docs]
{
defaultCommandTimeout: 10000
}
Upvotes: 1