Reputation: 10666
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.
Upvotes: 31
Views: 65576
Reputation: 1928
You can do something like this:
// Give an alias to request
cy.intercept({
method: 'GET',
url: '/odata/locations/**',
}).as('dataGetFirst');
// Visit site
cy.visit('admin/locations');
// Wait for response.status to be 200
cy.wait('@dataGetFirst').its('response.statusCode').should('equal', 200)
// Continue
Upvotes: 53
Reputation:
Since you are using cy.visit('/')
, I assume you have a baseUrl set in your configuration. The URL parameter in cy.route()
does baseUrl + the string you pass as a parameter 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: Network Requests, Fixtures
Upvotes: 0
Reputation: 2897
So most of the answers are deprecated now. As of Cypress@6.4.0, 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.
Upvotes: 18