Skip to content

Commit 19e2281

Browse files
committed
test: add test case for fallback: true
1 parent 96b2ec7 commit 19e2281

File tree

5 files changed

+217
-21
lines changed

5 files changed

+217
-21
lines changed

tests/e2e/page-router.test.ts

+125-21
Original file line numberDiff line numberDiff line change
@@ -51,51 +51,79 @@ export async function check(
5151

5252
test.describe('Simple Page Router (no basePath, no i18n)', () => {
5353
test.describe('On-demand revalidate works correctly', () => {
54-
for (const { label, prerendered, pagePath, revalidateApiBasePath, expectedH1Content } of [
54+
for (const {
55+
label,
56+
useFallback,
57+
prerendered,
58+
pagePath,
59+
revalidateApiBasePath,
60+
expectedH1Content,
61+
} of [
5562
{
56-
label: 'prerendered page with static path and awaited res.revalidate()',
63+
label:
64+
'prerendered page with static path with fallback: blocking and awaited res.revalidate()',
5765
prerendered: true,
5866
pagePath: '/static/revalidate-manual',
5967
revalidateApiBasePath: '/api/revalidate',
6068
expectedH1Content: 'Show #71',
6169
},
6270
{
63-
label: 'prerendered page with dynamic path and awaited res.revalidate()',
71+
label:
72+
'prerendered page with dynamic path with fallback: blocking and awaited res.revalidate()',
6473
prerendered: true,
6574
pagePath: '/products/prerendered',
6675
revalidateApiBasePath: '/api/revalidate',
6776
expectedH1Content: 'Product prerendered',
6877
},
6978
{
70-
label: 'not prerendered page with dynamic path and awaited res.revalidate()',
79+
label:
80+
'not prerendered page with dynamic path with fallback: blocking and awaited res.revalidate()',
7181
prerendered: false,
7282
pagePath: '/products/not-prerendered',
7383
revalidateApiBasePath: '/api/revalidate',
7484
expectedH1Content: 'Product not-prerendered',
7585
},
7686
{
77-
label: 'not prerendered page with dynamic path and not awaited res.revalidate()',
87+
label:
88+
'not prerendered page with dynamic path with fallback: blocking and not awaited res.revalidate()',
7889
prerendered: false,
7990
pagePath: '/products/not-prerendered-and-not-awaited-revalidation',
8091
revalidateApiBasePath: '/api/revalidate-no-await',
8192
expectedH1Content: 'Product not-prerendered-and-not-awaited-revalidation',
8293
},
8394
{
8495
label:
85-
'prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
96+
'prerendered page with dynamic path with fallback: blocking and awaited res.revalidate() - non-ASCII variant',
8697
prerendered: true,
8798
pagePath: '/products/事前レンダリング,test',
8899
revalidateApiBasePath: '/api/revalidate',
89100
expectedH1Content: 'Product 事前レンダリング,test',
90101
},
91102
{
92103
label:
93-
'not prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
104+
'not prerendered page with dynamic path with fallback: blocking and awaited res.revalidate() - non-ASCII variant',
94105
prerendered: false,
95106
pagePath: '/products/事前レンダリングされていない,test',
96107
revalidateApiBasePath: '/api/revalidate',
97108
expectedH1Content: 'Product 事前レンダリングされていない,test',
98109
},
110+
{
111+
label:
112+
'prerendered page with dynamic path with fallback: true and awaited res.revalidate()',
113+
prerendered: true,
114+
pagePath: '/fallback-true/prerendered',
115+
revalidateApiBasePath: '/api/revalidate',
116+
expectedH1Content: 'Product prerendered',
117+
},
118+
{
119+
label:
120+
'not prerendered page with dynamic path with fallback: true and awaited res.revalidate()',
121+
prerendered: false,
122+
useFallback: true,
123+
pagePath: '/fallback-true/not-prerendered',
124+
revalidateApiBasePath: '/api/revalidate',
125+
expectedH1Content: 'Product not-prerendered',
126+
},
99127
]) {
100128
test(label, async ({ page, pollUntilHeadersMatch, pageRouter }) => {
101129
// in case there is retry or some other test did hit that path before
@@ -126,11 +154,23 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => {
126154
const headers1 = response1?.headers() || {}
127155
expect(response1?.status()).toBe(200)
128156
expect(headers1['x-nextjs-cache']).toBeUndefined()
129-
expect(headers1['netlify-cache-tag']).toBe(`_n_t_${encodeURI(pagePath).toLowerCase()}`)
157+
158+
const fallbackWasServed =
159+
useFallback && headers1['cache-status'].includes('"Next.js"; fwd=miss')
160+
expect(headers1['netlify-cache-tag']).toBe(
161+
fallbackWasServed ? undefined : `_n_t_${encodeURI(pagePath).toLowerCase()}`,
162+
)
130163
expect(headers1['netlify-cdn-cache-control']).toBe(
131-
's-maxage=31536000, stale-while-revalidate=31536000, durable',
164+
fallbackWasServed
165+
? undefined
166+
: 's-maxage=31536000, stale-while-revalidate=31536000, durable',
132167
)
133168

169+
if (fallbackWasServed) {
170+
const loading = await page.textContent('[data-testid="loading"]')
171+
expect(loading, 'Fallback should be shown').toBe('Loading...')
172+
}
173+
134174
const date1 = await page.textContent('[data-testid="date-now"]')
135175
const h1 = await page.textContent('h1')
136176
expect(h1).toBe(expectedH1Content)
@@ -446,7 +486,14 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => {
446486

447487
test.describe('Page Router with basePath and i18n', () => {
448488
test.describe('Static revalidate works correctly', () => {
449-
for (const { label, prerendered, pagePath, revalidateApiBasePath, expectedH1Content } of [
489+
for (const {
490+
label,
491+
useFallback,
492+
prerendered,
493+
pagePath,
494+
revalidateApiBasePath,
495+
expectedH1Content,
496+
} of [
450497
{
451498
label: 'prerendered page with static path and awaited res.revalidate()',
452499
prerendered: true,
@@ -455,42 +502,62 @@ test.describe('Page Router with basePath and i18n', () => {
455502
expectedH1Content: 'Show #71',
456503
},
457504
{
458-
label: 'prerendered page with dynamic path and awaited res.revalidate()',
505+
label:
506+
'prerendered page with dynamic path with fallback: blocking and awaited res.revalidate()',
459507
prerendered: true,
460508
pagePath: '/products/prerendered',
461509
revalidateApiBasePath: '/api/revalidate',
462510
expectedH1Content: 'Product prerendered',
463511
},
464512
{
465-
label: 'not prerendered page with dynamic path and awaited res.revalidate()',
513+
label:
514+
'not prerendered page with dynamic path with fallback: blocking and awaited res.revalidate()',
466515
prerendered: false,
467516
pagePath: '/products/not-prerendered',
468517
revalidateApiBasePath: '/api/revalidate',
469518
expectedH1Content: 'Product not-prerendered',
470519
},
471520
{
472-
label: 'not prerendered page with dynamic path and not awaited res.revalidate()',
521+
label:
522+
'not prerendered page with dynamic path with fallback: blocking and not awaited res.revalidate()',
473523
prerendered: false,
474524
pagePath: '/products/not-prerendered-and-not-awaited-revalidation',
475525
revalidateApiBasePath: '/api/revalidate-no-await',
476526
expectedH1Content: 'Product not-prerendered-and-not-awaited-revalidation',
477527
},
478528
{
479529
label:
480-
'prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
530+
'prerendered page with dynamic path with fallback: blocking and awaited res.revalidate() - non-ASCII variant',
481531
prerendered: true,
482532
pagePath: '/products/事前レンダリング,test',
483533
revalidateApiBasePath: '/api/revalidate',
484534
expectedH1Content: 'Product 事前レンダリング,test',
485535
},
486536
{
487537
label:
488-
'not prerendered page with dynamic path and awaited res.revalidate() - non-ASCII variant',
538+
'not prerendered page with dynamic path with fallback: blocking and awaited res.revalidate() - non-ASCII variant',
489539
prerendered: false,
490540
pagePath: '/products/事前レンダリングされていない,test',
491541
revalidateApiBasePath: '/api/revalidate',
492542
expectedH1Content: 'Product 事前レンダリングされていない,test',
493543
},
544+
{
545+
label:
546+
'prerendered page with dynamic path with fallback: true and awaited res.revalidate()',
547+
prerendered: true,
548+
pagePath: '/fallback-true/prerendered',
549+
revalidateApiBasePath: '/api/revalidate',
550+
expectedH1Content: 'Product prerendered',
551+
},
552+
{
553+
label:
554+
'not prerendered page with dynamic path with fallback: true and awaited res.revalidate()',
555+
prerendered: false,
556+
useFallback: true,
557+
pagePath: '/fallback-true/not-prerendered',
558+
revalidateApiBasePath: '/api/revalidate',
559+
expectedH1Content: 'Product not-prerendered',
560+
},
494561
]) {
495562
test.describe(label, () => {
496563
test(`default locale`, async ({ page, pollUntilHeadersMatch, pageRouterBasePathI18n }) => {
@@ -528,13 +595,26 @@ test.describe('Page Router with basePath and i18n', () => {
528595
const headers1ImplicitLocale = response1ImplicitLocale?.headers() || {}
529596
expect(response1ImplicitLocale?.status()).toBe(200)
530597
expect(headers1ImplicitLocale['x-nextjs-cache']).toBeUndefined()
598+
599+
const fallbackWasServedImplicitLocale =
600+
useFallback && headers1ImplicitLocale['cache-status'].includes('"Next.js"; fwd=miss')
601+
531602
expect(headers1ImplicitLocale['netlify-cache-tag']).toBe(
532-
`_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
603+
fallbackWasServedImplicitLocale
604+
? undefined
605+
: `_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
533606
)
534607
expect(headers1ImplicitLocale['netlify-cdn-cache-control']).toBe(
535-
's-maxage=31536000, stale-while-revalidate=31536000, durable',
608+
fallbackWasServedImplicitLocale
609+
? undefined
610+
: 's-maxage=31536000, stale-while-revalidate=31536000, durable',
536611
)
537612

613+
if (fallbackWasServedImplicitLocale) {
614+
const loading = await page.textContent('[data-testid="loading"]')
615+
expect(loading, 'Fallback should be shown').toBe('Loading...')
616+
}
617+
538618
const date1ImplicitLocale = await page.textContent('[data-testid="date-now"]')
539619
const h1ImplicitLocale = await page.textContent('h1')
540620
expect(h1ImplicitLocale).toBe(expectedH1Content)
@@ -556,13 +636,25 @@ test.describe('Page Router with basePath and i18n', () => {
556636
const headers1ExplicitLocale = response1ExplicitLocale?.headers() || {}
557637
expect(response1ExplicitLocale?.status()).toBe(200)
558638
expect(headers1ExplicitLocale['x-nextjs-cache']).toBeUndefined()
639+
640+
const fallbackWasServedExplicitLocale =
641+
useFallback && headers1ExplicitLocale['cache-status'].includes('"Next.js"; fwd=miss')
559642
expect(headers1ExplicitLocale['netlify-cache-tag']).toBe(
560-
`_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
643+
fallbackWasServedExplicitLocale
644+
? undefined
645+
: `_n_t_/en${encodeURI(pagePath).toLowerCase()}`,
561646
)
562647
expect(headers1ExplicitLocale['netlify-cdn-cache-control']).toBe(
563-
's-maxage=31536000, stale-while-revalidate=31536000, durable',
648+
fallbackWasServedExplicitLocale
649+
? undefined
650+
: 's-maxage=31536000, stale-while-revalidate=31536000, durable',
564651
)
565652

653+
if (fallbackWasServedExplicitLocale) {
654+
const loading = await page.textContent('[data-testid="loading"]')
655+
expect(loading, 'Fallback should be shown').toBe('Loading...')
656+
}
657+
566658
const date1ExplicitLocale = await page.textContent('[data-testid="date-now"]')
567659
const h1ExplicitLocale = await page.textContent('h1')
568660
expect(h1ExplicitLocale).toBe(expectedH1Content)
@@ -910,11 +1002,23 @@ test.describe('Page Router with basePath and i18n', () => {
9101002
const headers1 = response1?.headers() || {}
9111003
expect(response1?.status()).toBe(200)
9121004
expect(headers1['x-nextjs-cache']).toBeUndefined()
913-
expect(headers1['netlify-cache-tag']).toBe(`_n_t_/de${encodeURI(pagePath).toLowerCase()}`)
1005+
1006+
const fallbackWasServed =
1007+
useFallback && headers1['cache-status'].includes('"Next.js"; fwd=miss')
1008+
expect(headers1['netlify-cache-tag']).toBe(
1009+
fallbackWasServed ? undefined : `_n_t_/de${encodeURI(pagePath).toLowerCase()}`,
1010+
)
9141011
expect(headers1['netlify-cdn-cache-control']).toBe(
915-
's-maxage=31536000, stale-while-revalidate=31536000, durable',
1012+
fallbackWasServed
1013+
? undefined
1014+
: 's-maxage=31536000, stale-while-revalidate=31536000, durable',
9161015
)
9171016

1017+
if (fallbackWasServed) {
1018+
const loading = await page.textContent('[data-testid="loading"]')
1019+
expect(loading, 'Fallback should be shown').toBe('Loading...')
1020+
}
1021+
9181022
const date1 = await page.textContent('[data-testid="date-now"]')
9191023
const h1 = await page.textContent('h1')
9201024
expect(h1).toBe(expectedH1Content)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useRouter } from 'next/router'
2+
3+
const Product = ({ time, slug }) => {
4+
const router = useRouter()
5+
6+
if (router.isFallback) {
7+
return <span data-testid="loading">Loading...</span>
8+
}
9+
10+
return (
11+
<div>
12+
<h1>Product {slug}</h1>
13+
<p>
14+
This page uses getStaticProps() and getStaticPaths() to pre-fetch a Product
15+
<span data-testid="date-now">{time}</span>
16+
</p>
17+
</div>
18+
)
19+
}
20+
21+
export async function getStaticProps({ params }) {
22+
return {
23+
props: {
24+
time: new Date().toISOString(),
25+
slug: params.slug,
26+
},
27+
}
28+
}
29+
30+
export const getStaticPaths = () => {
31+
return {
32+
paths: [
33+
{
34+
params: {
35+
slug: 'prerendered',
36+
},
37+
},
38+
],
39+
fallback: true,
40+
}
41+
}
42+
43+
export default Product
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useRouter } from 'next/router'
2+
3+
const Product = ({ time, slug }) => {
4+
const router = useRouter()
5+
6+
if (router.isFallback) {
7+
return <span data-testid="loading">Loading...</span>
8+
}
9+
10+
return (
11+
<div>
12+
<h1>Product {slug}</h1>
13+
<p>
14+
This page uses getStaticProps() and getStaticPaths() to pre-fetch a Product
15+
<span data-testid="date-now">{time}</span>
16+
</p>
17+
</div>
18+
)
19+
}
20+
21+
export async function getStaticProps({ params }) {
22+
return {
23+
props: {
24+
time: new Date().toISOString(),
25+
slug: params.slug,
26+
},
27+
}
28+
}
29+
30+
export const getStaticPaths = () => {
31+
return {
32+
paths: [
33+
{
34+
params: {
35+
slug: 'prerendered',
36+
},
37+
},
38+
],
39+
fallback: true,
40+
}
41+
}
42+
43+
export default Product

tests/integration/cache-handler.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ describe('page router', () => {
4343
// check if the blob entries where successful set on the build plugin
4444
const blobEntries = await getBlobEntries(ctx)
4545
expect(blobEntries.map(({ key }) => decodeBlobKey(key.substring(0, 50))).sort()).toEqual([
46+
'/fallback-true/[slug]',
47+
'/fallback-true/prerendered',
4648
// the real key is much longer and ends in a hash, but we only assert on the first 50 chars to make it easier
4749
'/products/an-incredibly-long-product-',
4850
'/products/prerendered',
@@ -53,6 +55,7 @@ describe('page router', () => {
5355
'/static/revalidate-slow-data',
5456
'404.html',
5557
'500.html',
58+
'fallback-true/[slug].html',
5659
'static/fully-static.html',
5760
])
5861

0 commit comments

Comments
 (0)