cypress: cy.wait("@route") sometimes does not catch route

Current behavior:

Occasionally (no, I do not have a reproducible example), when I wait for a route the route will complete before the call to cy.wait("@theRoute") is made, then the wait will time out. This happens a very small fraction (<1%) of the time and the only consistency that I can find is it happens after the test .click()s something, then immediately waits for a route, but it doesn’t seen to be any tests in particular. It is like the route returns too fast for cypress to see.

See screenshot below - the line above the failed wait is the route it is supposed to wait for. screen shot 2018-10-31 at 5 32 16 pm

Desired behavior:

I think this is obvious

Steps to reproduce:

I don’t have any - I know this is super helpful.

Versions

Cypress 3.1.0 Chrome 70

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 27
  • Comments: 45 (10 by maintainers)

Most upvoted comments

This was closed due to no one providing a reproducible example that we can run locally to recreate the failure. We will reopen if someone provides a reproducible example - app and test code that can be run to see the problem.

Unfortunately we’ll have to close this issue if no reproducible example is provided. Can anyone provide a way to reproduce this?

Experiencing the same issue. Please reopen the issue

cy 
  .get(Cart_Badge_Header)
  .should(($element) => {
    expect($element).to.be.visible;
})

The above code can just be written as

cy 
  .get(Cart_Badge_Header).should('be', 'visible')

@FilepCsaba Your issues with the waits is the same as @gituser3000, the routes should be defined BEFORE the calls to the routes happen.

Workaround

You should NEVER have a cy.wait() directly after a cy.route(). This will NEVER work. The wait will ALWAYS time out. Think of cy.route() as essentially just setting up a LISTENER to listen to those XHR requests.

cy.route('GET', '/thing').as('getThing')
cy.wait('@getThing') // <---- WILL NEVER WORK

It should be like this:

cy.route('GET', '/thing/*').as('getThing')
// <--- Do the thing that triggers the XHR request
// <--- maybe .click() on something or cy.visit() a url
cy.wait('@getThing');

I think I finally found a reproducible example. Github’s login response is very fast such that this test always fail on me.

describe('Capturing data sent by the form via POST method', () => {
    before( () => {
        Cypress.config('baseUrl', 'https://github.com');
        cy.server();
        cy.route({
            method: 'POST',
            url: '/session'
        }).as('githubLogin');
    });
    it('should capture the login and password', () => {
        cy.visit('/login');
        cy.get('#login_field').type('username');
        cy.get('#password').type('password');
        cy.get('input[value="Sign in"]').click();

        cy.wait('@githubLogin').then(  xhr => {
            cy.log(xhr.responseBody);
            cy.log(xhr.requestBody);
            expect(xhr.method).to.eq('POST');
        })
    });
});

Reddit is not as fast as Github so a similar test always pass.

describe('Capturing data sent by the form via POST method', () => {
  before(() => {
    Cypress.config('baseUrl', 'https://www.reddit.com');
    cy.server();
    cy.route({
      method: 'POST',
      url: '/login'
    }).as('redditLogin');
  });
  it('should capture the login and password', () => {
    cy.visit('/login');
    cy.get('#loginUsername').type('username');
    cy.get('#loginPassword').type('password');
    cy.get('button[type="submit"]').click();

    cy.wait('@redditLogin').then(xhr => {
      cy.log(xhr.responseBody);
      cy.log(xhr.requestBody);
      expect(xhr.method).to.eq('POST');
    })
  });
});

I was hoping to capture this GitHub login request. GitHub

This is how the test pass in Reddit. https://i.stack.imgur.com/vKwAj.png

The result is the same even if I use:

cy.visit('/login', {
  onBeforeLoad: (win) => {
    delete win.fetch;
  }
});

Also having this issue! The request is not captured by cypress so it’s not mocked and the live API is called instead.

There is now a reproducible example so the thread should be reopened?

I also had this issue several times. I made the route pattern very generic, which must have matched.

I think this happens because of, that your cy.server & cy.request calls are at the beginning of your spec. I suppose the app loads faster than cypress can hook into for listen to the requests. I hope this issue could be fixed with #687

Sadly because of this undeterministic issue, I can’t use the wait on request for first reuqest(s) from my app.

While we were never given a clear reproducible example of this bug (I was never able to reproduce from https://github.com/cypress-io/cypress/issues/2700#issuecomment-629597892 either), I believe this was likely occuring for some people. There were some documented cases of similar bugs here: https://github.com/cypress-io/cypress/issues/5999

Resolution

We suggest migrating cy.route() to use cy.intercept(). cy.route() will be deprecated in a future version. Moving to this new command resolved many documented reproducible examples of ‘wait’ never catching a route in https://github.com/cypress-io/cypress/issues/5999 and may resolve your issue also.

If you’re experiencing a bug after upgrading to cy.intercept(), please open a new issue with a fully reproducible example that we can run.

@jennifer-shehane

Steps to reproduce: 1- Have a function like the one below:

callApi() {
        cy.intercept("POST", GetNetCoreUrlPrefix("example/url")).as("ApiCall");
        // DO STUFF TO CALL API...
        cy.wait("@ApiCall");
    }

2- Call it once 3- Call it another time in the same testcase

The cy.wait(“@ApiCall”) will think that the API has been already called in the second function call and the code will continue running (because of the first time we called the function). Im assuming it has something to do with some flag that’s being saved.

+1 - we see this pretty frequently and have to workaround with a bunch of assertions that elements on the page exist as a result of a route rendering.

Code example:

it('can be loaded', () => {
        cy.server();
        cy.route('GET', '/sales').as('getSales');
        cy.siteSignIn('test-user');
        cy.visit('/account/sales');
        cy.wait('@getSales');
        cy.get('#accountDashboardApp').should('be.visible');
        cy.get('.screen-sales').should('be.visible');
        cy.get('.sales').should('be.visible');
        cy.get('.tips').should('be.visible');
        cy.fullRegressionSnapshot('Sales Dashboard Page');
});

Yet, often this fails when the getSales route starts and finishes rendering before the wait call starts. If we take out the wait because presumably the route renders quickly enough, we get flaky results because the route is not always fast enough for the subsequent snapshot.

When this fails, we see the getSales route has already completed before cypress begins waiting for the route. To answer some troubleshooting questions from earlier in this thread; we are calling cy.server() and defining the route first thing in the test and the route endpoint does not change, it is always the same.

We need a reliable way to wait for a request to finish loading, whether it’s fast or slow. It’d be nice if we could have some kind of listener or way to call a wait before the command that will trigger the request, so it’s for certain waiting before the request starts.

We have similar issue, however wait ignores 100% of requests like PUT, POST and others (only get is okay). Also i have the problem on my local pc, not even CI. we tried to catch requests with:

cy.route("PUT", "api/textObject/*").as("waitTextObjectReload").wait("@waitTextObjectReload");

or

cy.route("PUT", "api/textObject/*", {}).as("waitTextObjectReload").wait("@waitTextObjectReload");

or

cy.route("PUT", "/api/textObject/*").as("waitTextObjectReload").wait("@waitTextObjectReload");

and other variations of this command. Example of request:

PUT 200 /api/textObject/AE961F7C6EB14A56BBACD295A97B8DA200000000

XHR request is displayed in cypress panel, however it’s completely ignored by wait.

Our app is split in two parts: 1st part is backbone, 2nd part is angular 7. The issue is reproducible in each. Requests go through nginx.

Also had this issue once in a while even though my route pattern is already very generic like below.

    export function search(term) {
      cy.server()
      cy.route({
        method: "POST",
        url: "**/graphql",
        status: 200
      }).as("searchResponse")
      cy.fetch(css.searchInput)
       .clear()
       .type(term)
      cy.wait("@searchResponse")
    }

The app only starts hitting the backend API after I type more than 3 letters of the search term. However, even with this delayed start of API request from the app, Cypress still wasn’t able to catch the route. Cypress was still waiting until it timed out even though the app has already received the response from the API and has already displayed it in UI.

@clock-rick I’d probably add an assertion about the url before defining the wait.

cy.get('.form-action > .button').click();
cy.url().should('include', 'account');
cy.wait('@postBody');

@jennifer-shehane What is the reasoning behind adding an assertion before the wait?

I have a set of tests that fails reliably on at least one of the cy.wait(‘@fixture’) calls, but it isn’t always the same call. This spec is 368 lines long, and it only started failing when I added a couple of new “it” blocks. Each of the blocks passes individually when run with .only. This leads me to believe it’s a timing issue or memory leak within Cypress.

Update: After noticing that more and more cy.wait calls were failing as I went along, I just refreshed the whole chrome page instead of letting cypress re-run the tests. All tests passed. I’m voting memory leak.

@jennifer-shehane, I have a reproducible example if you wanted to get on a call with me, I can share. I’m seeing in the network request in cypress’s console output as well as the network tab of Chrome’s devtools.

Cypress’s output window (left pane) shows a route object being setup (matching pattern), in the test object I never see the XHR entry. More over I have the exact same form that fires off the XHR request working 100% of the time, and does show a XHR entry in Cypress’s left pane output.

GET /api/misc/stuffs?x=y 200 262.549 ms - -

I’m seeing similar issues. For us it is intermittent. We have about 7 out of 55 specs which are susceptible to this type of failure. Pretty reliably at least one of them will fail on every CI run. Running the tests individually always seems to work.

@jennifer-shehane, I have a reproducible example if you wanted to get on a call with me, I can share. I’m seeing in the network request in cypress’s console output as well as the network tab of Chrome’s devtools.

Cypress’s output window (left pane) shows a route object being setup (matching pattern), in the test object I never see the XHR entry. More over I have the exact same form that fires off the XHR request working 100% of the time, and does show a XHR entry in Cypress’s left pane output.

GET /api/misc/stuffs?x=y 200 262.549 ms - -

@jennifer-shehane - why did we close this issue? It does seem like a memory issue to me - though I am on Cypress v2.1.0.

Many people have problems but you are simply closing issues, cool

I was having this same issue just now. My issue was that I was using fetch instead of XHR

in my cypress.json I had to add "experimentalFetchPolyfill":true and everything seemed to work with cy.route() and cy.wait()

I tried using cy.route2 with experimentalNetworkStubbing enabled and that didn’t work.

You should see your network requests appear in the steps pane. If they don’t, you might not have the Fetch polyfill enabled.

as @jennifer-shehane mentioned the order of ops is:

  • define cy.server() somewhere (in your before or in your command you want to sniff network requests on)
  • define your cy.route(<METHOD>, <URLMatcher>, <Options>).as("mySpecialAlias") before invoking the click event or button that triggers your ReST call
  • Do your steps to invoke the API
  • Define cy.wait(["@myAlias",...,"@myOtherAlias"]) <-- this handles multiple calls in one waiter with similar behavior to Promise.all()

hope this helps!

@clock-rick I’d probably add an assertion about the url before defining the wait.

cy.get('.form-action > .button').click();
cy.url().should('include', 'account');
cy.wait('@postBody');