20
votes

in my app, when i visit a page it makes some network requests to fetch data and display it on the page. after that you click buttons and fill in fields to filter that data.

i have a cypress test that will basically visit the page, apply some filters, and make sure the stuff in the dom looks right:

it(`filters the data by 'price'`, () => {
  cy.server()
  cy.route('POST', 'http://my-api.biz/api').as('apiRequest')

  cy.visit('/')

  // initial page load loads the min and max price bounds for the UI,
  // as well as the data to initially populate the page. they happen
  // to hit the same URL with different POST params
  cy.wait(['@apiRequest', '@apiRequest'])

  cy.get('#price-filter-min').type('1000')
  cy.get('#price-filter-max').type('1400')

  // wait for data to get refreshed
  cy.wait('@apiRequest')

  cy
    .get('[data-test-column="price"]')
    .each($el => {
      const value = parseFloat($el.text())
      expect(value).to.be.gte(1000)
      expect(value).to.be.lte(1400)
    })
})

however sometimes cypress seems to load the page, do the XHR requests before waiting, then sporadically it'll fail on:

CypressError: Timed out retrying: cy.wait() timed out waiting 30000ms for the 2nd response to the route: 'apiRequest'. No response ever occurred.

because it's waiting for a request that has already happened.

is there a better way to write this test? is there a way to visit a page and wait for XHR requests that avoids this race condition?

UPDATE

i've tried to re-create this in an isolated test case, but it all seems to be working right, so there's probably some operator error.

3
Sorry for the inconvenience - I made only some grammar fixups, what is the problem with it? - peterh
@peterh-ReinstateMonica i prefer the way i had written the question originally, it reads better with the imperfections IMO. if you can refrain from editing it i would appreciate it! - schpet
I believe such content is not useful, more clearly: harmful. I am willing to vote it down. All of them. Is it not a problem for you? - peterh

3 Answers

8
votes

So most of the answers are deprecated now. As of [email protected], you should use intercept().

Here's how I did mine:

cy.intercept({
      method: "GET",
      url: "http://my-api.biz/api/**",
    }).as("dataGetFirst");
cy.wait("@dataGetFirst");

And that's it. You could do more and do an assertion chain on the wait but it's already an assertion by itself.

30
votes

You can do something like this

// Give an alias to request
cy.server().route('GET', '/odata/locations/**').as('dataGetFirst');

// Visit site
cy.visit('admin/locations');

// Wait for response.status to be 200
cy.wait('@dataGetFirst').its('status').should('be', 200);

// Continue
0
votes

Since you are using cy.visit('/'), I assume you have a baseUrl set in your config. The URL param in cy.route() does baseUrl + the string you pass as a param behind the scenes.

So the url it's sending the POST request to is http://my-api.biz/apihttp://my-api.biz/api or something like that.

Try changing your route command to:

cy.route('POST', '/api/foobar').as('apiRequest')

Additional Documentation and Examples: https://docs.cypress.io/guides/guides/network-requests.html#Fixtures