Skip to content

Commit 58ec5b8

Browse files
committed
tests
1 parent 0dd145f commit 58ec5b8

File tree

45 files changed

+3365
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3365
-4
lines changed

test/e2e/app-dir/segment-cache/cdn-cache-busting/cdn-cache-busting.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ describe('segment cache (CDN cache busting)', () => {
1111
return
1212
}
1313

14+
// TODO(runtime-ppr): add tests for runtime prefetches
15+
1416
// To debug these tests locally, run:
1517
// node start.mjs
1618
//
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Link from 'next/link'
2+
import { ReactNode } from 'react'
3+
4+
export default function RootLayout({ children }: { children: ReactNode }) {
5+
return (
6+
<html>
7+
<body style={{ fontFamily: 'monospace' }}>
8+
<Header />
9+
{children}
10+
</body>
11+
</html>
12+
)
13+
}
14+
15+
function Header() {
16+
return (
17+
<header>
18+
<Link href="/" prefetch={false}>
19+
Home
20+
</Link>
21+
</header>
22+
)
23+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { DebugLinkAccordion } from '../components/link-accordion'
2+
import { unstable_cacheLife } from 'next/cache'
3+
4+
export default async function Page() {
5+
'use cache'
6+
unstable_cacheLife('minutes')
7+
return (
8+
<main>
9+
<h2>shared layout prefetching - layout with cookies and dynamic data</h2>
10+
<ul>
11+
<li>
12+
<DebugLinkAccordion href="/shared-layout/one" prefetch={true} />
13+
</li>
14+
<li>
15+
<DebugLinkAccordion
16+
href="/shared-layout/one"
17+
prefetch={'unstable_forceStale'}
18+
/>
19+
</li>
20+
</ul>
21+
<ul>
22+
<li>
23+
<DebugLinkAccordion href="/shared-layout/two" prefetch={'auto'} />
24+
</li>
25+
<li>
26+
<DebugLinkAccordion href="/shared-layout/two" prefetch={true} />
27+
</li>
28+
<li>
29+
<DebugLinkAccordion
30+
href="/shared-layout/two"
31+
prefetch={'unstable_forceStale'}
32+
/>
33+
</li>
34+
</ul>
35+
<h2>shared layout prefetching - layout with cookies</h2>
36+
<ul>
37+
<li>
38+
<DebugLinkAccordion
39+
href="/runtime-prefetchable-layout/one"
40+
prefetch={true}
41+
/>
42+
</li>
43+
<li>
44+
<DebugLinkAccordion
45+
href="/runtime-prefetchable-layout/two"
46+
prefetch={'auto'}
47+
/>
48+
</li>
49+
<li>
50+
<DebugLinkAccordion
51+
href="/runtime-prefetchable-layout/two"
52+
prefetch={'unstable_forceStale'}
53+
/>
54+
</li>
55+
</ul>
56+
</main>
57+
)
58+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { cookies } from 'next/headers'
2+
import { Suspense } from 'react'
3+
import { cachedDelay, DebugRenderKind } from '../shared'
4+
5+
export default async function Layout({ children }) {
6+
return (
7+
<main>
8+
<div>
9+
<h2>Shared layout</h2>
10+
<DebugRenderKind />
11+
<p id="shared-layout-description">
12+
This shared layout uses cookies and no uncached IO, so it should be
13+
completely runtime-prefetchable.
14+
</p>
15+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
16+
<RuntimePrefetchable />
17+
</Suspense>
18+
</div>
19+
<hr />
20+
{children}
21+
</main>
22+
)
23+
}
24+
25+
async function RuntimePrefetchable() {
26+
const cookieStore = await cookies()
27+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
28+
await cachedDelay(500, [__filename, cookieValue])
29+
return (
30+
<div style={{ border: '1px solid blue', padding: '1em' }}>
31+
<div id="cookie-value-layout">{`Cookie from layout: ${cookieValue}`}</div>
32+
</div>
33+
)
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Suspense } from 'react'
2+
import { cachedDelay } from '../../shared'
3+
import { cookies } from 'next/headers'
4+
5+
export default function Page() {
6+
return (
7+
<main>
8+
<h1 style={{ color: 'yellow' }}>Page one</h1>
9+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
10+
<RuntimePrefetchable />
11+
</Suspense>
12+
</main>
13+
)
14+
}
15+
16+
async function RuntimePrefetchable() {
17+
const cookieStore = await cookies()
18+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
19+
await cachedDelay(500, [__filename, cookieValue])
20+
return (
21+
<div style={{ border: '1px solid blue', padding: '1em' }}>
22+
<div id="cookie-value-page">{`Cookie from page: ${cookieValue}`}</div>
23+
{/*
24+
TODO: a runtime-prefetched layout that had no holes itself will still be considered partial
25+
if any other segment in the response is partial, because we don't track partiality per-segment,
26+
so if we want to test that full prefetches can reuse layouts from runtime prefetches,
27+
the whole page needs to be dynamically prerenderable.
28+
*/}
29+
{/* <Suspense fallback={<div style={{ color: 'grey' }}>Loading 2...</div>}>
30+
<Dynamic />
31+
</Suspense> */}
32+
</div>
33+
)
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Suspense } from 'react'
2+
import { cachedDelay, uncachedIO } from '../../shared'
3+
import { cookies } from 'next/headers'
4+
5+
export default function Page() {
6+
return (
7+
<main>
8+
<h1 style={{ color: 'green' }}>Page two</h1>
9+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
10+
<RuntimePrefetchable />
11+
</Suspense>
12+
</main>
13+
)
14+
}
15+
16+
async function RuntimePrefetchable() {
17+
const cookieStore = await cookies()
18+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
19+
await cachedDelay(500, [__filename, cookieValue])
20+
return (
21+
<div style={{ border: '1px solid blue', padding: '1em' }}>
22+
<div id="cookie-value-page">{`Cookie from page: ${cookieValue}`}</div>
23+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 2...</div>}>
24+
<Dynamic />
25+
</Suspense>
26+
</div>
27+
)
28+
}
29+
30+
async function Dynamic() {
31+
await uncachedIO()
32+
return (
33+
<div style={{ border: '1px solid tomato', padding: '1em' }}>
34+
<div id="dynamic-content-page">Dynamic content from page two</div>
35+
</div>
36+
)
37+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { cookies } from 'next/headers'
2+
import { Suspense } from 'react'
3+
import { cachedDelay, DebugRenderKind, uncachedIO } from '../shared'
4+
import { connection } from 'next/server'
5+
6+
export default async function Layout({ children }) {
7+
return (
8+
<main>
9+
<div>
10+
<h2>Shared layout</h2>
11+
<DebugRenderKind />
12+
<p id="shared-layout-description">
13+
This shared layout uses cookies and some uncached IO, so parts of it
14+
should be runtime-prefetchable.
15+
</p>
16+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
17+
<RuntimePrefetchable />
18+
</Suspense>
19+
</div>
20+
<hr />
21+
{children}
22+
</main>
23+
)
24+
}
25+
26+
async function RuntimePrefetchable() {
27+
const cookieStore = await cookies()
28+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
29+
await cachedDelay(500, [__filename, cookieValue])
30+
return (
31+
<div style={{ border: '1px solid blue', padding: '1em' }}>
32+
<div id="cookie-value-layout">{`Cookie from layout: ${cookieValue}`}</div>
33+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 2...</div>}>
34+
<Dynamic />
35+
</Suspense>
36+
</div>
37+
)
38+
}
39+
40+
async function Dynamic() {
41+
await uncachedIO()
42+
await connection()
43+
return (
44+
<div style={{ border: '1px solid tomato', padding: '1em' }}>
45+
<div id="dynamic-content-layout">Dynamic content from layout</div>
46+
</div>
47+
)
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Suspense } from 'react'
2+
import { cachedDelay, uncachedIO } from '../../shared'
3+
import { cookies } from 'next/headers'
4+
5+
export default function Page() {
6+
return (
7+
<main>
8+
<h1 style={{ color: 'yellow' }}>Page one</h1>
9+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
10+
<RuntimePrefetchable />
11+
</Suspense>
12+
</main>
13+
)
14+
}
15+
16+
async function RuntimePrefetchable() {
17+
const cookieStore = await cookies()
18+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
19+
await cachedDelay(500, [__filename, cookieValue])
20+
return (
21+
<div style={{ border: '1px solid blue', padding: '1em' }}>
22+
<div id="cookie-value-page">{`Cookie from page: ${cookieValue}`}</div>
23+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 2...</div>}>
24+
<Dynamic />
25+
</Suspense>
26+
</div>
27+
)
28+
}
29+
30+
async function Dynamic() {
31+
await uncachedIO()
32+
return (
33+
<div style={{ border: '1px solid tomato', padding: '1em' }}>
34+
<div id="dynamic-content-page">Dynamic content from page one</div>
35+
</div>
36+
)
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Suspense } from 'react'
2+
import { cachedDelay, uncachedIO } from '../../shared'
3+
import { cookies } from 'next/headers'
4+
5+
export default function Page() {
6+
return (
7+
<main>
8+
<h1 style={{ color: 'green' }}>Page two</h1>
9+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 1...</div>}>
10+
<RuntimePrefetchable />
11+
</Suspense>
12+
</main>
13+
)
14+
}
15+
16+
async function RuntimePrefetchable() {
17+
const cookieStore = await cookies()
18+
const cookieValue = cookieStore.get('testCookie')?.value ?? null
19+
await cachedDelay(500, [__filename, cookieValue])
20+
return (
21+
<div style={{ border: '1px solid blue', padding: '1em' }}>
22+
<div id="cookie-value-page">{`Cookie from page: ${cookieValue}`}</div>
23+
<Suspense fallback={<div style={{ color: 'grey' }}>Loading 2...</div>}>
24+
<Dynamic />
25+
</Suspense>
26+
</div>
27+
)
28+
}
29+
30+
async function Dynamic() {
31+
await uncachedIO()
32+
return (
33+
<div style={{ border: '1px solid tomato', padding: '1em' }}>
34+
<div id="dynamic-content-page">Dynamic content from page two</div>
35+
</div>
36+
)
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { unstable_cacheLife } from 'next/cache'
2+
import { setTimeout } from 'timers/promises'
3+
4+
export async function uncachedIO() {
5+
await setTimeout(500)
6+
}
7+
8+
export async function cachedDelay(time: number, cacheBuster?: any) {
9+
'use cache'
10+
unstable_cacheLife('minutes')
11+
console.log('cachedDelay', time, cacheBuster)
12+
await setTimeout(time)
13+
}
14+
15+
export function DebugRenderKind() {
16+
const { workUnitAsyncStorage } =
17+
require('next/dist/server/app-render/work-unit-async-storage.external') as typeof import('next/dist/server/app-render/work-unit-async-storage.external')
18+
const workUnitStore = workUnitAsyncStorage.getStore()!
19+
return (
20+
<div>
21+
workUnitStore.type: {workUnitStore.type}
22+
{(() => {
23+
switch (workUnitStore.type) {
24+
case 'prerender':
25+
return '(static prefetch)'
26+
case 'prerender-runtime':
27+
return '(runtime prefetch)'
28+
default:
29+
return null
30+
}
31+
})()}
32+
</div>
33+
)
34+
}

0 commit comments

Comments
 (0)