Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cy.wait timeouts on aborted xhr requests #9549

Comments

@mac2000
Copy link

mac2000 commented Dec 7, 2020

Current behavior

Whenever there is something like this in app (pseudo code):

var xhr = new XHR()
xhr.send()
xhr.abort() // <- POI
xhr = new XHR()
xhr.send()

and this in test:

cy.intercept('...').as('xhr')
cy.visit('/')
cy.wait('@xhr')

cypress timeouts with an error Timed out retrying: cy.wait() timed out waiting 30000ms for the 1st response to the route: xhr. No response ever occurred.

image

As you can see on screenshot:

  • in network tab there is aborted and follow up requests
  • app renders response
  • on a sidebar we can see that interception recognized the query (yellow marks)
  • but somehow second requests still have spinning icon on left side

Desired behavior

Page with app loads and works as expected, cypress should handle such cases as well

Test code to reproduce

index.html - aka our app

<!DOCTYPE html>
<html>
  <head>
    <title>demo</title>
  </head>
  <body>
    <button onclick="good()">good</button>
    <button onclick="bad()">bad</button>
    <script>
      function good() {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.onload = () => {
          document.body.innerHTML += "<pre>" + JSON.stringify(xhr.response) + "</pre>";
        };
      }
      function bad() {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.abort();
        xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.onload = () => {
          document.body.innerHTML += "<pre>" + JSON.stringify(xhr.response) + "</pre>";
        };
      }
    </script>
  </body>
</html>

demo.spec.js

describe("demo", () => {
  it("should handle aborted requests", () => {
    cy.intercept("https://jsonplaceholder.typicode.com/todos/1").as("xhr");
    cy.visit("http://localhost:3000");
    cy.get("button").contains("bad").click();
    cy.wait("@xhr");
  });
});

Versions

cypress - 6.0.1
node - 12.8.3
chrome - 87.0.4280.88
os - macos 11.0.1 (20B29)
ci provider - undefined

PS: not sure but it might be related to this PR which was made by @brian-mann

@jennifer-shehane
Copy link
Member

jennifer-shehane commented Dec 8, 2020

I'm not able to reproduce the error with the provided code. It looks like in your screenshot you have a server running with some browser-sync requests - can you provide a full repo of the example? Something else may be involved outside of the example you gave.

Please also try the newly released 6.1.0 as it has some intercept fixes.

Test passes with only code provided above

Screen Shot 2020-12-08 at 11 16 33 AM

@jennifer-shehane jennifer-shehane added stage: needs information Not enough info to reproduce the issue topic: cy.intercept() labels Dec 8, 2020
@mac2000
Copy link
Author

mac2000 commented Dec 8, 2020

@jennifer-shehane indeed you are right, I'm also confirming the same behavior

So whenever you are doing something like: php -S 0.0.0.0:3000 everything works as expected

But when we use something like: npx lite-server which indeed use some socket connections to reload pages on changes issue is reproducible

Unfortunately, 6.1.0 still has this issue

As about repo, I bed that it will be reproduced with npx lite-server but if it will help please let me know I will create one

@guilhermebruzzi
Copy link

Running tests with Cypress 6.1.0 and having the same problem with cy.intercept.
When my axios request is aborted (appears (canceled) on Chrome network tab), cy.intercept keeps waiting for it, instead of getting the new request that is active:

Screen Shot 2020-12-10 at 10 46 04

image

With cy.server and cy.route works fine

@guilhermebruzzi
Copy link

And not directly related to this issue, but cy.state('requests') is not having the requests intercepting by cy.intercept just like cy.route? There is another issue talking about it?

@jennifer-shehane
Copy link
Member

@guilhermebruzzi Can you provide an example of the cy.state('requests') being different with cy.intercept()? The test below works and our tests in Cypress ensure if is defined the same.

  it('test cy.intercept()', () => {
    cy.intercept('/users').as('getUrl')
    cy.visit('https://example.com')
    cy.window().then((win) => {
      const xhr = new win.XMLHttpRequest()
      xhr.open('GET', '/users')
      xhr.send()
    })
    cy.wait('@getUrl').then(() => {
      expect(cy.state('requests')).to.have.length(1)
    })
  })

Also, we'll need a reproducible example for the issue in this thread. Without that, the issue will need to be closed as we can't move forward with investigating.

@jennifer-shehane
Copy link
Member

Right now there doesn't seem to be enough information to reproduce the problem on our end. We'll have to close this issue until we can reproduce it. This does not mean that your issue is not happening - it just means that we do not have a path to move forward.

Please comment in this issue with a reproducible example and we will consider reopening the issue. Here are some tips for providing a Short, Self Contained, Correct, Example and our own Troubleshooting Cypress guide.

@jennifer-shehane jennifer-shehane removed the stage: needs information Not enough info to reproduce the issue label Jan 20, 2021
@mac2000
Copy link
Author

mac2000 commented Jan 20, 2021

hm, here you go:

git clone https://github.com/mac2000/cypress-9549
cd cypress-9549
npm i
npm start
npm test
repro.mp4

@jennifer-shehane
Copy link
Member

jennifer-shehane commented Jan 20, 2021

@mac2000 Nice. Thanks for this.

Sometimes the first time I run this it passes, but on rerun it definitely fails.

index.html

<html>
<body>
  <button onclick="bad()">bad</button>
  <script>
    function bad() {
      let xhr = new XMLHttpRequest();
      xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
      xhr.responseType = "json";
      xhr.send();
      xhr.abort();
      
      xhr = new XMLHttpRequest();
      xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
      xhr.responseType = "json";
      xhr.send();
      xhr.onload = () => {
        document.body.innerHTML += "<pre>" + JSON.stringify(xhr.response) + "</pre>";
      };
    }
  </script>
</body>
</html>
it('should handle non-aborted requests', () => {
  cy.intercept('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('good').click()
  cy.get('pre').contains('delectus') // response body renders to page
  // ✅ passes
  cy.wait('@xhr')
})

it('cy.route should handle aborted requests', () => {
  cy.server()
  cy.route('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('good').click()
  cy.get('pre').contains('delectus') // response body renders to page
  // ✅ passes
  cy.wait('@xhr')
})

it('cy.intercept should handle aborted requests', () => {
  cy.intercept('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('bad').click()
  cy.get('pre').contains('delectus')  // response body renders to page
  // ❗️ times out sometimes
  cy.wait('@xhr')
})

Screen Shot 2021-01-20 at 4 18 36 PM

@cypress-bot cypress-bot bot added the stage: ready for work The issue is reproducible and in scope label Jan 20, 2021
@mac2000
Copy link
Author

mac2000 commented Jan 20, 2021

@jennifer-shehane indeed and it seems that root cause here is this socket connection being involved, so whenever on page we have some socket activity it somehow brakes everything, probably can test it even without lite-server by just adding socket connection to a page, but still need some server for this

@sainthkh
Copy link
Contributor

sainthkh commented Feb 3, 2021

There are minor errors in the reproducible example above:

  1. index.html doesn't contain good button.
  2. Test 2 should click bad button, not good.

The corrected example is below:

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>demo</title>
  </head>
  <body>
    <button onclick="good()">good</button>
    <button onclick="bad()">bad</button>
    <script>
      function good() {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.onload = () => {
          document.body.innerHTML += "<pre>" + JSON.stringify(xhr.response) + "</pre>";
        };
      }
      function bad() {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.abort();
        xhr = new XMLHttpRequest();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
        xhr.responseType = "json";
        xhr.send();
        xhr.onload = () => {
          document.body.innerHTML += "<pre>" + JSON.stringify(xhr.response) + "</pre>";
        };
      }
    </script>
  </body>
</html>

test.js*

it('should handle non-aborted requests', () => {
  cy.intercept('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('good').click()
  cy.get('pre').contains('delectus') // response body renders to page
  // ✅ passes
  cy.wait('@xhr')
})

it('cy.route should handle aborted requests', () => {
  cy.server()
  cy.route('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('bad').click()
  cy.get('pre').contains('delectus') // response body renders to page
  // ✅ passes
  cy.wait('@xhr')
})

it('cy.intercept should handle aborted requests', () => {
  cy.intercept('https://jsonplaceholder.typicode.com/todos/1').as('xhr')
  cy.visit('index.html')
  cy.get('button').contains('bad').click()
  cy.get('pre').contains('delectus')  // response body renders to page
  // ❗️ times out sometimes
  cy.wait('@xhr')
})

@JasonFairchild
Copy link

I can also reproduce this. Though it is flaky in my case since canceled requests don't always happen. But every time the canceled request does happen, cy.wait('@alias') definitely fails. I'm running 6.4.0

image

@everton-nasc
Copy link

everton-nasc commented Feb 16, 2021

I had the same issue on my CI environment (failure ratio up to 25% - GitActions) and the workaround (a good one so far) that I did was:

  1. Get rid from cy.intercept for lazy endpoints ---> replace it to cy.server and cy.route
  2. Increased the defaultCommandTimeout from 3s to 6s (we have a really heavy/lazy endopint)

Or
Downgrade your Cypress version prior to cy.intercept feature. - I had no issues with Cypress 4.8.1.

The failure rate went to 0.4%.

@bahmutov
Copy link
Contributor

I added a failing reproducible test in https://github.com/cypress-io/cypress-example-recipes/tree/aborted-request-example

https://github.com/cypress-io/cypress-example-recipes/blob/aborted-request-example/examples/stubbing-spying__intercept/cypress/integration/spy-on-fetch-spec.js#L78-L87

// https://github.com/cypress-io/cypress/issues/9549
it('handles aborted requests', () => {
  cy.intercept('https://jsonplaceholder.typicode.com/todos/1').as('aborted')
  cy.get('#aborted-request').click()
  cy.wait('@aborted', { timeout: 2000 })

  cy.wait(1000)
  cy.get('#aborted-request').click()
  cy.wait('@aborted', { timeout: 2000 })
})

Screen Shot 2021-02-16 at 7 28 41 PM

@JasonFairchild
Copy link

Does the PR from saintkhk fix the issue for Cypress, or is it just a PR to fix some tests that also demonstrate this behavior?

@bahmutov
Copy link
Contributor

bahmutov commented Feb 17, 2021 via email

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Feb 22, 2021

The code for this is done in cypress-io/cypress#14885, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Mar 15, 2021

Released in 6.7.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v6.7.0, please open a new issue.

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Mar 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.