cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.02k stars 3.18k forks source link

localhost URL not accessible after cy.request on server #25397

Open nicolas-marien opened 1 year ago

nicolas-marien commented 1 year ago

Current behavior

Hello 👋

We are trying to integrate Cypress but we run into an issue. Cypress tells us that our frontend is not reachable. The client runs on port 4200 and the server on port 3333. We are working in an monorepo managed by NX. The client is a basic React application, and the server is a NestJS application.

First we make a call to our server using cy.request and then we use cy.visit. The cy.visit fails (see screenshot attached) telling is that the connection is refused. When opening http://localhost:4200 on a browser behaves as intended.

When we change the server URL to point to 127.0.0.1 things work fine (but cookies are broken, and we want cookie 🍪).

Capture d’écran 2023-01-09 à 11 49 55

I put together a bare bone repository where things can be reproduced: https://github.com/nicolas-marien/nx-cypress-localhost-issue

Please find attached the output of DEBUG=cypress:* NODE_DEBUG=request y nx e2e client-e2e --watch

I do not have any corporate proxy.

Thank you for your help.

Desired behavior

The cy.visit(/) call should open my client.

Test code to reproduce

Start client and server using yarn nx run-many --target=serve Run yarn nx e2e client-e2e --watch and select the only spec file.

Cypress Version

12.3.0

Node version

18.10.0

Operating System

MacOS 12.6.1 (also happens on Ventura)

Debug Logs

[out.txt](https://github.com/cypress-io/cypress/files/10373543/out.txt)

Other

No response

astone123 commented 1 year ago

@nicolas-marien thanks for the repo. I cloned it, installed dependencies and ran

yarn nx e2e client-e2e --watch

which launched Cypress, but I didn't see any client or server running on port 4200 or 3333 like you described. So, the test failed when calling cy.login because the server wasn't running.

Am I missing something?

nicolas-marien commented 1 year ago

@astone123 Oh sorry! I forgot to add the start command to run everything 🙄 Both client and server can be run using yarn nx run-many --target=serve. I'll update the OP and the repo to add a start:all script.

astone123 commented 1 year ago

@nicolas-marien thanks for clarifying. I was able to run the application but I'm not seeing the same error message. The test passes for me

Image

nicolas-marien commented 1 year ago

Thank you @astone123 for your answer. Truth be told that is a bit of a bummer 😅 Since the issue happens on several of our machines (both corporate and personal). Do you have any idea of what could be involved? Does cy.request happens to have some inner-working behavior that might result in our issue? Also, the error states that 127.0.0.1:4200 is unreachable. That shows that localhost has already been resolved. Do you know why that print this and not localhost:4200. Thank you for your help.

ZachJW34 commented 1 year ago

@nicolas-marien I was able to reproduce your issue by using Node18. With Node16, the e2e test works fine. There was a DNS change between Node16 and Node18 (issue) that you might be running into. I'll keep investigating to see where the problem lies and if there is a workaround for you.

ZachJW34 commented 1 year ago

Can you try adding "host": "127.0.0.1" to your apps/client/project.json for the serve.options configuration and see if it works? I tried it locally and it worked for me. You can still use localhost for visiting your site locally and telling Cypress how to connect but Webpack needs to be forced to use ipv4 over ipv6 (my understanding of DNS is fuzzy admittedly).

To be explicit, this is the update I made:

"serve": {
  "executor": "@nrwl/webpack:dev-server",
  "defaultConfiguration": "development",
  "options": {
    "buildTarget": "client:build",
    "hmr": true,
    "host": "127.0.0.1" <----
  },

Still investigating if there is an issue on our side. Cypress will warn the user it can't connect to the provided baseUrl, but in this case there is no warning yet the connection fails when using cy.visit.

Edit: After investigating, it looks like the issue is due to Nx's use of localhost for the default value of host used in for their webpack-dev-server executor. This use prohibits use of ipv4. Workaround described above should be enough, you can also use 0.0.0.0 to open both ipv4 and ipv6.

ZachJW34 commented 1 year ago

I closed this prematurely, I'm now seeing some buggy behavior based on your reproduction where a request made to your backend before visiting is causing Cypress to lock onto the ipv4 range (hence the 127.0.0.1 connection refused). Shifting the request to be after the visit doesn't display this behavior. I'm working on a very simple reproduction.

ZachJW34 commented 1 year ago

I've created a simple reproduction here. The README explains the bug in detail. My understanding of the bug is:

If you move the request to after the visit, everything works fine. Also, if Cypress is allowed to visit before sending the request (by commenting out the request), adding the request back in works fine. There must be some server state being set from the initial request that influences subsequent visit requests.

nicolas-marien commented 1 year ago

Thank you @ZachJW34 for your detailed answers. To give some more context, we issue a request before visiting the page to create a test user (possibly not the best practice) and get a cookie for this user.

ZachJW34 commented 1 year ago

@nicolas-marien your workflow is totally valid. I've routed this to the e2e team and they will prioritize accordingly.

nicolas-marien commented 1 year ago

@ZachJW34 thanks for the feedback.

gitnubster commented 1 year ago

Are there any updates on this issue? Thanks.

cerojasg1 commented 1 year ago

Same issue here I was using node 18.10 and cypress 12.9.0, I downgraded the node version to 16.13.2 and that fixed the issue.

UgurGumushan commented 1 year ago

We are unable to upgrade to node 18 due to this issue

lucaslenz commented 1 year ago

For me instructing the bundle server (in my case vite) to explicitly use host '127.0.0.1' fixed the issue.

dbeuchler commented 1 year ago

For me instructing the bundle server (in my case vite) to explicitly use host '127.0.0.1' fixed the issue.

This was solving the issue also on my dev-env. Using v12.9.0

UgurGumushan commented 1 year ago

I have set default nvm version from 16 to 20 with nvm alias default 20 To make sure apps open outside the console have some latest nvm version (I intended to use Cypress GUI) Upgraded cypress to v12.17.3 And this resolved the problem

zihotki commented 1 year ago

The original issue was reported for Macos but same was happening on Windows for us. Upgrading to latest versions of Node and Cypress didn't help. The only thing which helped was changing the precedence rules for network stack by:

netsh interface ipv6 set prefix ::ffff:0:0/96 60 4

MetRonnie commented 1 year ago

I get this error after upgrading Node from 16 to 18 or 20. I'm not even using cy.request().

A workaround when using Vite is hinted at here: https://vitejs.dev/config/server-options.html#server-host

// vite.config.js
import { defineConfig } from 'vite'
import dns from 'dns'

dns.setDefaultResultOrder('ipv4first')

export default defineConfig({ ...

or set server.host to true but this will allow access to the server over the network


(Adding the code ECONNREFUSED to this thread to make it easier to find)

rkrisztian commented 1 year ago

This issue is painful to me, see my test results below.

Test machines:

Test project:

Test cases:

Conclusion:

MikeMcC399 commented 1 year ago

@rkrisztian

Are you able to test on Node.js 20? In some circumstances I've noticed that these IPv4 / IPv6 issues are less of a problem on the later version of Node.js.

This is not to invalidate your issue in any way, but it might possibly be a workaround.

rkrisztian commented 1 year ago

Thanks, @MikeMcC399, I did some quick checks on Linux with Node v20.9.0, my test case results did not change, even the IPv6 issue is still the same.

cesarvarela commented 11 months ago

News about this? We are using Gatsby v5, and Node 18 is a requirement.

Things I've tried:

Considering this bug is almost a year old, I'd like to know if there is any workaround I'm missing.

levino commented 11 months ago

You just have to start your dev server / app on a host with an ipv4 address. In my case bun astro dev --host 127.0.0.1. No settings / parameters for cypress needed. It looks like cypress cannot resolve localhost to an ipv6 address. Hope that helps.

RobertWagnerPLG commented 9 months ago

I just run into this issue (Cypress v12.17.4 with node v18.16.1). Configure the serving webapp (Angular) to use the host "127.0.0.1" instead the default "localhost" fixed the problem on my side.

Before I found this workaroud in this issue-thread here I had another one (Working with proxy and default "localhost"-setting"): I'm working behind a proxy, but this proxy isn't defined globally. Only in the .npmrc the proxy is set. Cypress takes this proxy and says that it is managed by the system.

RobinMeow commented 7 months ago

My workaround for anyone interested (Using angular, but should work for any, imho)

I had a few personal requirements:

go to your support file cypress/support/e2e.ts and add this one line of code: fetch(Cypress.env('baseUrl'), { method: 'GET' }) // fire and forget

Edit 2024-08-29: The intend is to have the first http request go the front end. The support file runs before the tests (to eliminate race conditions). Depending on what other code you have in your support file, you should most likely put that line of code at the top of the file.

I figured, since the cause for the issue is a cy.request() (and it was still an issue, after I replaced my login logic with fetch()), we can just do a fetch before all tests run.

Of course you can use a hardcoded url for this fetch('http://localhost:4200', {method: 'GET'}), but I used mine from the cypress.config.ts image

Give me a thumbs up, if it works for you as well. I will report back, If I improve or change anything or if it stop working :C

rphmee-lo commented 7 months ago

go to your support file cypress/support/e2e.ts and add this one line of code: fetch(Cypress.env('baseUrl'), { method: 'GET' }) // fire and forget

This resolved the issue for me, quick and easy without having to change the host.

fre-ben commented 7 months ago

@RobinMeow solution worked like a charm. Thanks!

robbporto commented 6 months ago

I was using Node 20.10.0 and had to downgrade to Node 16.13.2. I can confirm that this is a bug.

ph1823 commented 6 months ago

Ma solution de contournement pour toute personne intéressée (en utilisant angulaire, mais devrait fonctionner pour tout le monde, à mon humble avis)

J'avais quelques exigences personnelles :

  • Je ne voulais pas changer la propriété hôte. Je veux que l'application s'exécute sur localhost, pas sur 127.0.0.1
  • La solution de contournement ne devrait pas trop affecter les performances (durée d'exécution totale du test e2e) (pas du tout au mieux)
  • ne devrait pas causer beaucoup de passe-partout
  • ne devrait pas nécessiter d'outils tiers, aucun effort supplémentaire

allez dans votre fichier de support cypress/support/e2e.ts et ajoutez cette ligne de code : fetch(Cypress.env('baseUrl'), { method: 'GET' }) // fire and forget

J'ai pensé que, puisque la cause du problème est un cy.request() (et c'était toujours un problème, après avoir remplacé ma logique de connexion par fetch()) , nous pouvons simplement effectuer une récupération avant l'exécution de tous les tests.

Bien sûr, vous pouvez utiliser une URL codée en dur pour cela fetch('http://localhost:4200', {method: 'GET'}), mais j'ai utilisé la mienne ducypress.config.ts image

Donnez-moi un coup de pouce, si cela fonctionne aussi pour vous. Je ferai un rapport si j'améliore ou change quelque chose ou si cela cesse de fonctionner :C

what node version ? what angular version ? Because on my case dont work :( edit: a solution for me is set host to 0.0.0.0 and that work, url in cypress is localhost

zayehalo commented 6 months ago

Can you try adding "host": "127.0.0.1" to your apps/client/project.json for the serve.options configuration and see if it works? I tried it locally and it worked for me. You can still use localhost for visiting your site locally and telling Cypress how to connect but Webpack needs to be forced to use ipv4 over ipv6 (my understanding of DNS is fuzzy admittedly).

To be explicit, this is the update I made:

"serve": {
  "executor": "@nrwl/webpack:dev-server",
  "defaultConfiguration": "development",
  "options": {
    "buildTarget": "client:build",
    "hmr": true,
    "host": "127.0.0.1" <----
  },

Still investigating if there is an issue on our side. Cypress will warn the user it can't connect to the provided baseUrl, but in this case there is no warning yet the connection fails when using cy.visit.

Edit: After investigating, it looks like the issue is due to Nx's use of localhost for the default value of host used in for their webpack-dev-server executor. This use prohibits use of ipv4. Workaround described above should be enough, you can also use 0.0.0.0 to open both ipv4 and ipv6.

This is what worked for me. Thanks.

ETA: This works only when running the target headless. I still get the issue when using cypress open Very super annoying as it's very hard to debug failing tests. Cypress v13.8.1 Node 20.11.0

ETA #2: This only worked locally for me. Totally flopped in our gitlab pipeline

atefevan commented 2 months ago

go to your support file cypress/support/e2e.ts and add this one line of code: fetch(Cypress.env('baseUrl'), { method: 'GET' }) // fire and forget

This resolved the issue for me, quick and easy without having to change the host.

The solution is insane ! Works on Node 18 as well as 20 !! Could you explain the solution ? @rphmee-lo

RobinMeow commented 2 months ago

@atefevan

The solution is insane ! Works on Node 18 as well as 20 !! Could you explain the solution ?

Here is the original comment: https://github.com/cypress-io/cypress/issues/25397#issuecomment-2030468503

It was quite some time ago. To explain it propperly I'd need dive into the cypress code itself. The bug is not really transparent and I don't wanna invest the time to dig deep into it.

Please note the solution is a hacky work-around and not a good solution. (Yet, it didnt seem to cause any other harm so might as well use the quick hack and continue productivity)

Explanation to what I know: When I refer to http request or just requests I mean ´cy.request()´ ´cy.visit()´ etc.. they all just http requests.
The bug occurs when the first request is not your front end. In this case your other requests will fail (If I remeber correctly, this includes the requests being made by cypress itself).

Example:
Imagine you have two tests run in parallel. One test (lets call it "SERVER"-test) starts of with a cy.request to the server, whereas the other "CLIENT"-test starts of with a front end cy.visist(). You now have a race condition. You don't know which one will run first. Assumingly both tests are correct, they will both fail if the "SERVER"-test runs first. Likewise both will succeed if the "CLIENT"-tests runs first.

To prevent that we simply make sure that the very first request is made to the front end. We do this by putting that one line of code into the support file. It can by a cy.visit, a cy.request a fetch, or whatever http request (any will work). (There might be a better place to put that fetch in, than the support file, but it was the best thing I found, when I searched for a suitable place. The support file runs before the tests, the fethc should be at the top of the file depending on what other things you have in there.)

Last sidenote:
Usually a Http Request also returns a Http Response. We don't actually care about the result. We don't actually even care if it resolves at all. It only matters for the request to be made. Hence the comment "fire and forgot". Which is a comment you use to describe "work" you sheduled (like calling an async method) but you didnt wait (await) for the work to complete because you dont care when it finishes.

mikegwhit commented 2 months ago

See https://github.com/cypress-io/cypress/issues/27962 if researching ipv6 issues, i added my comment today relating to disabling ipv6 on windows when relying on ipv4 tunnels

scolson-pass commented 1 month ago

We are also facing this issue (cc @mageoffray) while testing a VITE v5.4.3 application. We'd like to process some cy.request() before loading the application and starting the tests but it fails unless adding a cy.visit() before as a workaround.

Cypress version used: 13.4.2