Skip to content

Commit 8a260c5

Browse files
committed
test: refactor app router on-demand revalidation test and add test cases for not prerendered content
1 parent f4eeaa2 commit 8a260c5

File tree

4 files changed

+127
-232
lines changed

4 files changed

+127
-232
lines changed

tests/e2e/on-demand-app.test.ts

+114-229
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,75 @@ import { expect } from '@playwright/test'
22
import { test } from '../utils/playwright-helpers.js'
33

44
test.describe('app router on-demand revalidation', () => {
5-
test('revalidatePath', async ({ page, pollUntilHeadersMatch, serverComponents }) => {
6-
// in case there is retry or some other test did hit that path before
7-
// we want to make sure that cdn cache is not warmed up
8-
const purgeCdnCache = await page.goto(
9-
new URL('/api/purge-cdn?path=/static-fetch/1', serverComponents.url).href,
10-
)
11-
expect(purgeCdnCache?.status()).toBe(200)
12-
13-
// wait a bit until cdn cache purge propagates
14-
await page.waitForTimeout(500)
15-
16-
const response1 = await pollUntilHeadersMatch(
17-
new URL('static-fetch/1', serverComponents.url).href,
18-
{
5+
for (const { label, prerendered, pagePath, revalidateApiPath, expectedH1Content } of [
6+
{
7+
label: 'revalidatePath (prerendered page)',
8+
prerendered: true,
9+
pagePath: '/static-fetch/1',
10+
revalidateApiPath: '/api/on-demand-revalidate/path?path=/static-fetch/1',
11+
expectedH1Content: 'Hello, Statically fetched show 1',
12+
},
13+
{
14+
label: 'revalidateTag (prerendered page)',
15+
prerendered: true,
16+
pagePath: '/static-fetch/2',
17+
revalidateApiPath: '/api/on-demand-revalidate/tag?tag=show-2',
18+
expectedH1Content: 'Hello, Statically fetched show 2',
19+
},
20+
{
21+
label: 'revalidatePath (not prerendered page)',
22+
prerendered: false,
23+
pagePath: '/static-fetch/3',
24+
revalidateApiPath: '/api/on-demand-revalidate/path?path=/static-fetch/3',
25+
expectedH1Content: 'Hello, Statically fetched show 3',
26+
},
27+
{
28+
label: 'revalidateTag (not prerendered page)',
29+
prerendered: false,
30+
pagePath: '/static-fetch/4',
31+
revalidateApiPath: '/api/on-demand-revalidate/tag?tag=show-4',
32+
expectedH1Content: 'Hello, Statically fetched show 4',
33+
},
34+
]) {
35+
test(label, async ({ page, pollUntilHeadersMatch, serverComponents }) => {
36+
// in case there is retry or some other test did hit that path before
37+
// we want to make sure that cdn cache is not warmed up
38+
const purgeCdnCache = await page.goto(
39+
new URL(`/api/purge-cdn?path=${pagePath}`, serverComponents.url).href,
40+
)
41+
expect(purgeCdnCache?.status()).toBe(200)
42+
43+
// wait a bit until cdn cache purge propagates
44+
await page.waitForTimeout(500)
45+
46+
const response1 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, {
1947
headersToMatch: {
2048
// either first time hitting this route or we invalidated
2149
// just CDN node in earlier step
2250
// we will invoke function and see Next cache hit status \
23-
// in the response because it was prerendered at build time
51+
// in the response if it was prerendered at build time
2452
// or regenerated in previous attempt to run this test
25-
'cache-status': [/"Netlify Edge"; fwd=(miss|stale)/m, /"Next.js"; hit/m],
53+
'cache-status': [
54+
/"Netlify Edge"; fwd=(miss|stale)/m,
55+
prerendered ? /"Next.js"; hit/m : /"Next.js"; (hit|fwd=miss)/m,
56+
],
2657
},
2758
headersNotMatchedMessage:
2859
'First request to tested page should be a miss or stale on the Edge and hit in Next.js',
29-
},
30-
)
31-
const headers1 = response1?.headers() || {}
32-
expect(response1?.status()).toBe(200)
33-
expect(headers1['x-nextjs-cache']).toBeUndefined()
34-
expect(headers1['netlify-cdn-cache-control']).toBe(
35-
's-maxage=31536000, stale-while-revalidate=31536000',
36-
)
60+
})
61+
const headers1 = response1?.headers() || {}
62+
expect(response1?.status()).toBe(200)
63+
expect(headers1['x-nextjs-cache']).toBeUndefined()
64+
expect(headers1['netlify-cdn-cache-control']).toBe(
65+
's-maxage=31536000, stale-while-revalidate=31536000',
66+
)
3767

38-
const date1 = await page.textContent('[data-testid="date-now"]')
68+
const date1 = await page.textContent('[data-testid="date-now"]')
3969

40-
const h1 = await page.textContent('h1')
41-
expect(h1).toBe('Hello, Statically fetched show 1')
70+
const h1 = await page.textContent('h1')
71+
expect(h1).toBe(expectedH1Content)
4272

43-
const response2 = await pollUntilHeadersMatch(
44-
new URL('static-fetch/1', serverComponents.url).href,
45-
{
73+
const response2 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, {
4674
headersToMatch: {
4775
// we are hitting the same page again and we most likely will see
4876
// CDN hit (in this case Next reported cache status is omitted
@@ -52,36 +80,31 @@ test.describe('app router on-demand revalidation', () => {
5280
},
5381
headersNotMatchedMessage:
5482
'Second request to tested page should most likely be a hit on the Edge (optionally miss or stale if different CDN node)',
55-
},
56-
)
57-
const headers2 = response2?.headers() || {}
58-
expect(response2?.status()).toBe(200)
59-
expect(headers2['x-nextjs-cache']).toBeUndefined()
60-
if (!headers2['cache-status'].includes('"Netlify Edge"; hit')) {
61-
// if we missed CDN cache, we will see Next cache hit status
62-
// as we reuse cached response
63-
expect(headers2['cache-status']).toMatch(/"Next.js"; hit/m)
64-
}
65-
expect(headers2['netlify-cdn-cache-control']).toBe(
66-
's-maxage=31536000, stale-while-revalidate=31536000',
67-
)
68-
69-
// the page is cached
70-
const date2 = await page.textContent('[data-testid="date-now"]')
71-
expect(date2).toBe(date1)
72-
73-
const revalidate = await page.goto(
74-
new URL('/api/on-demand-revalidate/path', serverComponents.url).href,
75-
)
76-
expect(revalidate?.status()).toBe(200)
77-
78-
// wait a bit until cdn tags and invalidated and cdn is purged
79-
await page.waitForTimeout(500)
80-
81-
// now after the revalidation it should have a different date
82-
const response3 = await pollUntilHeadersMatch(
83-
new URL('static-fetch/1', serverComponents.url).href,
84-
{
83+
})
84+
const headers2 = response2?.headers() || {}
85+
expect(response2?.status()).toBe(200)
86+
expect(headers2['x-nextjs-cache']).toBeUndefined()
87+
if (!headers2['cache-status'].includes('"Netlify Edge"; hit')) {
88+
// if we missed CDN cache, we will see Next cache hit status
89+
// as we reuse cached response
90+
expect(headers2['cache-status']).toMatch(/"Next.js"; hit/m)
91+
}
92+
expect(headers2['netlify-cdn-cache-control']).toBe(
93+
's-maxage=31536000, stale-while-revalidate=31536000',
94+
)
95+
96+
// the page is cached
97+
const date2 = await page.textContent('[data-testid="date-now"]')
98+
expect(date2).toBe(date1)
99+
100+
const revalidate = await page.goto(new URL(revalidateApiPath, serverComponents.url).href)
101+
expect(revalidate?.status()).toBe(200)
102+
103+
// wait a bit until cdn tags and invalidated and cdn is purged
104+
await page.waitForTimeout(500)
105+
106+
// now after the revalidation it should have a different date
107+
const response3 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, {
85108
headersToMatch: {
86109
// revalidatePath just marks the page(s) as invalid and does NOT
87110
// automatically refreshes the cache. This request will cause
@@ -92,156 +115,19 @@ test.describe('app router on-demand revalidation', () => {
92115
},
93116
headersNotMatchedMessage:
94117
'Third request to tested page should be a miss or stale on the Edge and miss in Next.js after on-demand revalidation',
95-
},
96-
)
97-
const headers3 = response3?.headers() || {}
98-
expect(response3?.status()).toBe(200)
99-
expect(headers3?.['x-nextjs-cache']).toBeUndefined()
100-
expect(headers3['netlify-cdn-cache-control']).toBe(
101-
's-maxage=31536000, stale-while-revalidate=31536000',
102-
)
103-
104-
// the page has now an updated date
105-
const date3 = await page.textContent('[data-testid="date-now"]')
106-
expect(date3).not.toBe(date2)
107-
108-
const response4 = await pollUntilHeadersMatch(
109-
new URL('static-fetch/1', serverComponents.url).href,
110-
{
111-
headersToMatch: {
112-
// we are hitting the same page again and we most likely will see
113-
// CDN hit (in this case Next reported cache status is omitted
114-
// as it didn't actually take place in handling this request)
115-
// or we will see CDN miss because different CDN node handled request
116-
'cache-status': /"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m,
117-
},
118-
headersNotMatchedMessage:
119-
'Fourth request to tested page should most likely be a hit on the Edge (optionally miss or stale if different CDN node)',
120-
},
121-
)
122-
const headers4 = response4?.headers() || {}
123-
expect(response4?.status()).toBe(200)
124-
expect(headers4?.['x-nextjs-cache']).toBeUndefined()
125-
if (!headers4['cache-status'].includes('"Netlify Edge"; hit')) {
126-
// if we missed CDN cache, we will see Next cache hit status
127-
// as we reuse cached response
128-
expect(headers4['cache-status']).toMatch(/"Next.js"; hit/m)
129-
}
130-
expect(headers4['netlify-cdn-cache-control']).toBe(
131-
's-maxage=31536000, stale-while-revalidate=31536000',
132-
)
133-
134-
// the page is cached
135-
const date4 = await page.textContent('[data-testid="date-now"]')
136-
expect(date4).toBe(date3)
137-
})
138-
139-
test('revalidateTag', async ({ page, pollUntilHeadersMatch, serverComponents }) => {
140-
// in case there is retry or some other test did hit that path before
141-
// we want to make sure that cdn cache is not warmed up
142-
const purgeCdnCache = await page.goto(
143-
new URL('/api/purge-cdn?path=/static-fetch-1', serverComponents.url).href,
144-
)
145-
expect(purgeCdnCache?.status()).toBe(200)
146-
147-
// wait a bit until cdn cache purge propagates
148-
await page.waitForTimeout(500)
149-
150-
const response1 = await pollUntilHeadersMatch(
151-
new URL('static-fetch-1', serverComponents.url).href,
152-
{
153-
headersToMatch: {
154-
// either first time hitting this route or we invalidated
155-
// just CDN node in earlier step
156-
// we will invoke function and see Next cache hit status \
157-
// in the response because it was prerendered at build time
158-
// or regenerated in previous attempt to run this test
159-
'cache-status': [/"Netlify Edge"; fwd=(miss|stale)/m, /"Next.js"; hit/m],
160-
},
161-
headersNotMatchedMessage:
162-
'First request to tested page should be a miss or stale on the Edge and hit in Next.js',
163-
},
164-
)
165-
const headers1 = response1?.headers() || {}
166-
expect(response1?.status()).toBe(200)
167-
expect(headers1['x-nextjs-cache']).toBeUndefined()
168-
expect(headers1['netlify-cdn-cache-control']).toBe(
169-
's-maxage=31536000, stale-while-revalidate=31536000',
170-
)
171-
172-
const date1 = await page.textContent('[data-testid="date-now"]')
173-
174-
const h1 = await page.textContent('h1')
175-
expect(h1).toBe('Hello, Static Fetch 1')
176-
177-
const response2 = await pollUntilHeadersMatch(
178-
new URL('static-fetch-1', serverComponents.url).href,
179-
{
180-
headersToMatch: {
181-
// we are hitting the same page again and we most likely will see
182-
// CDN hit (in this case Next reported cache status is omitted
183-
// as it didn't actually take place in handling this request)
184-
// or we will see CDN miss because different CDN node handled request
185-
'cache-status': /"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m,
186-
},
187-
headersNotMatchedMessage:
188-
'Second request to tested page should most likely be a hit on the Edge (optionally miss or stale if different CDN node)',
189-
},
190-
)
191-
const headers2 = response2?.headers() || {}
192-
expect(response2?.status()).toBe(200)
193-
expect(headers2['x-nextjs-cache']).toBeUndefined()
194-
if (!headers2['cache-status'].includes('"Netlify Edge"; hit')) {
195-
// if we missed CDN cache, we will see Next cache hit status
196-
// as we reuse cached response
197-
expect(headers2['cache-status']).toMatch(/"Next.js"; hit/m)
198-
}
199-
expect(headers2['netlify-cdn-cache-control']).toBe(
200-
's-maxage=31536000, stale-while-revalidate=31536000',
201-
)
202-
203-
// the page is cached
204-
const date2 = await page.textContent('[data-testid="date-now"]')
205-
expect(date2).toBe(date1)
206-
207-
const revalidate = await page.goto(
208-
new URL('/api/on-demand-revalidate/tag', serverComponents.url).href,
209-
)
210-
expect(revalidate?.status()).toBe(200)
211-
212-
// wait a bit until cdn tags and invalidated and cdn is purged
213-
await page.waitForTimeout(500)
214-
215-
// now after the revalidation it should have a different date
216-
const response3 = await pollUntilHeadersMatch(
217-
new URL('static-fetch-1', serverComponents.url).href,
218-
{
219-
headersToMatch: {
220-
// revalidateTag just marks the page(s) as invalid and does NOT
221-
// automatically refreshes the cache. This request will cause
222-
// Next.js cache miss and new response will be generated and cached
223-
// Depending if we hit same CDN node as previous request, we might
224-
// get either fwd=miss or fwd=stale
225-
'cache-status': [/"Next.js"; fwd=miss/m, /"Netlify Edge"; fwd=(miss|stale)/m],
226-
},
227-
headersNotMatchedMessage:
228-
'Third request to tested page should be a miss or stale on the Edge and miss in Next.js after on-demand revalidation',
229-
},
230-
)
231-
const headers3 = response3?.headers() || {}
232-
expect(response3?.status()).toBe(200)
233-
expect(headers3?.['x-nextjs-cache']).toBeUndefined()
234-
expect(headers3['netlify-cdn-cache-control']).toBe(
235-
's-maxage=31536000, stale-while-revalidate=31536000',
236-
)
237-
238-
// the page has now an updated date
239-
const date3 = await page.textContent('[data-testid="date-now"]')
240-
expect(date3).not.toBe(date2)
241-
242-
const response4 = await pollUntilHeadersMatch(
243-
new URL('static-fetch-1', serverComponents.url).href,
244-
{
118+
})
119+
const headers3 = response3?.headers() || {}
120+
expect(response3?.status()).toBe(200)
121+
expect(headers3?.['x-nextjs-cache']).toBeUndefined()
122+
expect(headers3['netlify-cdn-cache-control']).toBe(
123+
's-maxage=31536000, stale-while-revalidate=31536000',
124+
)
125+
126+
// the page has now an updated date
127+
const date3 = await page.textContent('[data-testid="date-now"]')
128+
expect(date3).not.toBe(date2)
129+
130+
const response4 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, {
245131
headersToMatch: {
246132
// we are hitting the same page again and we most likely will see
247133
// CDN hit (in this case Next reported cache status is omitted
@@ -251,23 +137,22 @@ test.describe('app router on-demand revalidation', () => {
251137
},
252138
headersNotMatchedMessage:
253139
'Fourth request to tested page should most likely be a hit on the Edge (optionally miss or stale if different CDN node)',
254-
},
255-
)
256-
const headers4 = response4?.headers() || {}
257-
expect(response4?.status()).toBe(200)
258-
expect(headers4?.['x-nextjs-cache']).toBeUndefined()
259-
260-
if (!headers4['cache-status'].includes('"Netlify Edge"; hit')) {
261-
// if we missed CDN cache, we will see Next cache hit status
262-
// as we reuse cached response
263-
expect(headers4['cache-status']).toMatch(/"Next.js"; hit/m)
264-
}
265-
expect(headers4['netlify-cdn-cache-control']).toBe(
266-
's-maxage=31536000, stale-while-revalidate=31536000',
267-
)
268-
269-
// the page is cached
270-
const date4 = await page.textContent('[data-testid="date-now"]')
271-
expect(date4).toBe(date3)
272-
})
140+
})
141+
const headers4 = response4?.headers() || {}
142+
expect(response4?.status()).toBe(200)
143+
expect(headers4?.['x-nextjs-cache']).toBeUndefined()
144+
if (!headers4['cache-status'].includes('"Netlify Edge"; hit')) {
145+
// if we missed CDN cache, we will see Next cache hit status
146+
// as we reuse cached response
147+
expect(headers4['cache-status']).toMatch(/"Next.js"; hit/m)
148+
}
149+
expect(headers4['netlify-cdn-cache-control']).toBe(
150+
's-maxage=31536000, stale-while-revalidate=31536000',
151+
)
152+
153+
// the page is cached
154+
const date4 = await page.textContent('[data-testid="date-now"]')
155+
expect(date4).toBe(date3)
156+
})
157+
}
273158
})

tests/fixtures/server-components/app/api/on-demand-revalidate/path/route.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { NextRequest, NextResponse } from 'next/server'
22
import { revalidatePath } from 'next/cache'
33

44
export async function GET(request: NextRequest) {
5-
revalidatePath('/static-fetch/[id]', 'page')
5+
const url = new URL(request.url)
6+
const pathToRevalidate = url.searchParams.get('path') ?? '/static-fetch/[id]/page'
7+
8+
revalidatePath(pathToRevalidate)
69
return NextResponse.json({ revalidated: true, now: new Date().toISOString() })
710
}
811

0 commit comments

Comments
 (0)