fix(testing): reserve dev-server ports in the react-router e2e suite - #36585
Merged
Conversation
Both e2e cases in this suite generated their app without --port, so both took the 4200 default. When the playwright case's dev server had not released 4200 by the time the cypress case's server probed it, the second server relocated to 4201 while Playwright kept navigating to 4200 — "Could not connect to localhost: Connection refused". Two of the three browsers still passed, because the leftover server was serving an identical app until it exited mid-run. Reserve a port per app via reservePort() and pass it as --port, which the react generator threads into both the vite dev server and the playwright config's baseURL/webServer url. reservePort() scans from 6100, outside the framework-default zone, so the reserved ports cannot collide with each other or with a parallel suite on a generator default.
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit 4cf2083
☁️ Nx Cloud last updated this comment at |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
A later failure showed the collision is not confined to one file. The ts-solution suite (pid 32156) failed the same way while the ts-paths suite (pid 29595) was running alongside it on the same agent — its own log records "Cypress installed by process 29595". Both suites put every dev server on the 4200 default, so the loser relocates to 4201 while Playwright keeps navigating to 4200. reservePort()'s lock files are shared across processes, so reserving in both suites is what actually separates them. Corrects the comment in ts-paths, which blamed the two cases within that one file.
FrozenPandaz
enabled auto-merge (squash)
August 5, 2026 22:01
AgentEnder
approved these changes
Aug 5, 2026
polygraph-snapshot-app Bot
pushed a commit
that referenced
this pull request
Aug 11, 2026
…36585) ## Current Behavior Both e2e cases in `e2e/react/src/react-router-ts-paths.test.ts` generate their app without `--port`, so both take the `4200` default: the `should execute e2e tests using playwright` case, and the `should execute e2e tests using cypress` case that runs right after it. When the first case's dev server has not released `4200` by the time the second case's server probes it, the second server relocates itself — but nothing tells Playwright, whose `baseURL` is still `4200`: ``` Port 4200 is in use, trying another one... ➜ Local: http://localhost:4201/ ... Error: page.goto: Could not connect to localhost: Connection refused - navigating to "http://localhost:4200/", waiting until "load" ``` A detail worth noting, because it makes the failure look stranger than it is: **two of the three browsers pass.** Chromium and Firefox connect to `4200` fine and only webkit fails. The leftover server from the previous case is still serving `4200`, and since it is the same generated react-router app the `h1` says "Welcome" and the assertions hold — until that server exits mid-run and the last browser hits a dead port. Observed on 2026-08-05, and this suite also failed in master run [30947753956](https://github.com/nrwl/nx/actions/runs/30947753956) (2026-08-04), though I did not keep that log so I cannot confirm the same cause. ## Expected Behavior Each app gets its own reserved dev-server port, so the two cases cannot contend for `4200` at all. `reservePort()` (`e2e/utils/port-utils.ts`) claims a port via an atomic lock file and verifies the OS port is actually free, scanning from `6100` — deliberately outside the framework-default zone of `3000`/`4200`/`5173`/`8080`. So the reserved ports collide neither with each other nor with a parallel suite that generated an app on a framework default. The port is passed as `--port`, which `@nx/react:app` already threads into both ends of the problem — the vite dev server (`createOrEditViteConfig`'s `port`/`previewPort`) and the Playwright config's `baseURL` and `webServer.url` (via `getReactRouterE2EWebServerInfo`). No config file surgery needed; this is the same approach `react-vite.test.ts` already uses for its custom-port case. `killPorts()` is called after each e2e run. That part is hygiene rather than correctness — the distinct reserved ports are what make the collision structurally impossible — it just stops dev servers accumulating on the agent. ## Related Issue(s) N/A <!-- polygraph-session-start --> --- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-dark.svg"><img src="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-light.svg" width="16" height="22" align="middle" alt="Polygraph"></picture> <a href="https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/Reserve-dev-server-ports-in-the-react-router-e2e-suite-be8f4ac5">View session ↗</a></p> <!-- polygraph-session-end -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
Both e2e cases in
e2e/react/src/react-router-ts-paths.test.tsgenerate their app without--port, so both take the4200default: theshould execute e2e tests using playwrightcase, and theshould execute e2e tests using cypresscase that runs right after it.When the first case's dev server has not released
4200by the time the second case's server probes it, the second server relocates itself — but nothing tells Playwright, whosebaseURLis still4200:A detail worth noting, because it makes the failure look stranger than it is: two of the three browsers pass. Chromium and Firefox connect to
4200fine and only webkit fails. The leftover server from the previous case is still serving4200, and since it is the same generated react-router app theh1says "Welcome" and the assertions hold — until that server exits mid-run and the last browser hits a dead port.Observed on 2026-08-05, and this suite also failed in master run 30947753956 (2026-08-04), though I did not keep that log so I cannot confirm the same cause.
Expected Behavior
Each app gets its own reserved dev-server port, so the two cases cannot contend for
4200at all.reservePort()(e2e/utils/port-utils.ts) claims a port via an atomic lock file and verifies the OS port is actually free, scanning from6100— deliberately outside the framework-default zone of3000/4200/5173/8080. So the reserved ports collide neither with each other nor with a parallel suite that generated an app on a framework default.The port is passed as
--port, which@nx/react:appalready threads into both ends of the problem — the vite dev server (createOrEditViteConfig'sport/previewPort) and the Playwright config'sbaseURLandwebServer.url(viagetReactRouterE2EWebServerInfo). No config file surgery needed; this is the same approachreact-vite.test.tsalready uses for its custom-port case.killPorts()is called after each e2e run. That part is hygiene rather than correctness — the distinct reserved ports are what make the collision structurally impossible — it just stops dev servers accumulating on the agent.Related Issue(s)
N/A