mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
The problem is that the adapter expected a `output.pages` for the `/404` route so that it can add these files to other Pages handlers so that they can render a 404 at runtime (getStaticProps with revaliate/getServerSideProps): https://github.com/nextjs/adapter-vercel/blob/650effd340b4f676a9bca6e0cdaba9bf7d34595a/packages/adapter/src/outputs.ts#L421-L438 https://github.com/nextjs/adapter-vercel/blob/650effd340b4f676a9bca6e0cdaba9bf7d34595a/packages/adapter/src/node-handler.ts#L331-L340 But this 404 Pages handler was not actually emitted if it was prerendered, where it only emitted the `/404.html` static file. But that's not what the adapter needs. Tests: 1. Run as an e2e test 2. Add a variant with dynamically rendered 404 pages Full e2e test run: ~~https://github.com/vercel/next.js/actions/runs/29081105397~~ https://github.com/vercel/next.js/actions/runs/29087851475 Closes NEXT-4947
78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
import { createRouterAct } from 'router-act'
|
|
import { nextTestSetup } from 'e2e-utils'
|
|
|
|
describe('optimistic routing - parallel slot catch-all regression', () => {
|
|
const { next, isNextDev } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
if (isNextDev) {
|
|
// Optimistic routing is a production-build feature; in dev mode the
|
|
// router does not have complete information about which routes
|
|
// exist, so prediction is disabled.
|
|
test('skipped in dev mode', () => {})
|
|
return
|
|
}
|
|
|
|
// Regression test: a route with a parallel slot whose route is a
|
|
// required catch-all (e.g., /[teamSlug] with an
|
|
// `@actions/[...catchAll]` slot) used to poison route prediction.
|
|
// After visiting /myteam, navigating to a longer sibling URL like
|
|
// /myteam/myproject would reuse a prediction derived from the
|
|
// @actions slot's catch-all, resolving the URL to the team-page
|
|
// content even though /myteam/myproject is a separate route. The
|
|
// router skipped the server fetch entirely under the (incorrect)
|
|
// assumption that the URL was already known.
|
|
//
|
|
// Detection: visit /myteam (which would teach the bad prediction).
|
|
// Then click /myteam/myproject and wait for a response containing
|
|
// "PROJECT PAGE". With the bug, no server fetch happens, so "PROJECT
|
|
// PAGE" never arrives and the `act` waiter rejects. With the fix, no
|
|
// prediction matches /myteam/myproject and a real server fetch
|
|
// delivers the project page.
|
|
it('does not match a parallel-slot catch-all for an unrelated sibling URL', async () => {
|
|
let act: ReturnType<typeof createRouterAct>
|
|
const browser = await next.browser('/', {
|
|
beforePageLoad(page) {
|
|
act = createRouterAct(page)
|
|
},
|
|
})
|
|
|
|
// Reveal + click /myteam (a /[teamSlug] route). Its layout
|
|
// includes an `@actions/[...catchAll]` parallel slot. Visiting
|
|
// /myteam would, under the bug, teach the router a prediction
|
|
// that wrongly matches longer sibling URLs.
|
|
await act(
|
|
async () => {
|
|
const toggle = await browser.elementByCss(
|
|
'input[data-link-accordion="/myteam"]'
|
|
)
|
|
await toggle.click()
|
|
const link = await browser.elementByCss('a[href="/myteam"]')
|
|
await link.click()
|
|
},
|
|
{ includes: 'TEAM OVERVIEW' }
|
|
)
|
|
expect(await browser.elementByCss('h1').text()).toBe('TEAM OVERVIEW')
|
|
|
|
// Reveal + click /myteam/myproject — a sibling URL whose data is
|
|
// not already cached. Without the fix, the router uses the
|
|
// incorrect prediction taught by the previous step and skips the
|
|
// server fetch; "PROJECT PAGE" never arrives and `act` rejects.
|
|
// With the fix, no prediction matches /myteam/myproject and a
|
|
// real server fetch delivers the project page.
|
|
await act(
|
|
async () => {
|
|
const toggle = await browser.elementByCss(
|
|
'input[data-link-accordion="/myteam/myproject"]'
|
|
)
|
|
await toggle.click()
|
|
const link = await browser.elementByCss('a[href="/myteam/myproject"]')
|
|
await link.click()
|
|
},
|
|
{ includes: 'PROJECT PAGE' }
|
|
)
|
|
expect(await browser.elementByCss('h1').text()).toBe('PROJECT PAGE')
|
|
})
|
|
})
|