rsnext/test/e2e/app-dir/app-static/app-static.test.ts

1613 lines
57 KiB
TypeScript
Raw Normal View History

import globOrig from 'glob'
import cheerio from 'cheerio'
import { promisify } from 'util'
import { join } from 'path'
import { createNextDescribe } from 'e2e-utils'
import { check, fetchViaHTTP, normalizeRegEx, waitFor } from 'next-test-utils'
import stripAnsi from 'strip-ansi'
const glob = promisify(globOrig)
createNextDescribe(
'app-dir static/dynamic handling',
{
files: __dirname,
env: {
NEXT_DEBUG_BUILD: '1',
...(process.env.CUSTOM_CACHE_HANDLER
? {
CUSTOM_CACHE_HANDLER: process.env.CUSTOM_CACHE_HANDLER,
}
: {}),
},
},
({ next, isNextDev: isDev, isNextStart, isNextDeploy }) => {
let prerenderManifest
beforeAll(async () => {
if (isNextStart) {
prerenderManifest = JSON.parse(
await next.readFile('.next/prerender-manifest.json')
)
}
})
if (isDev) {
it('should error correctly for invalid params from generateStaticParams', async () => {
await next.patchFile(
'app/invalid/[slug]/page.js',
`
export function generateStaticParams() {
return [{slug: { invalid: true }}]
}
`
)
const html = await next.render('/invalid/first')
await next.deleteFile('app/invalid/[slug]/page.js')
expect(html).toContain(
'A required parameter (slug) was not provided as a string received object'
)
})
}
if (!process.env.CUSTOM_CACHE_HANDLER) {
it('should revalidate correctly with config and fetch revalidate', async () => {
const initial$ = await next.render$(
'/variable-config-revalidate/revalidate-3'
)
const initialDate = initial$('#date').text()
const initialData = initial$('#data').text()
expect(initialDate).toBeTruthy()
expect(initialData).toBeTruthy()
let revalidatedDate
let revalidatedData
// wait for a fresh revalidation
await check(async () => {
const $ = await next.render$(
'/variable-config-revalidate/revalidate-3'
)
revalidatedDate = $('#date').text()
revalidatedData = $('#data').text()
expect(revalidatedData).not.toBe(initialDate)
expect(revalidatedDate).not.toBe(initialData)
return 'success'
}, 'success')
// the date should revalidate first after 3 seconds
// while the fetch data stays in place for 15 seconds
await check(async () => {
const $ = await next.render$(
'/variable-config-revalidate/revalidate-3'
)
expect($('#date').text()).not.toBe(revalidatedDate)
expect($('#data').text()).toBe(revalidatedData)
return 'success'
}, 'success')
})
}
it('should include statusCode in cache', async () => {
const $ = await next.render$('/variable-revalidate/status-code')
const origData = JSON.parse($('#page-data').text())
expect(origData.status).toBe(404)
await check(async () => {
const new$ = await next.render$('/variable-revalidate/status-code')
const newData = JSON.parse(new$('#page-data').text())
expect(newData.status).toBe(origData.status)
expect(newData.text).not.toBe(origData.text)
return 'success'
}, 'success')
})
if (isNextStart) {
it('should output HTML/RSC files for static paths', async () => {
const files = (
await glob('**/*', {
cwd: join(next.testDir, '.next/server/app'),
})
)
.filter((file) => file.match(/.*\.(js|html|rsc)$/))
.map((file) => {
return file.replace(
/partial-gen-params-no-additional-([\w]{1,})\/([\w]{1,})\/([\d]{1,})/,
'partial-gen-params-no-additional-$1/$2/RAND'
)
})
expect(files).toEqual([
'(new)/custom/page.js',
'blog/[author]/[slug]/page.js',
'blog/[author]/page.js',
'blog/seb.html',
'blog/seb.rsc',
'blog/seb/second-post.html',
'blog/seb/second-post.rsc',
'blog/styfle.html',
'blog/styfle.rsc',
'blog/styfle/first-post.html',
'blog/styfle/first-post.rsc',
'blog/styfle/second-post.html',
'blog/styfle/second-post.rsc',
'blog/tim.html',
'blog/tim.rsc',
'blog/tim/first-post.html',
'blog/tim/first-post.rsc',
'dynamic-error/[id]/page.js',
'dynamic-no-gen-params-ssr/[slug]/page.js',
'dynamic-no-gen-params/[slug]/page.js',
'force-dynamic-no-prerender/[id]/page.js',
'force-static/[slug]/page.js',
'force-static/first.html',
'force-static/first.rsc',
'force-static/page.js',
'force-static/second.html',
'force-static/second.rsc',
'gen-params-dynamic-revalidate/[slug]/page.js',
'gen-params-dynamic-revalidate/one.html',
'gen-params-dynamic-revalidate/one.rsc',
'gen-params-dynamic/[slug]/page.js',
'hooks/use-pathname/[slug]/page.js',
'hooks/use-pathname/slug.html',
'hooks/use-pathname/slug.rsc',
'hooks/use-search-params.html',
'hooks/use-search-params.rsc',
'hooks/use-search-params/force-static.html',
'hooks/use-search-params/force-static.rsc',
'hooks/use-search-params/force-static/page.js',
'hooks/use-search-params/page.js',
'hooks/use-search-params/with-suspense.html',
'hooks/use-search-params/with-suspense.rsc',
'hooks/use-search-params/with-suspense/page.js',
'partial-gen-params-no-additional-lang/[lang]/[slug]/page.js',
'partial-gen-params-no-additional-lang/en/RAND.html',
'partial-gen-params-no-additional-lang/en/RAND.rsc',
'partial-gen-params-no-additional-lang/en/first.html',
'partial-gen-params-no-additional-lang/en/first.rsc',
'partial-gen-params-no-additional-lang/en/second.html',
'partial-gen-params-no-additional-lang/en/second.rsc',
'partial-gen-params-no-additional-lang/fr/RAND.html',
'partial-gen-params-no-additional-lang/fr/RAND.rsc',
'partial-gen-params-no-additional-lang/fr/first.html',
'partial-gen-params-no-additional-lang/fr/first.rsc',
'partial-gen-params-no-additional-lang/fr/second.html',
'partial-gen-params-no-additional-lang/fr/second.rsc',
'partial-gen-params-no-additional-slug/[lang]/[slug]/page.js',
'partial-gen-params-no-additional-slug/en/RAND.html',
'partial-gen-params-no-additional-slug/en/RAND.rsc',
'partial-gen-params-no-additional-slug/en/first.html',
'partial-gen-params-no-additional-slug/en/first.rsc',
'partial-gen-params-no-additional-slug/en/second.html',
'partial-gen-params-no-additional-slug/en/second.rsc',
'partial-gen-params-no-additional-slug/fr/RAND.html',
'partial-gen-params-no-additional-slug/fr/RAND.rsc',
'partial-gen-params-no-additional-slug/fr/first.html',
'partial-gen-params-no-additional-slug/fr/first.rsc',
'partial-gen-params-no-additional-slug/fr/second.html',
'partial-gen-params-no-additional-slug/fr/second.rsc',
'partial-gen-params/[lang]/[slug]/page.js',
'ssg-preview.html',
'ssg-preview.rsc',
'ssg-preview/[[...route]]/page.js',
'ssg-preview/test-2.html',
'ssg-preview/test-2.rsc',
'ssg-preview/test.html',
'ssg-preview/test.rsc',
'ssr-auto/cache-no-store/page.js',
'ssr-auto/fetch-revalidate-zero/page.js',
'ssr-forced/page.js',
'static-to-dynamic-error-forced/[id]/page.js',
'static-to-dynamic-error/[id]/page.js',
'variable-config-revalidate/revalidate-3.html',
'variable-config-revalidate/revalidate-3.rsc',
'variable-config-revalidate/revalidate-3/page.js',
'variable-revalidate-edge/encoding/page.js',
'variable-revalidate-edge/no-store/page.js',
'variable-revalidate-edge/post-method-request/page.js',
'variable-revalidate-edge/post-method/page.js',
'variable-revalidate-edge/revalidate-3/page.js',
'variable-revalidate/authorization.html',
'variable-revalidate/authorization.rsc',
'variable-revalidate/authorization/page.js',
'variable-revalidate/cookie.html',
'variable-revalidate/cookie.rsc',
'variable-revalidate/cookie/page.js',
'variable-revalidate/encoding.html',
'variable-revalidate/encoding.rsc',
'variable-revalidate/encoding/page.js',
'variable-revalidate/no-store/page.js',
'variable-revalidate/post-method-request/page.js',
'variable-revalidate/post-method.html',
'variable-revalidate/post-method.rsc',
'variable-revalidate/post-method/page.js',
'variable-revalidate/revalidate-3.html',
'variable-revalidate/revalidate-3.rsc',
'variable-revalidate/revalidate-3/page.js',
'variable-revalidate/status-code.html',
'variable-revalidate/status-code.rsc',
'variable-revalidate/status-code/page.js',
])
})
it('should have correct prerender-manifest entries', async () => {
const curManifest = JSON.parse(JSON.stringify(prerenderManifest))
for (const key of Object.keys(curManifest.dynamicRoutes)) {
const item = curManifest.dynamicRoutes[key]
if (item.dataRouteRegex) {
item.dataRouteRegex = normalizeRegEx(item.dataRouteRegex)
}
if (item.routeRegex) {
item.routeRegex = normalizeRegEx(item.routeRegex)
}
}
for (const key of Object.keys(curManifest.routes)) {
const newKey = key.replace(
/partial-gen-params-no-additional-([\w]{1,})\/([\w]{1,})\/([\d]{1,})/,
'partial-gen-params-no-additional-$1/$2/RAND'
)
if (newKey !== key) {
const route = curManifest.routes[key]
delete curManifest.routes[key]
curManifest.routes[newKey] = {
...route,
dataRoute: `${newKey}.rsc`,
}
}
}
expect(curManifest.version).toBe(4)
expect(curManifest.routes).toEqual({
'/blog/tim': {
initialRevalidateSeconds: 10,
srcRoute: '/blog/[author]',
dataRoute: '/blog/tim.rsc',
},
'/blog/seb': {
initialRevalidateSeconds: 10,
srcRoute: '/blog/[author]',
dataRoute: '/blog/seb.rsc',
},
'/blog/styfle': {
initialRevalidateSeconds: 10,
srcRoute: '/blog/[author]',
dataRoute: '/blog/styfle.rsc',
},
'/blog/tim/first-post': {
initialRevalidateSeconds: false,
srcRoute: '/blog/[author]/[slug]',
dataRoute: '/blog/tim/first-post.rsc',
},
'/blog/seb/second-post': {
initialRevalidateSeconds: false,
srcRoute: '/blog/[author]/[slug]',
dataRoute: '/blog/seb/second-post.rsc',
},
'/blog/styfle/first-post': {
initialRevalidateSeconds: false,
srcRoute: '/blog/[author]/[slug]',
dataRoute: '/blog/styfle/first-post.rsc',
},
'/blog/styfle/second-post': {
initialRevalidateSeconds: false,
srcRoute: '/blog/[author]/[slug]',
dataRoute: '/blog/styfle/second-post.rsc',
},
'/hooks/use-pathname/slug': {
dataRoute: '/hooks/use-pathname/slug.rsc',
initialRevalidateSeconds: false,
srcRoute: '/hooks/use-pathname/[slug]',
},
'/hooks/use-search-params': {
dataRoute: '/hooks/use-search-params.rsc',
initialRevalidateSeconds: false,
srcRoute: '/hooks/use-search-params',
},
'/hooks/use-search-params/force-static': {
dataRoute: '/hooks/use-search-params/force-static.rsc',
initialRevalidateSeconds: false,
srcRoute: '/hooks/use-search-params/force-static',
},
'/hooks/use-search-params/with-suspense': {
dataRoute: '/hooks/use-search-params/with-suspense.rsc',
initialRevalidateSeconds: false,
srcRoute: '/hooks/use-search-params/with-suspense',
},
'/partial-gen-params-no-additional-lang/en/RAND': {
dataRoute: '/partial-gen-params-no-additional-lang/en/RAND.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-lang/en/first': {
dataRoute: '/partial-gen-params-no-additional-lang/en/first.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-lang/en/second': {
dataRoute: '/partial-gen-params-no-additional-lang/en/second.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-lang/fr/RAND': {
dataRoute: '/partial-gen-params-no-additional-lang/fr/RAND.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-lang/fr/first': {
dataRoute: '/partial-gen-params-no-additional-lang/fr/first.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-lang/fr/second': {
dataRoute: '/partial-gen-params-no-additional-lang/fr/second.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-lang/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/en/RAND': {
dataRoute: '/partial-gen-params-no-additional-slug/en/RAND.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/en/first': {
dataRoute: '/partial-gen-params-no-additional-slug/en/first.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/en/second': {
dataRoute: '/partial-gen-params-no-additional-slug/en/second.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/fr/RAND': {
dataRoute: '/partial-gen-params-no-additional-slug/fr/RAND.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/fr/first': {
dataRoute: '/partial-gen-params-no-additional-slug/fr/first.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/partial-gen-params-no-additional-slug/fr/second': {
dataRoute: '/partial-gen-params-no-additional-slug/fr/second.rsc',
initialRevalidateSeconds: false,
srcRoute: '/partial-gen-params-no-additional-slug/[lang]/[slug]',
},
'/force-static/first': {
dataRoute: '/force-static/first.rsc',
initialRevalidateSeconds: false,
srcRoute: '/force-static/[slug]',
},
'/force-static/second': {
dataRoute: '/force-static/second.rsc',
initialRevalidateSeconds: false,
srcRoute: '/force-static/[slug]',
},
'/gen-params-dynamic-revalidate/one': {
dataRoute: '/gen-params-dynamic-revalidate/one.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/gen-params-dynamic-revalidate/[slug]',
},
'/ssg-preview': {
dataRoute: '/ssg-preview.rsc',
initialRevalidateSeconds: false,
srcRoute: '/ssg-preview/[[...route]]',
},
'/ssg-preview/test': {
dataRoute: '/ssg-preview/test.rsc',
initialRevalidateSeconds: false,
srcRoute: '/ssg-preview/[[...route]]',
},
'/ssg-preview/test-2': {
dataRoute: '/ssg-preview/test-2.rsc',
initialRevalidateSeconds: false,
srcRoute: '/ssg-preview/[[...route]]',
},
'/variable-config-revalidate/revalidate-3': {
dataRoute: '/variable-config-revalidate/revalidate-3.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/variable-config-revalidate/revalidate-3',
},
'/variable-revalidate/authorization': {
dataRoute: '/variable-revalidate/authorization.rsc',
initialRevalidateSeconds: 10,
srcRoute: '/variable-revalidate/authorization',
},
'/variable-revalidate/cookie': {
dataRoute: '/variable-revalidate/cookie.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/variable-revalidate/cookie',
},
'/variable-revalidate/encoding': {
dataRoute: '/variable-revalidate/encoding.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/variable-revalidate/encoding',
},
'/variable-revalidate/post-method': {
dataRoute: '/variable-revalidate/post-method.rsc',
initialRevalidateSeconds: 10,
srcRoute: '/variable-revalidate/post-method',
},
'/variable-revalidate/revalidate-3': {
dataRoute: '/variable-revalidate/revalidate-3.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/variable-revalidate/revalidate-3',
},
'/variable-revalidate/status-code': {
dataRoute: '/variable-revalidate/status-code.rsc',
initialRevalidateSeconds: 3,
srcRoute: '/variable-revalidate/status-code',
},
})
expect(curManifest.dynamicRoutes).toEqual({
'/blog/[author]/[slug]': {
routeRegex: normalizeRegEx('^/blog/([^/]+?)/([^/]+?)(?:/)?$'),
dataRoute: '/blog/[author]/[slug].rsc',
fallback: null,
dataRouteRegex: normalizeRegEx('^/blog/([^/]+?)/([^/]+?)\\.rsc$'),
},
'/blog/[author]': {
dataRoute: '/blog/[author].rsc',
dataRouteRegex: normalizeRegEx('^\\/blog\\/([^\\/]+?)\\.rsc$'),
fallback: false,
routeRegex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'),
},
'/dynamic-error/[id]': {
dataRoute: '/dynamic-error/[id].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/dynamic\\-error\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/dynamic\\-error\\/([^\\/]+?)(?:\\/)?$'
),
},
'/gen-params-dynamic-revalidate/[slug]': {
dataRoute: '/gen-params-dynamic-revalidate/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/gen\\-params\\-dynamic\\-revalidate\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/gen\\-params\\-dynamic\\-revalidate\\/([^\\/]+?)(?:\\/)?$'
),
},
'/hooks/use-pathname/[slug]': {
dataRoute: '/hooks/use-pathname/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/hooks\\/use\\-pathname\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/hooks\\/use\\-pathname\\/([^\\/]+?)(?:\\/)?$'
),
},
'/partial-gen-params-no-additional-lang/[lang]/[slug]': {
dataRoute:
'/partial-gen-params-no-additional-lang/[lang]/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\-no\\-additional\\-lang\\/([^\\/]+?)\\/([^\\/]+?)\\.rsc$'
),
fallback: false,
routeRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\-no\\-additional\\-lang\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'
),
},
'/partial-gen-params-no-additional-slug/[lang]/[slug]': {
dataRoute:
'/partial-gen-params-no-additional-slug/[lang]/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\-no\\-additional\\-slug\\/([^\\/]+?)\\/([^\\/]+?)\\.rsc$'
),
fallback: false,
routeRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\-no\\-additional\\-slug\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'
),
},
'/partial-gen-params/[lang]/[slug]': {
dataRoute: '/partial-gen-params/[lang]/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\/([^\\/]+?)\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/partial\\-gen\\-params\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$'
),
},
'/force-static/[slug]': {
dataRoute: '/force-static/[slug].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/force\\-static\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/force\\-static\\/([^\\/]+?)(?:\\/)?$'
),
},
'/ssg-preview/[[...route]]': {
dataRoute: '/ssg-preview/[[...route]].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/ssg\\-preview(?:\\/(.+?))?\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/ssg\\-preview(?:\\/(.+?))?(?:\\/)?$'
),
},
'/static-to-dynamic-error-forced/[id]': {
dataRoute: '/static-to-dynamic-error-forced/[id].rsc',
dataRouteRegex: normalizeRegEx(
'^\\/static\\-to\\-dynamic\\-error\\-forced\\/([^\\/]+?)\\.rsc$'
),
fallback: null,
routeRegex: normalizeRegEx(
'^\\/static\\-to\\-dynamic\\-error\\-forced\\/([^\\/]+?)(?:\\/)?$'
),
},
})
})
it('should output debug info for static bailouts', async () => {
const cleanedOutput = stripAnsi(next.cliOutput)
expect(cleanedOutput).toContain(
'Static generation failed due to dynamic usage on /force-static, reason: headers'
)
expect(cleanedOutput).toContain(
'Static generation failed due to dynamic usage on /ssr-auto/cache-no-store, reason: no-store fetch'
)
})
}
if (isDev) {
it('should bypass fetch cache with cache-control: no-cache', async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/revalidate-3'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/revalidate-3',
undefined,
{
headers: {
'cache-control': 'no-cache',
},
}
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).not.toBe(layoutData)
expect($2('#page-data').text()).not.toBe(pageData)
})
} else {
it('should not error with dynamic server usage with force-static', async () => {
const res = await next.fetch(
'/static-to-dynamic-error-forced/static-bailout-1'
)
const outputIndex = next.cliOutput.length
const html = await res.text()
expect(res.status).toBe(200)
expect(html).toContain('/static-to-dynamic-error-forced')
expect(html).toMatch(/id:.*?static-bailout-1/)
if (isNextStart) {
expect(stripAnsi(next.cliOutput).substring(outputIndex)).not.toMatch(
/Page changed from static to dynamic at runtime \/static-to-dynamic-error-forced\/static-bailout-1, reason: cookies/
)
}
})
it('should properly error when dynamic = "error" page uses dynamic', async () => {
const res = await next.fetch('/dynamic-error/static-bailout-1')
const outputIndex = next.cliOutput.length
expect(res.status).toBe(500)
if (isNextStart) {
expect(stripAnsi(next.cliOutput).substring(outputIndex)).not.toMatch(
/Page with dynamic = "error" encountered dynamic data method on \/dynamic-error\/static-bailout-1/
)
}
})
}
it('should handle partial-gen-params with default dynamicParams correctly', async () => {
const res = await next.fetch('/partial-gen-params/en/first')
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const params = JSON.parse($('#params').text())
expect(params).toEqual({ lang: 'en', slug: 'first' })
})
it('should handle partial-gen-params with layout dynamicParams = false correctly', async () => {
for (const { path, status, params } of [
// these checks don't work with custom memory only
// cache handler
...(process.env.CUSTOM_CACHE_HANDLER
? []
: [
{
path: '/partial-gen-params-no-additional-lang/en/first',
status: 200,
params: { lang: 'en', slug: 'first' },
},
]),
{
path: '/partial-gen-params-no-additional-lang/de/first',
status: 404,
params: {},
},
{
path: '/partial-gen-params-no-additional-lang/en/non-existent',
status: 404,
params: {},
},
]) {
const res = await next.fetch(path)
expect(res.status).toBe(status)
const html = await res.text()
const $ = cheerio.load(html)
const curParams = JSON.parse($('#params').text() || '{}')
expect(curParams).toEqual(params)
}
})
it('should handle partial-gen-params with page dynamicParams = false correctly', async () => {
for (const { path, status, params } of [
// these checks don't work with custom memory only
// cache handler
...(process.env.CUSTOM_CACHE_HANDLER
? []
: [
{
path: '/partial-gen-params-no-additional-slug/en/first',
status: 200,
params: { lang: 'en', slug: 'first' },
},
]),
{
path: '/partial-gen-params-no-additional-slug/de/first',
status: 404,
params: {},
},
{
path: '/partial-gen-params-no-additional-slug/en/non-existent',
status: 404,
params: {},
},
]) {
const res = await next.fetch(path)
expect(res.status).toBe(status)
const html = await res.text()
const $ = cheerio.load(html)
const curParams = JSON.parse($('#params').text() || '{}')
expect(curParams).toEqual(params)
}
})
// fetch cache in generateStaticParams needs fs for persistence
// so doesn't behave as expected with custom in memory only
// cache handler
if (!process.env.CUSTOM_CACHE_HANDLER) {
it('should honor fetch cache in generateStaticParams', async () => {
const initialRes = await next.fetch(
`/partial-gen-params-no-additional-lang/en/first`
)
expect(initialRes.status).toBe(200)
// we can't read prerender-manifest from deployment
if (isNextDeploy) return
let langFetchSlug
let slugFetchSlug
if (isDev) {
await check(() => {
const matches = stripAnsi(next.cliOutput).match(
/partial-gen-params fetch ([\d]{1,})/
)
if (matches[1]) {
langFetchSlug = matches[1]
slugFetchSlug = langFetchSlug
}
return langFetchSlug ? 'success' : next.cliOutput
}, 'success')
} else {
// the fetch cache can potentially be a miss since
// the generateStaticParams are executed parallel
// in separate workers so parse value from
// prerender-manifest
const routes = Object.keys(prerenderManifest.routes)
for (const route of routes) {
const langSlug = route.match(
/partial-gen-params-no-additional-lang\/en\/([\d]{1,})/
)?.[1]
if (langSlug) {
langFetchSlug = langSlug
}
const slugSlug = route.match(
/partial-gen-params-no-additional-slug\/en\/([\d]{1,})/
)?.[1]
if (slugSlug) {
slugFetchSlug = slugSlug
}
}
}
require('console').log({ langFetchSlug, slugFetchSlug })
for (const { pathname, slug } of [
{
pathname: '/partial-gen-params-no-additional-lang/en',
slug: langFetchSlug,
},
{
pathname: '/partial-gen-params-no-additional-slug/en',
slug: slugFetchSlug,
},
]) {
const res = await next.fetch(`${pathname}/${slug}`)
expect(res.status).toBe(200)
expect(
JSON.parse(
cheerio
.load(await res.text())('#params')
.text()
)
).toEqual({ lang: 'en', slug })
}
})
}
it('should honor fetch cache correctly', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/revalidate-3'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/revalidate-3'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('should honor fetch cache correctly (edge)', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/revalidate-3'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
// the test cache handler is simple and doesn't share
// state across workers so not guaranteed to have cache hit
if (!(isNextDeploy && process.env.CUSTOM_CACHE_HANDLER)) {
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/revalidate-3'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
}
return 'success'
}, 'success')
})
it('should cache correctly with authorization header and revalidate', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/authorization'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/authorization'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('should not cache correctly with POST method request init', async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/post-method-request'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const pageData2 = $('#page-data2').text()
for (let i = 0; i < 3; i++) {
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/post-method-request'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#page-data2').text()).not.toBe(pageData2)
}
})
it('should cache correctly with post method and revalidate', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/post-method'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const dataBody1 = $('#data-body1').text()
const dataBody2 = $('#data-body2').text()
const dataBody3 = $('#data-body3').text()
const dataBody4 = $('#data-body4').text()
expect(dataBody1).not.toBe(dataBody2)
expect(dataBody2).not.toBe(dataBody3)
expect(dataBody3).not.toBe(dataBody4)
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/post-method'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
expect($2('#data-body1').text()).toBe(dataBody1)
expect($2('#data-body2').text()).toBe(dataBody2)
expect($2('#data-body3').text()).toBe(dataBody3)
return 'success'
}, 'success')
})
it('should cache correctly with post method and revalidate edge', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/post-method'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const dataBody1 = $('#data-body1').text()
const dataBody2 = $('#data-body2').text()
const dataBody3 = $('#data-body3').text()
const dataBody4 = $('#data-body4').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/post-method'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
expect($2('#data-body1').text()).toBe(dataBody1)
expect($2('#data-body2').text()).toBe(dataBody2)
expect($2('#data-body3').text()).toBe(dataBody3)
expect($2('#data-body4').text()).toBe(dataBody4)
return 'success'
}, 'success')
})
it('should cache correctly with POST method and revalidate', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/post-method'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/post-method'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('should cache correctly with cookie header and revalidate', async () => {
await check(async () => {
const res = await fetchViaHTTP(next.url, '/variable-revalidate/cookie')
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
const res2 = await fetchViaHTTP(next.url, '/variable-revalidate/cookie')
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('should cache correctly with utf8 encoding', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate/encoding'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
expect(JSON.parse(pageData).jp).toBe(
'超鬼畜激辛ボム兵スピンジャンプ Bomb Spin Jump'
)
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate/encoding'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('should cache correctly with utf8 encoding edge', async () => {
await check(async () => {
const res = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/encoding'
)
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
const layoutData = $('#layout-data').text()
const pageData = $('#page-data').text()
expect(JSON.parse(pageData).jp).toBe(
'超鬼畜激辛ボム兵スピンジャンプ Bomb Spin Jump'
)
const res2 = await fetchViaHTTP(
next.url,
'/variable-revalidate-edge/encoding'
)
expect(res2.status).toBe(200)
const html2 = await res2.text()
const $2 = cheerio.load(html2)
expect($2('#layout-data').text()).toBe(layoutData)
expect($2('#page-data').text()).toBe(pageData)
return 'success'
}, 'success')
})
it('Should not throw Dynamic Server Usage error when using generateStaticParams with previewData', async () => {
const browserOnIndexPage = await next.browser('/ssg-preview')
const content = await browserOnIndexPage
.elementByCss('#preview-data')
.text()
expect(content).toContain('previewData')
})
it('should force SSR correctly for headers usage', async () => {
const res = await next.fetch('/force-static', {
headers: {
Cookie: 'myCookie=cookieValue',
another: 'header',
},
})
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect(JSON.parse($('#headers').text())).toIncludeAllMembers([
'cookie',
'another',
])
expect(JSON.parse($('#cookies').text())).toEqual([
{
name: 'myCookie',
value: 'cookieValue',
},
])
const firstTime = $('#now').text()
if (!(global as any).isNextDev) {
const res2 = await next.fetch('/force-static')
expect(res2.status).toBe(200)
const $2 = cheerio.load(await res2.text())
expect(firstTime).not.toBe($2('#now').text())
}
})
it('should not error with generateStaticParams and dynamic data', async () => {
const res = await next.fetch('/gen-params-dynamic/one')
const html = await res.text()
expect(res.status).toBe(200)
expect(html).toContain('gen-params-dynamic/[slug]')
expect(html).toContain('one')
const data = cheerio.load(html)('#data').text()
for (let i = 0; i < 5; i++) {
const res2 = await next.fetch('/gen-params-dynamic/one')
expect(res2.status).toBe(200)
expect(
cheerio
.load(await res2.text())('#data')
.text()
).not.toBe(data)
}
})
it('should not error with generateStaticParams and authed data on revalidate', async () => {
const res = await next.fetch('/gen-params-dynamic-revalidate/one')
const html = await res.text()
expect(res.status).toBe(200)
expect(html).toContain('gen-params-dynamic/[slug]')
expect(html).toContain('one')
const initData = cheerio.load(html)('#data').text()
await check(async () => {
const res2 = await next.fetch('/gen-params-dynamic-revalidate/one')
expect(res2.status).toBe(200)
const $ = cheerio.load(await res2.text())
expect($('#data').text()).toBeTruthy()
expect($('#data').text()).not.toBe(initData)
return 'success'
}, 'success')
})
it('should honor dynamic = "force-static" correctly', async () => {
const res = await next.fetch('/force-static/first')
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect(JSON.parse($('#params').text())).toEqual({ slug: 'first' })
expect(JSON.parse($('#headers').text())).toEqual([])
expect(JSON.parse($('#cookies').text())).toEqual([])
const firstTime = $('#now').text()
if (!(global as any).isNextDev) {
const res2 = await next.fetch('/force-static/first')
expect(res2.status).toBe(200)
const $2 = cheerio.load(await res2.text())
expect(firstTime).toBe($2('#now').text())
}
})
it('should honor dynamic = "force-static" correctly (lazy)', async () => {
const res = await next.fetch('/force-static/random')
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect(JSON.parse($('#params').text())).toEqual({ slug: 'random' })
expect(JSON.parse($('#headers').text())).toEqual([])
expect(JSON.parse($('#cookies').text())).toEqual([])
const firstTime = $('#now').text()
if (!(global as any).isNextDev) {
const res2 = await next.fetch('/force-static/random')
expect(res2.status).toBe(200)
const $2 = cheerio.load(await res2.text())
expect(firstTime).toBe($2('#now').text())
}
})
// since we aren't leveraging fs cache with custom handler
// then these will 404 as they are cache misses
if (!(isNextStart && process.env.CUSTOM_CACHE_HANDLER)) {
it('should handle dynamicParams: false correctly', async () => {
const validParams = ['tim', 'seb', 'styfle']
for (const param of validParams) {
const res = await next.fetch(`/blog/${param}`, {
redirect: 'manual',
})
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect(JSON.parse($('#params').text())).toEqual({
author: param,
})
expect($('#page').text()).toBe('/blog/[author]')
}
const invalidParams = ['timm', 'non-existent']
for (const param of invalidParams) {
const invalidRes = await next.fetch(`/blog/${param}`, {
redirect: 'manual',
})
expect(invalidRes.status).toBe(404)
expect(await invalidRes.text()).toContain('page could not be found')
}
})
}
it('should work with forced dynamic path', async () => {
for (const slug of ['first', 'second']) {
const res = await next.fetch(`/dynamic-no-gen-params-ssr/${slug}`, {
redirect: 'manual',
})
expect(res.status).toBe(200)
expect(await res.text()).toContain(`${slug}`)
}
})
it('should work with dynamic path no generateStaticParams', async () => {
for (const slug of ['first', 'second']) {
const res = await next.fetch(`/dynamic-no-gen-params/${slug}`, {
redirect: 'manual',
})
expect(res.status).toBe(200)
expect(await res.text()).toContain(`${slug}`)
}
})
it('should handle dynamicParams: true correctly', async () => {
const paramsToCheck = [
{
author: 'tim',
slug: 'first-post',
},
{
author: 'seb',
slug: 'second-post',
},
{
author: 'styfle',
slug: 'first-post',
},
{
author: 'new-author',
slug: 'first-post',
},
]
for (const params of paramsToCheck) {
const res = await next.fetch(`/blog/${params.author}/${params.slug}`, {
redirect: 'manual',
})
expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)
expect(JSON.parse($('#params').text())).toEqual(params)
expect($('#page').text()).toBe('/blog/[author]/[slug]')
}
})
// since we aren't leveraging fs cache with custom handler
// then these will 404 as they are cache misses
if (!(isNextStart && process.env.CUSTOM_CACHE_HANDLER)) {
it('should navigate to static path correctly', async () => {
const browser = await next.browser('/blog/tim')
await browser.eval('window.beforeNav = 1')
expect(
await browser.eval('document.documentElement.innerHTML')
).toContain('/blog/[author]')
await browser.elementByCss('#author-2').click()
await check(async () => {
const params = JSON.parse(
await browser.elementByCss('#params').text()
)
return params.author === 'seb' ? 'found' : params
}, 'found')
expect(await browser.eval('window.beforeNav')).toBe(1)
await browser.elementByCss('#author-1-post-1').click()
await check(async () => {
const params = JSON.parse(
await browser.elementByCss('#params').text()
)
return params.author === 'tim' && params.slug === 'first-post'
? 'found'
: params
}, 'found')
expect(await browser.eval('window.beforeNav')).toBe(1)
await browser.back()
await check(async () => {
const params = JSON.parse(
await browser.elementByCss('#params').text()
)
return params.author === 'seb' ? 'found' : params
}, 'found')
expect(await browser.eval('window.beforeNav')).toBe(1)
})
}
it('should ssr dynamically when detected automatically with fetch cache option', async () => {
const pathname = '/ssr-auto/cache-no-store'
const initialRes = await next.fetch(pathname, {
redirect: 'manual',
})
expect(initialRes.status).toBe(200)
const initialHtml = await initialRes.text()
const initial$ = cheerio.load(initialHtml)
expect(initial$('#page').text()).toBe(pathname)
const initialDate = initial$('#date').text()
expect(initialHtml).toContain('Example Domain')
const secondRes = await next.fetch(pathname, {
redirect: 'manual',
})
expect(secondRes.status).toBe(200)
const secondHtml = await secondRes.text()
const second$ = cheerio.load(secondHtml)
expect(second$('#page').text()).toBe(pathname)
const secondDate = second$('#date').text()
expect(secondHtml).toContain('Example Domain')
expect(secondDate).not.toBe(initialDate)
})
it('should render not found pages correctly and fallback to the default one', async () => {
const res = await next.fetch(`/blog/shu/hi`, {
redirect: 'manual',
})
expect(res.status).toBe(404)
const html = await res.text()
expect(html).toInclude('"noindex"')
expect(html).toInclude('This page could not be found.')
})
// TODO-APP: support fetch revalidate case for dynamic rendering
it.skip('should ssr dynamically when detected automatically with fetch revalidate option', async () => {
const pathname = '/ssr-auto/fetch-revalidate-zero'
const initialRes = await next.fetch(pathname, {
redirect: 'manual',
})
expect(initialRes.status).toBe(200)
const initialHtml = await initialRes.text()
const initial$ = cheerio.load(initialHtml)
expect(initial$('#page').text()).toBe(pathname)
const initialDate = initial$('#date').text()
expect(initialHtml).toContain('Example Domain')
const secondRes = await next.fetch(pathname, {
redirect: 'manual',
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
})
expect(secondRes.status).toBe(200)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
const secondHtml = await secondRes.text()
const second$ = cheerio.load(secondHtml)
expect(second$('#page').text()).toBe(pathname)
const secondDate = second$('#date').text()
expect(secondHtml).toContain('Example Domain')
expect(secondDate).not.toBe(initialDate)
})
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
it('should ssr dynamically when forced via config', async () => {
const initialRes = await next.fetch('/ssr-forced', {
redirect: 'manual',
})
expect(initialRes.status).toBe(200)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
const initialHtml = await initialRes.text()
const initial$ = cheerio.load(initialHtml)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
expect(initial$('#page').text()).toBe('/ssr-forced')
const initialDate = initial$('#date').text()
const secondRes = await next.fetch('/ssr-forced', {
redirect: 'manual',
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
})
expect(secondRes.status).toBe(200)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
const secondHtml = await secondRes.text()
const second$ = cheerio.load(secondHtml)
expect(second$('#page').text()).toBe('/ssr-forced')
const secondDate = second$('#date').text()
expect(secondDate).not.toBe(initialDate)
})
describe('useSearchParams', () => {
describe('client', () => {
it('should bailout to client rendering - without suspense boundary', async () => {
const browser = await next.browser(
'/hooks/use-search-params?first=value&second=other&third'
)
expect(await browser.elementByCss('#params-first').text()).toBe(
'value'
)
expect(await browser.elementByCss('#params-second').text()).toBe(
'other'
)
expect(await browser.elementByCss('#params-third').text()).toBe('')
expect(await browser.elementByCss('#params-not-real').text()).toBe(
'N/A'
)
})
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
it('should bailout to client rendering - with suspense boundary', async () => {
const browser = await next.browser(
'/hooks/use-search-params/with-suspense?first=value&second=other&third'
)
expect(await browser.elementByCss('#params-first').text()).toBe(
'value'
)
expect(await browser.elementByCss('#params-second').text()).toBe(
'other'
)
expect(await browser.elementByCss('#params-third').text()).toBe('')
expect(await browser.elementByCss('#params-not-real').text()).toBe(
'N/A'
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
)
})
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
it.skip('should have empty search params on force-static', async () => {
const browser = await next.browser(
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
'/hooks/use-search-params/force-static?first=value&second=other&third'
)
expect(await browser.elementByCss('#params-first').text()).toBe('N/A')
expect(await browser.elementByCss('#params-second').text()).toBe(
'N/A'
)
expect(await browser.elementByCss('#params-third').text()).toBe('N/A')
expect(await browser.elementByCss('#params-not-real').text()).toBe(
'N/A'
)
await browser.elementById('to-use-search-params').click()
await browser.waitForElementByCss('#hooks-use-search-params')
// Should not be empty after navigating to another page with useSearchParams
expect(await browser.elementByCss('#params-first').text()).toBe('1')
expect(await browser.elementByCss('#params-second').text()).toBe('2')
expect(await browser.elementByCss('#params-third').text()).toBe('3')
expect(await browser.elementByCss('#params-not-real').text()).toBe(
'N/A'
)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
})
// TODO-APP: re-enable after investigating rewrite params
if (!(global as any).isNextDeploy) {
it('should have values from canonical url on rewrite', async () => {
const browser = await next.browser(
'/rewritten-use-search-params?first=a&second=b&third=c'
)
expect(await browser.elementByCss('#params-first').text()).toBe('a')
expect(await browser.elementByCss('#params-second').text()).toBe(
'b'
)
expect(await browser.elementByCss('#params-third').text()).toBe('c')
expect(await browser.elementByCss('#params-not-real').text()).toBe(
'N/A'
)
})
}
})
// Don't run these tests in dev mode since they won't be statically generated
if (!isDev) {
describe('server response', () => {
it('should bailout to client rendering - without suspense boundary', async () => {
const res = await next.fetch('/hooks/use-search-params')
const html = await res.text()
expect(html).toInclude('<html id="__next_error__">')
})
it('should bailout to client rendering - with suspense boundary', async () => {
const res = await next.fetch(
'/hooks/use-search-params/with-suspense'
)
const html = await res.text()
expect(html).toInclude('<p>search params suspense</p>')
})
it.skip('should have empty search params on force-static', async () => {
const res = await next.fetch(
'/hooks/use-search-params/force-static?first=value&second=other&third'
)
const html = await res.text()
// Shouild not bail out to client rendering
expect(html).not.toInclude('<p>search params suspense</p>')
// Use empty search params instead
const $ = cheerio.load(html)
expect($('#params-first').text()).toBe('N/A')
expect($('#params-second').text()).toBe('N/A')
expect($('#params-third').text()).toBe('N/A')
expect($('#params-not-real').text()).toBe('N/A')
})
})
}
})
// TODO: needs updating as usePathname should not bail
describe.skip('usePathname', () => {
if (isDev) {
it('should bail out to client rendering during SSG', async () => {
const res = await next.fetch('/hooks/use-pathname/slug')
const html = await res.text()
expect(html).toInclude('<html id="__next_error__">')
})
}
it('should have the correct values', async () => {
const browser = await next.browser('/hooks/use-pathname/slug')
expect(await browser.elementByCss('#pathname').text()).toBe(
'/hooks/use-pathname/slug'
)
})
it('should have values from canonical url on rewrite', async () => {
const browser = await next.browser('/rewritten-use-pathname')
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
expect(await browser.elementByCss('#pathname').text()).toBe(
'/rewritten-use-pathname'
)
})
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
})
if (!(global as any).isNextDeploy) {
it('should show a message to leave feedback for `appDir`', async () => {
expect(next.cliOutput).toContain(
`Thank you for testing \`appDir\` please leave your feedback at https://nextjs.link/app-feedback`
)
})
}
it('should keep querystring on static page', async () => {
const browser = await next.browser('/blog/tim?message=hello-world')
const checkUrl = async () =>
expect(await browser.url()).toBe(
next.url + '/blog/tim?message=hello-world'
)
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
checkUrl()
await waitFor(1000)
checkUrl()
})
if (process.env.CUSTOM_CACHE_HANDLER && !isNextDeploy) {
it('should have logs from cache-handler', () => {
expect(next.cliOutput).toContain('initialized custom cache-handler')
expect(next.cliOutput).toContain('cache-handler get')
expect(next.cliOutput).toContain('cache-handler set')
})
}
useSearchParams - bailout to client rendering during static generation (#43603) Currently `useSearchParams` bails out of static generation altogether, forcing the page to be dynamic. This behaviour is wrong. Instead it should still be statically generated, but `useSearchParams` should only run on the client. This is achieved by throwing a "bailout to client rendering" error. If there's no suspense boundary the whole page will bailout to be rendered on the client. If there is a suspense boundary it will only bailout from that point. ~This PR also adds handling for `export const dynamic = 'force-static'` combined with `useSearchParams`. If it is enabled it will return an empty `ReadonlyURLSearchParams` and skip the bailout to client rendering. Since the `staticGenerationAsyncStorage` only is available on the server - `forceStatic` is sent to the `app-router` to enable sending an empty `URLSearchParams` to match the server response.~ https://github.com/vercel/next.js/pull/43603#discussion_r1042071542 the implementation was wrong, added skipped tests and todo comment for now. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-12-12 12:53:38 +01:00
}
)