## TL;DR
toHaveURL timed out because the navigation itself is slower in CI: a cold browser, a slow webServer boot, or a baseURL pointing at a service that is not ready yet. The assertion races the navigation and loses. Make the webServer's readiness a precondition of the job, wait for the navigation's load state explicitly, and raise the assertion timeout only after the page is provably loading.

## The query
```text
playwright expect(page).toHaveURL() timed out after navigation only in CI  -  how to fix
```

## Use this when
- expect(page).toHaveURL() times out only in CI
- navigations are visibly slower in CI than locally
- the app uses a webServer or service containers in CI

## Not for
- the expected URL is wrong in every environment (the app navigates somewhere unexpected; fix the app or the expectation)
- the navigation is never attempted (the click or goto that should trigger it failed first)
- the URL is correct but the assertion is checked before a client-side redirect finishes (wait for the redirect explicitly)

## Steps

### 1. Check what the page was doing when the assertion timed out
The trace or video shows whether the page was loading slowly, stuck blank, or already there. Each points at a different fix.
```bash
npx playwright show-trace test-results/trace.zip
```
Expected output: you see the page state at timeout: slow load, blank page, or wrong page.

### 2. Ensure the webServer is ready before tests start
A server still booting when tests begin makes every navigation slow. Playwright's webServer config with a reuse guard, or a CI service healthcheck, makes readiness explicit.
```ts
// playwright.config.ts
export default defineConfig({
  webServer: {
    command: 'npm run dev',
    url: 'https://example.com/health',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },
});
```
Expected output: tests only start after the server responds to the health check.

### 3. Wait for the load state, then assert the URL
Separate the navigation wait from the URL assertion so the timeout message tells you which one failed.
```js
await page.goto('/login');
await page.waitForLoadState('domcontentloaded');
await expect(page).toHaveURL(/.*\/dashboard/, { timeout: 15000 });
```
Expected output: the navigation completes before the assertion runs, and the assertion has its own documented timeout.

### 4. Verify against the real CI service topology
If baseURL points at a service that is slow to become reachable in CI, a service container healthcheck or a readiness probe in the workflow, the navigation will keep racing.
```yaml
services:
  web:
    image: myapp:test
    options: >-
      --health-cmd "curl -f https://example.com/health"
      --health-interval 10s
```
Expected output: the service is reachable before the first navigation, and the timeout is gone.

## Tool compatibility
- Playwright 1.40 - 1.55
- any browser Playwright supports

## Variant phrasings
- toHaveURL() timed out after navigation in CI
- playwright expect toHaveURL timeout CI only
- waiting for page to have URL timed out in CI

## Why it happens
toHaveURL polls the page URL until it matches or the timeout expires. In CI everything upstream is slower: the browser starts cold, the dev server compiles on first request, service containers boot sequentially. The assertion's default timeout was tuned for a warm local environment, so it expires while the navigation is still legitimately in flight.

## Edge cases / pitfalls
- SPA client-side routing changes the URL without a document navigation; waitForURL with the right waitUntil matches the app's behavior better
- a trailing slash or query-param difference between baseURL and the app's canonical URL fails the match; compare exactly what the browser shows
- raising the global expect timeout masks slow navigations suite-wide; prefer per-assertion timeouts with comments
- redirect chains (http to https, trailing-slash normalization) each cost round trips in CI; assert on the final URL, not an intermediate one

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_8bPWwqhCVtk-ajIvx4HwFA
