Repository: vercel/next.js
Pull request: test(typed-routes): fix flaky tests by increasing retry timeout

## Fixing a bug

### What?

Three tests in `test/e2e/app-dir/typed-routes/typed-routes.test.ts` were intermittently failing with `Failed to retry within 3000ms`:

- `typed-routes › should generate route types correctly`
- `typed-routes › should have passing tsc after start`
- `typed-routes › should correctly convert custom route patterns from path-to-regexp to bracket syntax`

### Why?

The test harness marks the dev server as "ready" when it sees `✓ Ready in X` in stdout. However, the server logs this message _before_ completing the full initialization sequence in `start-server.ts` — specifically before `getRequestHandlers()` resolves, which chains into `setupDevBundler()`. Route type generation (`routes.d.ts`) only completes inside `setupDevBundler`'s Watchpack `aggregated` event handler.

The gap between the "Ready" log and actual `routes.d.ts` completion can exceed 3000ms on loaded CI runners, causing the tests' `retry()` calls to time out before the file exists.

A secondary issue in `should have passing tsc after start`: the test called `next.stop()` immediately after the server was considered ready, before `routes.d.ts` was written. This meant `tsc` ran against a project missing the generated types, causing type errors even though the server had been healthy.

### How?

- Increased the `retry()` timeout from the default 3000ms to 30000ms for the three affected tests.
- Added a `retry()` guard in `should have passing tsc after start` to wait for `routes.d.ts` to be present and correct _before_ stopping the server. This ensures `tsc` sees complete generated types.

Verified by running the full test file 3 consecutive times locally with `NEXT_SKIP_ISOLATE=1 NEXT_TEST_MODE=dev pnpm testheadless` — all passes, all 5 tests green each run.

Changed files:
filename: test/e2e/app-dir/typed-routes/typed-routes.test.ts
patch: @@ -23,13 +23,21 @@ describe('typed-routes', () => {
   }
 
   it('should generate route types correctly', async () => {
+    // Route type generation happens after the "Ready" log fires; give it time.
     await retry(async () => {
       const dts = await next.readFile(`${next.distDir}/types/routes.d.ts`)
       expect(dts).toContain(expectedDts)
-    })
+    }, 30000)
   })
 
   it('should have passing tsc after start', async () => {
+    // Wait for routes.d.ts before stopping the server; route type generation
+    // happens after the "Ready" log fires and tsc may run before it completes.
+    await retry(async () => {
+      const dts = await next.readFile(`${next.distDir}/types/routes.d.ts`)
+      expect(dts).toContain(expectedDts)
+    }, 30000)
+
     await next.stop()
     try {
       const { stdout, stderr } = await execa('pnpm', ['tsc', '--noEmit'], {
@@ -47,6 +55,7 @@ describe('typed-routes', () => {
   })
 
   it('should correctly convert custom route patterns from path-to-regexp to bracket syntax', async () => {
+    // Route type generation happens after the "Ready" log fires; give it time.
     await retry(async () => {
       const dts = await next.readFile(`${next.distDir}/types/routes.d.ts`)
 
@@ -59,7 +68,7 @@ describe('typed-routes', () => {
       // Test catch-all zero-or-more: :slug* -> [[...slug]]
       expect(dts).toContain('"/blog/[category]/[[...slug]]"')
       expect(dts).toContain('"/api-legacy/[version]/[[...endpoint]]"')
-    })
+    }, 30000)
   })
 
   if (isNextDev) {