rsnext/test/e2e/app-dir/app-prefetch/prefetching.test.ts
Zack Tanner 59f7ca85c2
remove additional static prefetch code (#59313)
This is a continuation from https://github.com/vercel/next.js/pull/58783
to remove the remaining code related to static prefetching.
2023-12-05 21:29:23 -08:00

361 lines
11 KiB
TypeScript

import { createNextDescribe } from 'e2e-utils'
import { check, waitFor } from 'next-test-utils'
import { NEXT_RSC_UNION_QUERY } from 'next/dist/client/components/app-router-headers'
const browserConfigWithFixedTime = {
beforePageLoad: (page) => {
page.addInitScript(() => {
const startTime = new Date()
const fixedTime = new Date('2023-04-17T00:00:00Z')
// Override the Date constructor
// @ts-ignore
// eslint-disable-next-line no-native-reassign
Date = class extends Date {
constructor() {
super()
// @ts-ignore
return new startTime.constructor(fixedTime)
}
static now() {
return fixedTime.getTime()
}
}
})
},
}
createNextDescribe(
'app dir - prefetching',
{
files: __dirname,
skipDeployment: true,
},
({ next, isNextDev }) => {
// TODO: re-enable for dev after https://vercel.slack.com/archives/C035J346QQL/p1663822388387959 is resolved (Sep 22nd 2022)
if (isNextDev) {
it('should skip next dev for now', () => {})
return
}
it('NEXT_RSC_UNION_QUERY query name is _rsc', async () => {
expect(NEXT_RSC_UNION_QUERY).toBe('_rsc')
})
it('should show layout eagerly when prefetched with loading one level down', async () => {
const browser = await next.browser('/', browserConfigWithFixedTime)
// Ensure the page is prefetched
await waitFor(1000)
const before = Date.now()
await browser
.elementByCss('#to-dashboard')
.click()
.waitForElementByCss('#dashboard-layout')
const after = Date.now()
const timeToComplete = after - before
expect(timeToComplete).toBeLessThan(1000)
expect(await browser.elementByCss('#dashboard-layout').text()).toBe(
'Dashboard Hello World'
)
await browser.waitForElementByCss('#dashboard-page')
expect(await browser.waitForElementByCss('#dashboard-page').text()).toBe(
'Welcome to the dashboard'
)
})
it('should not have prefetch error for static path', async () => {
const browser = await next.browser('/')
await browser.eval('window.nd.router.prefetch("/dashboard/123")')
await waitFor(3000)
await browser.eval('window.nd.router.push("/dashboard/123")')
expect(next.cliOutput).not.toContain('ReferenceError')
expect(next.cliOutput).not.toContain('is not defined')
})
it('should not fetch again when a static page was prefetched', async () => {
const browser = await next.browser('/404', browserConfigWithFixedTime)
let requests: string[] = []
browser.on('request', (req) => {
requests.push(new URL(req.url()).pathname)
})
await browser.eval('location.href = "/"')
await browser.eval(
'window.nd.router.prefetch("/static-page", {kind: "auto"})'
)
await check(() => {
return requests.some(
(req) =>
req.includes('static-page') && !req.includes(NEXT_RSC_UNION_QUERY)
)
? 'success'
: JSON.stringify(requests)
}, 'success')
await browser
.elementByCss('#to-static-page')
.click()
.waitForElementByCss('#static-page')
expect(
requests.filter((request) => request === '/static-page').length
).toBe(1)
})
it('should not fetch again when a static page was prefetched when navigating to it twice', async () => {
const browser = await next.browser('/404', browserConfigWithFixedTime)
let requests: string[] = []
browser.on('request', (req) => {
requests.push(new URL(req.url()).pathname)
})
await browser.eval('location.href = "/"')
await browser.eval(
`window.nd.router.prefetch("/static-page", {kind: "auto"})`
)
await check(() => {
return requests.some(
(req) =>
req.includes('static-page') && !req.includes(NEXT_RSC_UNION_QUERY)
)
? 'success'
: JSON.stringify(requests)
}, 'success')
await browser
.elementByCss('#to-static-page')
.click()
.waitForElementByCss('#static-page')
await browser
.elementByCss('#to-home')
// Go back to home page
.click()
// Wait for homepage to load
.waitForElementByCss('#to-static-page')
// Click on the link to the static page again
.click()
// Wait for the static page to load again
.waitForElementByCss('#static-page')
expect(
requests.filter(
(request) =>
request === '/static-page' || request.includes(NEXT_RSC_UNION_QUERY)
).length
).toBe(1)
})
it('should calculate `_rsc` query based on `Next-Url`', async () => {
const browser = await next.browser('/404', browserConfigWithFixedTime)
let staticPageRequests: string[] = []
browser.on('request', (req) => {
const url = new URL(req.url())
if (url.toString().includes(`/static-page?${NEXT_RSC_UNION_QUERY}=`)) {
staticPageRequests.push(`${url.pathname}${url.search}`)
}
})
await browser.eval('location.href = "/"')
await browser.eval(
`window.nd.router.prefetch("/static-page", {kind: "auto"})`
)
await check(() => {
return staticPageRequests.length === 1
? 'success'
: JSON.stringify(staticPageRequests)
}, 'success')
// Unable to clear router cache so mpa navigation
await browser.eval('location.href = "/dashboard"')
await browser.eval(
`window.nd.router.prefetch("/static-page", {kind: "auto"})`
)
await check(() => {
return staticPageRequests.length === 2
? 'success'
: JSON.stringify(staticPageRequests)
}, 'success')
expect(staticPageRequests[0]).toMatch('/static-page?_rsc=')
expect(staticPageRequests[1]).toMatch('/static-page?_rsc=')
// `_rsc` does not match because it depends on the `Next-Url`
expect(staticPageRequests[0]).not.toBe(staticPageRequests[1])
})
it('should not prefetch for a bot user agent', async () => {
const browser = await next.browser('/404')
let requests: string[] = []
browser.on('request', (req) => {
requests.push(new URL(req.url()).pathname)
})
await browser.eval(
`location.href = "/?useragent=${encodeURIComponent(
'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
)}"`
)
await browser.elementByCss('#to-static-page').moveTo()
// check five times to ensure prefetch didn't occur
for (let i = 0; i < 5; i++) {
await waitFor(500)
expect(
requests.filter(
(request) =>
request === '/static-page' ||
request.includes(NEXT_RSC_UNION_QUERY)
).length
).toBe(0)
}
})
it('should navigate when prefetch is false', async () => {
const browser = await next.browser('/prefetch-false/initial')
await browser
.elementByCss('#to-prefetch-false-result')
.click()
.waitForElementByCss('#prefetch-false-page-result')
expect(
await browser.elementByCss('#prefetch-false-page-result').text()
).toBe('Result page')
})
it('should not need to prefetch the layout if the prefetch is initiated at the same segment', async () => {
const stateTree = encodeURIComponent(
JSON.stringify([
'',
{
children: [
'prefetch-auto',
{
children: [
['slug', 'justputit', 'd'],
{ children: ['__PAGE__', {}] },
],
},
],
},
null,
null,
true,
])
)
const response = await next.fetch(`/prefetch-auto/justputit?_rsc=dcqtr`, {
headers: {
RSC: '1',
'Next-Router-Prefetch': '1',
'Next-Router-State-Tree': stateTree,
'Next-Url': '/prefetch-auto/justputit',
},
})
const prefetchResponse = await response.text()
expect(prefetchResponse).not.toContain('Hello World')
expect(prefetchResponse).not.toContain('Loading Prefetch Auto')
})
it('should only prefetch the loading state and not the component tree when prefetching at the same segment', async () => {
const stateTree = encodeURIComponent(
JSON.stringify([
'',
{
children: [
'prefetch-auto',
{
children: [
['slug', 'vercel', 'd'],
{ children: ['__PAGE__', {}] },
],
},
],
},
null,
null,
true,
])
)
const response = await next.fetch(`/prefetch-auto/justputit?_rsc=dcqtr`, {
headers: {
RSC: '1',
'Next-Router-Prefetch': '1',
'Next-Router-State-Tree': stateTree,
'Next-Url': '/prefetch-auto/vercel',
},
})
const prefetchResponse = await response.text()
expect(prefetchResponse).not.toContain('Hello World')
expect(prefetchResponse).toContain('Loading Prefetch Auto')
})
describe('dynamic rendering', () => {
describe.each(['/force-dynamic', '/revalidate-0'])('%s', (basePath) => {
it('should not re-render layout when navigating between sub-pages', async () => {
const logStartIndex = next.cliOutput.length
const browser = await next.browser(`${basePath}/test-page`)
let initialRandomNumber = await browser
.elementById('random-number')
.text()
await browser
.elementByCss(`[href="${basePath}/test-page/sub-page"]`)
.click()
await check(() => browser.hasElementByCssSelector('#sub-page'), true)
const newRandomNumber = await browser
.elementById('random-number')
.text()
expect(initialRandomNumber).toBe(newRandomNumber)
await check(() => {
const logOccurrences =
next.cliOutput.slice(logStartIndex).split('re-fetching in layout')
.length - 1
return logOccurrences
}, 1)
})
it('should update search params following a link click', async () => {
const browser = await next.browser(`${basePath}/search-params`)
await check(
() => browser.elementById('search-params-data').text(),
/{}/
)
await browser.elementByCss('[href="?foo=true"]').click()
await check(
() => browser.elementById('search-params-data').text(),
/{"foo":"true"}/
)
await browser
.elementByCss(`[href="${basePath}/search-params"]`)
.click()
await check(
() => browser.elementById('search-params-data').text(),
/{}/
)
await browser.elementByCss('[href="?foo=true"]').click()
await check(
() => browser.elementById('search-params-data').text(),
/{"foo":"true"}/
)
})
})
})
}
)