mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
After adding validation that fixing invalid segment configs (when DIO is enabled), it revealed that errors weren't being displayed in Turbopack, because `setHMRServerError` wasn't implemented. This refactors the implementation to use the existing server component transform when validating if the segments are valid. This has the benefit of highlighting precisely where the segment is coming, making it easier to remove. The test that was skipped in Turbopack is also re-enabled. <details> <summary>Screenshot</summary>  </details>
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import {
|
|
assertHasRedbox,
|
|
assertNoRedbox,
|
|
getRedboxCallStack,
|
|
getRedboxDescription,
|
|
hasErrorToast,
|
|
retry,
|
|
waitForAndOpenRuntimeError,
|
|
getRedboxSource,
|
|
} from 'next-test-utils'
|
|
import { sandbox } from 'development-sandbox'
|
|
import { outdent } from 'outdent'
|
|
|
|
describe('Dynamic IO Dev Errors', () => {
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
it('should show a red box error on the SSR render', async () => {
|
|
const browser = await next.browser('/error')
|
|
|
|
await retry(async () => {
|
|
expect(await hasErrorToast(browser)).toBe(true)
|
|
|
|
await waitForAndOpenRuntimeError(browser)
|
|
|
|
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
|
`"[ Server ] Error: Route "/error" used \`Math.random()\` outside of \`"use cache"\` and without explicitly calling \`await connection()\` beforehand. See more info here: https://nextjs.org/docs/messages/next-prerender-random"`
|
|
)
|
|
})
|
|
})
|
|
|
|
it('should show a red box error on client navigations', async () => {
|
|
const browser = await next.browser('/no-error')
|
|
|
|
expect(await hasErrorToast(browser)).toBe(false)
|
|
|
|
await browser.elementByCss("[href='/error']").click()
|
|
|
|
await retry(async () => {
|
|
expect(await hasErrorToast(browser)).toBe(true)
|
|
|
|
await waitForAndOpenRuntimeError(browser)
|
|
|
|
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
|
`"[ Server ] Error: Route "/error" used \`Math.random()\` outside of \`"use cache"\` and without explicitly calling \`await connection()\` beforehand. See more info here: https://nextjs.org/docs/messages/next-prerender-random"`
|
|
)
|
|
})
|
|
})
|
|
|
|
// NOTE: when update this snapshot, use `pnpm build` in packages/next to avoid next source code get mapped to source.
|
|
it('should display error when component accessed data without suspense boundary', async () => {
|
|
const browser = await next.browser('/no-accessed-data')
|
|
|
|
await retry(async () => {
|
|
expect(await hasErrorToast(browser)).toBe(true)
|
|
await waitForAndOpenRuntimeError(browser)
|
|
await assertHasRedbox(browser)
|
|
})
|
|
|
|
const description = await getRedboxDescription(browser)
|
|
const stack = await getRedboxCallStack(browser)
|
|
|
|
expect(description).toMatchInlineSnapshot(
|
|
`"[ Server ] Error: Route "/no-accessed-data": A component accessed data, headers, params, searchParams, or a short-lived cache without a Suspense boundary nor a "use cache" above it. We don't have the exact line number added to error messages yet but you can see which component in the stack below. See more info: https://nextjs.org/docs/messages/next-prerender-missing-suspense"`
|
|
)
|
|
// TODO: use snapshot testing for stack
|
|
// FIXME: avoid `next` code to be mapped to source code and filter them out even when sourcemap is enabled.
|
|
expect(stack).toContain('Page [Server]')
|
|
expect(stack).toContain('Root [Server]')
|
|
expect(stack).toContain('<anonymous> (2:1)')
|
|
})
|
|
|
|
it('should clear segment errors after correcting them', async () => {
|
|
const { cleanup, session, browser } = await sandbox(
|
|
next,
|
|
new Map([
|
|
[
|
|
'app/page.tsx',
|
|
outdent`
|
|
export const revalidate = 10
|
|
export default function Page() {
|
|
return (
|
|
<div>Hello World</div>
|
|
);
|
|
}
|
|
`,
|
|
],
|
|
])
|
|
)
|
|
|
|
await assertHasRedbox(browser)
|
|
const redbox = {
|
|
description: await getRedboxDescription(browser),
|
|
source: await getRedboxSource(browser),
|
|
}
|
|
|
|
expect(redbox.description).toMatchInlineSnapshot(`"Failed to compile"`)
|
|
expect(redbox.source).toContain(
|
|
'"revalidate" is not compatible with `nextConfig.experimental.dynamicIO`. Please remove it.'
|
|
)
|
|
|
|
await session.patch(
|
|
'app/page.tsx',
|
|
outdent`
|
|
export default function Page() {
|
|
return (
|
|
<div>Hello World</div>
|
|
);
|
|
}
|
|
`
|
|
)
|
|
|
|
await retry(async () => {
|
|
assertNoRedbox(browser)
|
|
})
|
|
|
|
await cleanup()
|
|
})
|
|
})
|