rsnext/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts
Wyatt Johnson c6320ed87a
Replace createNextDescribe with nextTestSetup (#64817)
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->

I took some time and [wrote a
codemod](https://gist.github.com/wyattjoh/0d4464427506cb02062a4729ca906b62)
that replaces the old usage of the `createNextDescribe` with the new
`nextTestSetup`. You'll likely have to turn on hiding of whitespace in
order to review, but this should primarily introduce no changes to the
test structure other than using the new mechanism now.

Closes NEXT-3178
2024-04-25 12:06:12 -06:00

312 lines
11 KiB
TypeScript

import { nextTestSetup } from 'e2e-utils'
import { getRedboxHeader, hasRedbox } from 'next-test-utils'
process.env.__TEST_SENTINEL = 'build'
describe('dynamic-data', () => {
const { next, isNextDev, skipped } = nextTestSetup({
files: __dirname + '/fixtures/main',
skipStart: true,
skipDeployment: true,
})
if (skipped) {
return
}
beforeAll(async () => {
await next.start()
// This will update the __TEST_SENTINEL value to "run"
await next.render('/setenv?value=run')
})
it('should render the dynamic apis dynamically when used in a top-level scope', async () => {
const $ = await next.render$(
'/top-level?foo=foosearch',
{},
{
headers: {
fooheader: 'foo header value',
cookie: 'foocookie=foo cookie value',
},
}
)
if (isNextDev) {
// in dev we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
} else if (process.env.__NEXT_EXPERIMENTAL_PPR) {
// in PPR we expect the shell to be rendered at build and the page to be rendered at runtime
expect($('#layout').text()).toBe('build')
expect($('#page').text()).toBe('run')
// we expect there to be a suspense boundary in fallback state
expect($('#boundary').html()).not.toBeNull()
} else {
// in static generation we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
}
expect($('#headers .fooheader').text()).toBe('foo header value')
expect($('#cookies .foocookie').text()).toBe('foo cookie value')
expect($('#searchparams .foo').text()).toBe('foosearch')
})
it('should render the dynamic apis dynamically when used in a top-level scope with force dynamic', async () => {
const $ = await next.render$(
'/force-dynamic?foo=foosearch',
{},
{
headers: {
fooheader: 'foo header value',
cookie: 'foocookie=foo cookie value',
},
}
)
if (isNextDev) {
// in dev we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
} else if (process.env.__NEXT_EXPERIMENTAL_PPR) {
// @TODO this should actually be build but there is a bug in how we do segment level dynamic in PPR at the moment
// see note in create-component-tree
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be a suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
} else {
// in static generation we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
}
expect($('#headers .fooheader').text()).toBe('foo header value')
expect($('#cookies .foocookie').text()).toBe('foo cookie value')
expect($('#searchparams .foo').text()).toBe('foosearch')
})
it('should render empty objects for dynamic APIs when rendering with force-static', async () => {
const $ = await next.render$(
'/force-static?foo=foosearch',
{},
{
headers: {
fooheader: 'foo header value',
cookie: 'foocookie=foo cookie value',
},
}
)
if (isNextDev) {
// in dev we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
} else if (process.env.__NEXT_EXPERIMENTAL_PPR) {
// in PPR we expect the shell to be rendered at build and the page to be rendered at runtime
expect($('#layout').text()).toBe('build')
expect($('#page').text()).toBe('build')
// we expect there to be a suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
} else {
// in static generation we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('build')
expect($('#page').text()).toBe('build')
// we expect there to be no suspense boundary in fallback state
expect($('#boundary').html()).toBeNull()
}
expect($('#headers .fooheader').html()).toBeNull()
expect($('#cookies .foocookie').html()).toBeNull()
expect($('#searchparams .foo').html()).toBeNull()
})
it('should track searchParams access as dynamic when the Page is a client component', async () => {
const $ = await next.render$(
'/client-page?foo=foosearch',
{},
{
headers: {
fooheader: 'foo header value',
cookie: 'foocookie=foo cookie value',
},
}
)
if (isNextDev) {
// in dev we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we don't assert the state of the fallback because it can depend on the timing
// of when streaming starts and how fast the client references resolve
} else if (process.env.__NEXT_EXPERIMENTAL_PPR) {
// in PPR we expect the shell to be rendered at build and the page to be rendered at runtime
expect($('#layout').text()).toBe('build')
expect($('#page').text()).toBe('run')
// we expect there to be a suspense boundary in fallback state
expect($('#boundary').html()).not.toBeNull()
} else {
// in static generation we expect the entire page to be rendered at runtime
expect($('#layout').text()).toBe('run')
expect($('#page').text()).toBe('run')
// we don't assert the state of the fallback because it can depend on the timing
// of when streaming starts and how fast the client references resolve
}
expect($('#searchparams .foo').text()).toBe('foosearch')
})
if (!isNextDev) {
it('should track dynamic apis when rendering app routes', async () => {
expect(next.cliOutput).toContain(
`Caught Error: Dynamic server usage: Route /routes/url couldn't be rendered statically because it accessed \`request.url\`.`
)
expect(next.cliOutput).toContain(
`Caught Error: Dynamic server usage: Route /routes/next-url couldn't be rendered statically because it accessed \`nextUrl.toString\`.`
)
})
}
})
describe('dynamic-data with dynamic = "error"', () => {
const { next, isNextDev, isNextDeploy, skipped } = nextTestSetup({
files: __dirname + '/fixtures/require-static',
skipStart: true,
})
if (skipped) {
return
}
if (isNextDeploy) {
it.skip('should not run in next deploy.', () => {})
return
}
if (isNextDev) {
beforeAll(async () => {
await next.start()
})
it('displays redbox when `dynamic = "error"` and dynamic data is read in dev', async () => {
let browser = await next.browser('/cookies?foo=foosearch')
try {
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(
'Error: Route /cookies with `dynamic = "error"` couldn\'t be rendered statically because it used `cookies`'
)
} finally {
await browser.close()
}
browser = await next.browser('/headers?foo=foosearch')
try {
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(
'Error: Route /headers with `dynamic = "error"` couldn\'t be rendered statically because it used `headers`'
)
} finally {
await browser.close()
}
browser = await next.browser('/search?foo=foosearch')
try {
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(
'Error: Route /search with `dynamic = "error"` couldn\'t be rendered statically because it used `searchParams`'
)
} finally {
await browser.close()
}
})
} else {
it('error when the build when `dynamic = "error"` and dynamic data is read', async () => {
try {
await next.start()
} catch (err) {
// We expect this to fail
}
// Error: Page with `dynamic = "error"` couldn't be rendered statically because it used `headers`
expect(next.cliOutput).toMatch(
'Error: Route /cookies with `dynamic = "error"` couldn\'t be rendered statically because it used `cookies`'
)
expect(next.cliOutput).toMatch(
'Error: Route /headers with `dynamic = "error"` couldn\'t be rendered statically because it used `headers`'
)
expect(next.cliOutput).toMatch(
'Error: Route /search with `dynamic = "error"` couldn\'t be rendered statically because it used `searchParams`.'
)
expect(next.cliOutput).toMatch(
'Error: Route /routes/form-data/error with `dynamic = "error"` couldn\'t be rendered statically because it accessed `request.formData`.'
)
expect(next.cliOutput).toMatch(
'Error: Route /routes/next-url/error with `dynamic = "error"` couldn\'t be rendered statically because it accessed `nextUrl.toString`.'
)
})
}
})
describe('dynamic-data inside cache scope', () => {
const { next, isNextDev, isNextDeploy, skipped } = nextTestSetup({
files: __dirname + '/fixtures/cache-scoped',
skipStart: true,
})
if (skipped) {
return
}
if (isNextDeploy) {
it.skip('should not run in next deploy..', () => {})
return
}
if (isNextDev) {
beforeAll(async () => {
await next.start()
})
it('displays redbox when accessing dynamic data inside a cache scope', async () => {
let browser = await next.browser('/cookies')
try {
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(
'Error: Route /cookies used "cookies" inside a function cached with "unstable_cache(...)".'
)
} finally {
await browser.close()
}
browser = await next.browser('/headers')
try {
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(
'Error: Route /headers used "headers" inside a function cached with "unstable_cache(...)".'
)
} finally {
await browser.close()
}
})
} else {
it('error when the build when accessing dynamic data inside a cache scope', async () => {
try {
await next.start()
} catch (err) {
// We expect this to fail
}
expect(next.cliOutput).toMatch(
'Error: Route /cookies used "cookies" inside a function cached with "unstable_cache(...)".'
)
expect(next.cliOutput).toMatch(
'Error: Route /headers used "headers" inside a function cached with "unstable_cache(...)".'
)
})
}
})