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
239 lines
8.5 KiB
TypeScript
239 lines
8.5 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import {
|
|
waitForRedbox,
|
|
assertNoConsoleErrors,
|
|
waitForNoRedbox,
|
|
getRedboxDescription,
|
|
getRedboxSource,
|
|
} from 'next-test-utils'
|
|
import stripAnsi from 'strip-ansi'
|
|
|
|
const getExpectedErrorMessage = (route: string) =>
|
|
`Route ${route} used \`searchParams\` inside "use cache". Accessing dynamic request data inside a cache scope is not supported. If you need some search params inside a cached function await \`searchParams\` outside of the cached function and pass only the required search params as arguments to the cached function. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache`
|
|
|
|
describe('use-cache-search-params', () => {
|
|
const { next, isNextDev, skipped } = nextTestSetup({
|
|
files: __dirname,
|
|
skipDeployment: true,
|
|
skipStart: process.env.NEXT_TEST_MODE !== 'dev',
|
|
})
|
|
|
|
if (skipped) {
|
|
return
|
|
}
|
|
|
|
if (isNextDev) {
|
|
let route: string
|
|
|
|
describe('when searchParams are used inside of "use cache"', () => {
|
|
beforeAll(() => {
|
|
route = '/search-params-used'
|
|
})
|
|
|
|
it('should show an error', async () => {
|
|
const outputIndex = next.cliOutput.length
|
|
const browser = await next.browser(`${route}?foo=1`)
|
|
|
|
await waitForRedbox(browser)
|
|
|
|
const errorDescription = await getRedboxDescription(browser)
|
|
const errorSource = await getRedboxSource(browser)
|
|
const expectedErrorMessage = getExpectedErrorMessage(route)
|
|
|
|
expect(errorDescription).toBe(expectedErrorMessage)
|
|
|
|
const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex))
|
|
|
|
expect(errorSource).toMatchInlineSnapshot(`
|
|
"app/search-params-used/page.tsx (8:17) @ Page
|
|
|
|
6 | searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
|
7 | }) {
|
|
> 8 | const param = (await searchParams).foo
|
|
| ^
|
|
9 |
|
|
10 | return <p>param: {param}</p>
|
|
11 | }"
|
|
`)
|
|
|
|
expect(cliOutput).toContain(`Error: ${expectedErrorMessage}
|
|
at Page (app/search-params-used/page.tsx:8:17)`)
|
|
})
|
|
})
|
|
|
|
describe('when searchParams are caught inside of "use cache"', () => {
|
|
beforeAll(() => {
|
|
route = '/search-params-caught'
|
|
})
|
|
|
|
it('should show an error', async () => {
|
|
const outputIndex = next.cliOutput.length
|
|
const browser = await next.browser(`${route}?foo=1`)
|
|
|
|
await waitForRedbox(browser)
|
|
|
|
const errorDescription = await getRedboxDescription(browser)
|
|
const errorSource = await getRedboxSource(browser)
|
|
const expectedErrorMessage = getExpectedErrorMessage(route)
|
|
|
|
expect(errorDescription).toBe(expectedErrorMessage)
|
|
|
|
const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex))
|
|
|
|
expect(errorSource).toMatchInlineSnapshot(`
|
|
"app/search-params-caught/page.tsx (11:5) @ Page
|
|
|
|
9 |
|
|
10 | try {
|
|
> 11 | param = (await searchParams).foo
|
|
| ^
|
|
12 | } catch {}
|
|
13 |
|
|
14 | return <p>param: {param}</p>"
|
|
`)
|
|
|
|
expect(cliOutput).toContain(`Error: ${expectedErrorMessage}
|
|
at Page (app/search-params-caught/page.tsx:11:5)`)
|
|
})
|
|
|
|
it('should also show an error after the second reload', async () => {
|
|
// There was an obscure bug that lead to the error not being triggered
|
|
// anymore starting with the third request. We test this scenario
|
|
// explicitly to ensure we won't regress.
|
|
const browser = await next.browser(`${route}?foo=1`)
|
|
await browser.refresh()
|
|
await browser.refresh()
|
|
|
|
await waitForRedbox(browser)
|
|
|
|
const errorDescription = await getRedboxDescription(browser)
|
|
|
|
expect(errorDescription).toBe(getExpectedErrorMessage(route))
|
|
})
|
|
})
|
|
|
|
describe('when searchParams are unused inside of "use cache"', () => {
|
|
beforeAll(() => {
|
|
route = '/search-params-unused'
|
|
})
|
|
|
|
it('should not show an error', async () => {
|
|
const outputIndex = next.cliOutput.length
|
|
const browser = await next.browser(`${route}?foo=1`)
|
|
|
|
await waitForNoRedbox(browser)
|
|
|
|
const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex))
|
|
|
|
expect(cliOutput).not.toContain(getExpectedErrorMessage(route))
|
|
})
|
|
})
|
|
|
|
it('should show an error when searchParams are used inside of a cached generateMetadata', async () => {
|
|
const browser = await next.browser(
|
|
'/search-params-used-generate-metadata?title=foo'
|
|
)
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E394",
|
|
"description": "Route /search-params-used-generate-metadata used \`searchParams\` inside "use cache". Accessing dynamic request data inside a cache scope is not supported. If you need some search params inside a cached function await \`searchParams\` outside of the cached function and pass only the required search params as arguments to the cached function. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": "app/search-params-used-generate-metadata/page.tsx (9:17) @ generateMetadata
|
|
> 9 | const title = (await searchParams).title
|
|
| ^",
|
|
"stack": [
|
|
"generateMetadata app/search-params-used-generate-metadata/page.tsx (9:17)",
|
|
],
|
|
}
|
|
`)
|
|
})
|
|
|
|
it('should show an error when searchParams are used inside of a cached generateViewport', async () => {
|
|
const browser = await next.browser(
|
|
'/search-params-used-generate-viewport?color=red'
|
|
)
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E394",
|
|
"description": "Route /search-params-used-generate-viewport used \`searchParams\` inside "use cache". Accessing dynamic request data inside a cache scope is not supported. If you need some search params inside a cached function await \`searchParams\` outside of the cached function and pass only the required search params as arguments to the cached function. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": "app/search-params-used-generate-viewport/page.tsx (9:17) @ generateViewport
|
|
> 9 | const color = (await searchParams).color
|
|
| ^",
|
|
"stack": [
|
|
"generateViewport app/search-params-used-generate-viewport/page.tsx (9:17)",
|
|
],
|
|
}
|
|
`)
|
|
})
|
|
} else {
|
|
afterEach(async () => {
|
|
await next.stop()
|
|
})
|
|
|
|
it('should fail the build with errors', async () => {
|
|
const { cliOutput } = await next.build()
|
|
|
|
expect(cliOutput).toInclude(
|
|
getExpectedErrorMessage('/search-params-used')
|
|
)
|
|
|
|
expect(cliOutput).toInclude(
|
|
getExpectedErrorMessage('/search-params-caught')
|
|
)
|
|
|
|
expect(cliOutput).not.toInclude(
|
|
getExpectedErrorMessage('/search-params-unused')
|
|
)
|
|
|
|
expect(cliOutput).toInclude(
|
|
'Error occurred prerendering page "/search-params-used"'
|
|
)
|
|
|
|
expect(cliOutput).toInclude(
|
|
'Error occurred prerendering page "/search-params-caught"'
|
|
)
|
|
|
|
expect(cliOutput).not.toInclude(
|
|
'Error occurred prerendering page "/search-params-unused"'
|
|
)
|
|
})
|
|
|
|
it('should resume a cached page that does not access search params without hydration errors', async () => {
|
|
await next.build({
|
|
args: ['--debug-build-paths', 'app/search-params-unused/page.tsx'],
|
|
})
|
|
|
|
await next.start({ skipBuild: true })
|
|
|
|
let browser = await next.browser('/search-params-unused', {
|
|
disableJavaScript: true,
|
|
})
|
|
|
|
const prerenderedPageDate = await browser.elementById('page-date').text()
|
|
|
|
await browser.close()
|
|
|
|
browser = await next.browser('/search-params-unused', {
|
|
pushErrorAsConsoleLog: true,
|
|
})
|
|
|
|
// After hydration, the resumed page date should be the prerendered date.
|
|
// Note: When cacheComponents is not enabled, the page is not actually
|
|
// prerendered, but because the page is cached on the first page load, the
|
|
// date should still be the same for the second page load.
|
|
expect(await browser.elementById('page-date').text()).toBe(
|
|
prerenderedPageDate
|
|
)
|
|
|
|
// There should also be no hydration errors due to a buildtime date being
|
|
// replaced by a new runtime date.
|
|
await assertNoConsoleErrors(browser)
|
|
})
|
|
}
|
|
})
|