mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Stacked on #89984 When we initially implemented runtime prefetching we decided to make sync IO that happens after runtime but before async IO an error. This is similar to but more aggressive than the error for sync IO before runtime data but is important because if you have sync IO before uncached IO and we are runtime prefetching the page then we aren't going to get anything useful from that prefetch but it might be very expensive. The problem with the current implementation is that if you turn on runtime prefetching for a Page segment then in practice the router will only ever runtime prefetch that page segment but the sync IO validator will error for sync IO before uncached IO anywhere in the route. This makes adding runtime prefetching to leaf Segments much harder b/c you have to address sync IO in the entire Segment stack. This change updates the sync IO heuristic to only error in segments that would be runtime prefetched and below. This way when you add runtime prefetching to a segment you only have to true up the sync IO for the part of the tree that has the changed behavior. This PR only partially implements the intended semantics for runtime prefetching validation of sync IO. One thing that will need to be done in a follow up is handling sync IO in generateMetadata. In this case while the functions are defined in layouts and pages they are semantically always "owned" by the page itself. This means that while the layout might not be runtime prefetchable and thus allow sync IO after runtime data access the generateMetadata in that same layout might not allow sync IO after runtime if there is a runtime prefetch config at the page level or some layout higher. This will be done in a follow up
152 lines
4.6 KiB
TypeScript
152 lines
4.6 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
|
|
describe('react-performance-track', () => {
|
|
// false is the default when visiting pages as an ordinary user.
|
|
// true is the default when having Chrome DevTools open.
|
|
// Hardcoded for now since most of the actual behavior is not intended.
|
|
const disableCache = false
|
|
const extraHTTPHeaders = disableCache
|
|
? { 'Cache-Control': 'no-cache' }
|
|
: undefined
|
|
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
it('should show setTimeout', async () => {
|
|
const browser = await next.browser('/set-timeout', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{ name: '\u200bsetTimeout', properties: [] },
|
|
{ name: '\u200bsetTimeout', properties: [] },
|
|
])
|
|
)
|
|
})
|
|
|
|
it('should show fetch', async () => {
|
|
const browser = await next.browser('/fetch', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
// React might decide to display the shorthand in round brackets differently.
|
|
// Double check with React changes if a shorthand change is intended.
|
|
name: '\u200bfetch (…/random)',
|
|
properties: expect.arrayContaining([
|
|
['status', '200'],
|
|
['url', '"https://next-data-api-endpoint.vercel.app/api/random"'],
|
|
]),
|
|
},
|
|
])
|
|
)
|
|
})
|
|
|
|
it('should show params', async () => {
|
|
const browser = await next.browser('/params/next', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
name: '\u200bparams [Prefetchable]',
|
|
properties: [],
|
|
},
|
|
])
|
|
)
|
|
})
|
|
|
|
it('should show searchParams', async () => {
|
|
const browser = await next.browser('/searchparams?slug=next', {
|
|
extraHTTPHeaders,
|
|
})
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
name: '\u200bsearchParams [Prefetchable]',
|
|
properties: [],
|
|
},
|
|
])
|
|
)
|
|
})
|
|
|
|
it('should show cookies', async () => {
|
|
const browser = await next.browser('/cookies', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
name: '\u200bcookies [Prefetchable]',
|
|
properties: [],
|
|
},
|
|
])
|
|
)
|
|
})
|
|
|
|
it('should show draftMode', async () => {
|
|
const browser = await next.browser('/draftMode', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
// TODO the addition of a promise to delay some Segments from rendering until the later Static
|
|
// stage has caused the draftMode snapshot to include an empty-named entry. This is probably
|
|
// a bug in React and should be fixed there but
|
|
// expect(track).toEqual([])
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
name: '\u200b [Prerender]',
|
|
properties: [],
|
|
},
|
|
])
|
|
)
|
|
let didThrow = false
|
|
try {
|
|
// including this anti-assertion here so we can restore the test when the bug in React is fixed
|
|
expect(track).toEqual([])
|
|
} catch (e) {
|
|
didThrow = true
|
|
}
|
|
expect(didThrow).toBe(true)
|
|
})
|
|
|
|
it('should show headers', async () => {
|
|
const browser = await next.browser('/headers', { extraHTTPHeaders })
|
|
await browser.elementByCss('[data-react-server-requests-done]', {
|
|
state: 'attached',
|
|
})
|
|
|
|
const track = await browser.eval('window.reactServerRequests.getSnapshot()')
|
|
expect(track).toEqual(
|
|
expect.arrayContaining([
|
|
{
|
|
name: '\u200bheaders [Prefetchable]',
|
|
properties: [],
|
|
},
|
|
])
|
|
)
|
|
})
|
|
})
|