Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
431 lines
16 KiB
TypeScript
431 lines
16 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import { assertNoConsoleErrors } from 'next-test-utils'
|
|
|
|
describe('fallback-shells', () => {
|
|
const { next, isNextDev, isNextDeploy, isNextStart } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
describe('without IO', () => {
|
|
it('should start and not postpone the response', async () => {
|
|
const { browser, response } =
|
|
await next.browserWithResponse('/without-io/world')
|
|
|
|
expect(await browser.elementById('slug').text()).toBe('Hello /world')
|
|
const headers = response.headers()
|
|
|
|
// If we didn't use the fallback shell, then we didn't postpone the
|
|
// response, and therefore shouldn't have sent the postponed header.
|
|
expect(headers['x-nextjs-postponed']).not.toBe('1')
|
|
})
|
|
})
|
|
|
|
describe('with cached IO', () => {
|
|
describe('with generateStaticParams', () => {
|
|
describe('and the page wrapped in Suspense', () => {
|
|
describe('and the params accessed in the cached page', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
|
|
it('does not produce hydration errors when resuming a fallback shell containing a layout with unused params', async () => {
|
|
const browser = await next.browser(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/bar',
|
|
{ pushErrorAsConsoleLog: true }
|
|
)
|
|
|
|
// There should also be no hydration errors due to a buildtime date
|
|
// being replaced by a new runtime date.
|
|
await assertNoConsoleErrors(browser)
|
|
})
|
|
|
|
// TODO: To be implemented in NAR-136.
|
|
it.skip('includes a cached layout with unused params in the fallback shell', async () => {
|
|
const browser = await next.browser(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/bar'
|
|
)
|
|
|
|
const layout = await browser.elementById('layout').text()
|
|
|
|
// When prerendered, this should be restored from the RDC during the
|
|
// resume of the fallback shell, so it should be "buildtime". If the
|
|
// layout is unexpectedly a cache miss, then it will be "runtime".
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
})
|
|
|
|
// TODO: Activate for deploy tests once background revalidation for
|
|
// prerendered pages is not triggered anymore on the first visit.
|
|
if (!isNextDeploy) {
|
|
it('shares a cached parent layout between a prerendered route shell and the fallback shell', async () => {
|
|
// `/foo` was prerendered
|
|
const browser = await next.browser(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/foo'
|
|
)
|
|
|
|
const layoutDateRouteShell = await browser
|
|
.elementById('root-layout')
|
|
.text()
|
|
|
|
expect(layoutDateRouteShell).toInclude(
|
|
isNextDev ? 'runtime' : 'buildtime'
|
|
)
|
|
|
|
await browser.loadPage(
|
|
new URL(
|
|
// Use a unique slug so earlier tests don't upgrade this route.
|
|
`/with-cached-io/with-static-params/with-suspense/params-in-page/baz`,
|
|
next.url
|
|
).href
|
|
)
|
|
|
|
const layoutDateFallbackShell = await browser
|
|
.elementById('root-layout')
|
|
.text()
|
|
|
|
expect(layoutDateRouteShell).toInclude(
|
|
isNextDev ? 'runtime' : 'buildtime'
|
|
)
|
|
|
|
expect(layoutDateFallbackShell).toBe(layoutDateRouteShell)
|
|
})
|
|
|
|
// TODO: To be implemented in NAR-136.
|
|
it.skip('shares a cached layout with unused params between a prerendered route shell and the fallback shell', async () => {
|
|
// `/foo` was prerendered
|
|
const browser = await next.browser(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/foo'
|
|
)
|
|
|
|
const layoutDateRouteShell = await browser
|
|
.elementById('layout')
|
|
.text()
|
|
|
|
expect(layoutDateRouteShell).toInclude(
|
|
isNextDev ? 'runtime' : 'buildtime'
|
|
)
|
|
|
|
// `/bar` was not prerendered, and thus resumes the fallback shell.
|
|
await browser.loadPage(
|
|
new URL(
|
|
'/with-cached-io/with-static-params/with-suspense/params-in-page/bar',
|
|
next.url
|
|
).href
|
|
)
|
|
|
|
const layoutDateFallbackShell = await browser
|
|
.elementById('layout')
|
|
.text()
|
|
|
|
expect(layoutDateRouteShell).toInclude(
|
|
isNextDev ? 'runtime' : 'buildtime'
|
|
)
|
|
|
|
expect(layoutDateFallbackShell).toBe(layoutDateRouteShell)
|
|
})
|
|
}
|
|
})
|
|
|
|
describe('and the params accessed in cached non-page function', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/with-suspense/params-not-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('and params.then/catch/finally passed to a cached function', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/with-suspense/params-then-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('and the params transformed with an async function and then passed to a cached function', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/with-suspense/params-transformed/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('and the page not wrapped in Suspense', () => {
|
|
describe('and the params accessed in the cached page', () => {
|
|
it('does not resume a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/without-suspense/params-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude('runtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).not.toBe('1')
|
|
}
|
|
})
|
|
|
|
// TODO: Re-enable as deploy test when (potential) infra issue is
|
|
// resolved.
|
|
if (!isNextDeploy) {
|
|
it('does not render a fallback shell when using a params placeholder', async () => {
|
|
// This should trigger a blocking prerender of the route shell.
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/without-suspense/params-in-page/[slug]'
|
|
)
|
|
|
|
expect(response.status()).toBe(200)
|
|
|
|
// This should render the encoded param in the route shell, and not
|
|
// interpret the param as a fallback param, and subsequently try to
|
|
// render the fallback shell instead, which would fail because of the
|
|
// missing parent suspense boundary.
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /%5Bslug%5D')
|
|
expect(lastModified).toInclude('runtime')
|
|
})
|
|
}
|
|
})
|
|
|
|
describe('and the params accessed in a cached non-page function', () => {
|
|
it('does not resume a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/without-suspense/params-not-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude('runtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).not.toBe('1')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('and params.then/catch/finally passed to a cached function', () => {
|
|
it('does not resume a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/without-suspense/params-then-in-page/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude('runtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).not.toBe('1')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('and the params transformed with an async function and then passed to a cached function', () => {
|
|
it('does not resume a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/with-static-params/without-suspense/params-transformed/bar'
|
|
)
|
|
|
|
const lastModified = await browser
|
|
.elementById('last-modified')
|
|
.text()
|
|
expect(lastModified).toInclude('Page /bar')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude('runtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).not.toBe('1')
|
|
}
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('without generateStaticParams', () => {
|
|
describe('and the params accessed in the cached page', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/without-static-params/params-in-page/foo'
|
|
)
|
|
|
|
const lastModified = await browser.elementById('last-modified').text()
|
|
expect(lastModified).toInclude('Page /foo')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
|
|
// TODO: To be implemented in NAR-136.
|
|
it.skip('does not produce hydration errors when resuming a fallback shell containing a layout with unused params', async () => {
|
|
const browser = await next.browser(
|
|
'/with-cached-io/without-static-params/params-in-page/bar',
|
|
{ pushErrorAsConsoleLog: true }
|
|
)
|
|
|
|
// There should also be no hydration errors due to a buildtime date
|
|
// being replaced by a new runtime date.
|
|
await assertNoConsoleErrors(browser)
|
|
})
|
|
|
|
// TODO: To be implemented in NAR-136.
|
|
it.skip('includes a cached layout with unused params in the fallback shell', async () => {
|
|
const browser = await next.browser(
|
|
'/with-cached-io/without-static-params/params-in-page/bar'
|
|
)
|
|
|
|
const layout = await browser.elementById('layout').text()
|
|
|
|
// When prerendered, this should be restored from the RDC during the
|
|
// resume of the fallback shell, so it should be "buildtime". If the
|
|
// layout is unexpectedly a cache miss, then it will be "runtime".
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
})
|
|
})
|
|
|
|
describe('and the params accessed in cached non-page function', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/without-static-params/params-not-in-page/foo'
|
|
)
|
|
|
|
const lastModified = await browser.elementById('last-modified').text()
|
|
expect(lastModified).toInclude('Page /foo')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('and params.then/catch/finally passed to a cached function', () => {
|
|
it('resumes a postponed fallback shell', async () => {
|
|
const { browser, response } = await next.browserWithResponse(
|
|
'/with-cached-io/without-static-params/params-then-in-page/foo'
|
|
)
|
|
|
|
const lastModified = await browser.elementById('last-modified').text()
|
|
expect(lastModified).toInclude('Page /foo')
|
|
expect(lastModified).toInclude('runtime')
|
|
|
|
const layout = await browser.elementById('root-layout').text()
|
|
expect(layout).toInclude(isNextDev ? 'runtime' : 'buildtime')
|
|
|
|
const headers = response.headers()
|
|
|
|
if (isNextStart) {
|
|
expect(headers['x-nextjs-postponed']).toBe('1')
|
|
}
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
if (isNextStart) {
|
|
it('should not log a HANGING_PROMISE_REJECTION error', async () => {
|
|
expect(next.cliOutput).not.toContain('HANGING_PROMISE_REJECTION')
|
|
})
|
|
}
|
|
})
|