Cypress: how to wait for all requests to finish

Viewed 17346

I am using cypress to test our web application.

In certain pages there are different endpoint requests that are executed multiple times. [ e.g. GET /A GET /B GET /A].

What would be the best practise in cypress in order to wait for all requests to finish and guarantee that page has been fully loaded.

I don't want to use a ton cy.wait() commands to wait for all request to be processed. (there are a lot of different sets of requests in each page)

4 Answers

I'm sure this is not recommended practice but here's what I came up with. It effectively waits until there's no response for a certain amount of time:

function debouncedWait({ debounceTimeout = 3000, waitTimeout = 4000 } = {}) {
  cy.intercept('/api/*').as('ignoreMe');

  let done = false;
  const recursiveWait = () => {
    if (!done) {
      // set a timeout so if no response within debounceTimeout 
      // send a dummy request to satisfy the current wait
      const x = setTimeout(() => {
        done = true; // end recursion

        fetch('/api/blah');
      }, debounceTimeout);

      // wait for a response
      cy.wait('@ignoreMe', { timeout: waitTimeout }).then(() => {
        clearTimeout(x);  // cancel this wait's timeout
        recursiveWait();  // wait for the next response
      });
    }
  };

  recursiveWait();
}

According to Cypress FAQ there is no definite way. But I will share some solutions I use:

  1. Use the JQuery sintax supported by cypress

    $('document').ready(function() { //Code to run after it is ready });

The problem is that after the initial load - some action on the page can initiate a second load.

  1. Select an element like an image or select and wait for it to load. The problem with this method is that some other element might need more time.

  2. Decide on a maindatory time you will wait for the api requests (I personaly use 4000 for my app) and place a cy.wait(mandatoryWaitTime) where you need your page to be loaded.

You can use the cy.route() feature from cypress. Using this you can intercept all your Get requests and wait till all of them are executed:

cy.server()
cy.route('GET', '**/users').as('getusers')
cy.visit('/')
cy.wait('@getusers')

I faced the same issue with our large Angular application doing tens of requests as you navigate through it.

At first I tried what you are asking: to automatically wait for all requests to complete. I used https://github.com/bahmutov/cypress-network-idle as suggested by @Xiao Wang in this post. This worked and did the job, but I eventually realized I was over-optimizing my tests. Tests became slow. Test was waiting for all kinds of calls to finish, even those that weren't needed at that point in time to finish (like 3rd party analytics etc).

So I'd suggest not trying to wait for everything at a step, but instead finding the key API calls (you don't need to know the full path, even api/customers is enough) in your test step, use cy.intercept() and create an alias for it. Then use cy.wait() with your alias. The result is that you are waiting only when needed and only for the calls that really matter.

// At this point, there are lots of GET requests that need to finish in order to continue the test
// Intercept calls that contain a GET request with a request path containing /api/customer/
cy.intercept({ method: 'GET', url: '**/api/customer/**' }).as("customerData");

// Wait for all the GET requests with path containing /api/customer/ to complete
cy.wait("@customerData");

// Continue my test knowing all requested data is available..
cy.get(".continueMyTest").click()
Related