Files
Jiwon ChoiandGitHub b1f49163f8 Return APP_PAGE instead of PAGES for production Instant Navigation tests (#95184)
### Why?

Current behavior:
https://github.com/vercel/next.js/actions/runs/28225010081/job/83615571987?pr=95184#step:31:8732

For a minimal-mode resume request carrying the Instant Navigation
testing cookie, Next.js skips the postponed render so the test receives
only the prefetched static shell. The resulting empty continuation was
represented as `PAGES`, but the App Page handler requires `APP_PAGE`,
causing error:

```
Error: Invariant app-page handler received invalid cache entry PAGES
```

### How?

Return an empty `APP_PAGE` response with the correct HTML or RSC content
type and postponed metadata. If the document continuation has no
`<head>` chunk, insert the Instant Navigation bootstrap while flushing
the stream.

Add production regression coverage for document and RSC-prefetch resume
requests using build-generated postponed state.

<!-- NEXT_JS_LLM_PR -->
2026-06-26 18:15:37 +00:00

69 lines
2.0 KiB
TypeScript

import { nextTestSetup } from 'e2e-utils'
describe('instant-navigation-resume', () => {
const { next } = nextTestSetup({
files: __dirname,
// This test directly emulates the platform's internal resume request using
// locally generated postponed state and private runtime switches.
skipDeployment: true,
env: {
NEXT_PRIVATE_TEST_HEADERS: '1',
NEXT_PRIVATE_MINIMAL_MODE: '1',
},
})
async function getPostponedState() {
const { postponed } = await next.readJSON('.next/server/app/index.meta')
expect(postponed).toEqual(expect.any(String))
expect(postponed.length).toBeGreaterThan(0)
return postponed as string
}
it('handles an instant-navigation document resume', async () => {
const postponed = await getPostponedState()
const cliOutputIndex = next.cliOutput.length
const response = await next.fetch('/', {
method: 'POST',
headers: {
cookie: 'next-instant-navigation-testing=1',
'next-resume': '1',
'x-matched-path': '/',
},
body: postponed,
})
expect(response.status).toBe(200)
await response.text()
expect(
next.cliOutput
.slice(cliOutputIndex)
.match(/Invariant app-page handler received invalid cache entry PAGES/)
).toBeNull()
})
it('handles an instant-navigation RSC prefetch resume', async () => {
const postponed = await getPostponedState()
const cliOutputIndex = next.cliOutput.length
const response = await next.fetch('/', {
method: 'POST',
headers: {
cookie: 'next-instant-navigation-testing=1',
'next-resume': '1',
'next-router-prefetch': '1',
rsc: '1',
'x-matched-path': '/',
},
body: postponed,
})
expect(response.status).toBe(200)
await response.text()
expect(
next.cliOutput
.slice(cliOutputIndex)
.match(/Invariant app-page handler received invalid cache entry PAGES/)
).toBeNull()
})
})