@@ -2,47 +2,89 @@ import { expect } from '@playwright/test'
2
2
import { test } from '../utils/playwright-helpers.js'
3
3
4
4
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 with static path)' ,
8
+ prerendered : true ,
9
+ pagePath : '/static-fetch-1' ,
10
+ revalidateApiPath : '/api/on-demand-revalidate/path?path=/static-fetch-1' ,
11
+ expectedH1Content : 'Hello, Static Fetch 1' ,
12
+ } ,
13
+ {
14
+ label : 'revalidateTag (prerendered page with static path)' ,
15
+ prerendered : true ,
16
+ pagePath : '/static-fetch-2' ,
17
+ revalidateApiPath : '/api/on-demand-revalidate/tag?tag=collection' ,
18
+ expectedH1Content : 'Hello, Static Fetch 2' ,
19
+ } ,
20
+ {
21
+ label : 'revalidatePath (prerendered page with dynamic path)' ,
22
+ prerendered : true ,
23
+ pagePath : '/static-fetch/1' ,
24
+ revalidateApiPath : '/api/on-demand-revalidate/path?path=/static-fetch/1' ,
25
+ expectedH1Content : 'Hello, Statically fetched show 1' ,
26
+ } ,
27
+ {
28
+ label : 'revalidateTag (prerendered page with dynamic path)' ,
29
+ prerendered : true ,
30
+ pagePath : '/static-fetch/2' ,
31
+ revalidateApiPath : '/api/on-demand-revalidate/tag?tag=show-2' ,
32
+ expectedH1Content : 'Hello, Statically fetched show 2' ,
33
+ } ,
34
+ {
35
+ label : 'revalidatePath (not prerendered page with dynamic path)' ,
36
+ prerendered : false ,
37
+ pagePath : '/static-fetch/3' ,
38
+ revalidateApiPath : '/api/on-demand-revalidate/path?path=/static-fetch/3' ,
39
+ expectedH1Content : 'Hello, Statically fetched show 3' ,
40
+ } ,
41
+ {
42
+ label : 'revalidateTag (not prerendered page with dynamic path)' ,
43
+ prerendered : false ,
44
+ pagePath : '/static-fetch/4' ,
45
+ revalidateApiPath : '/api/on-demand-revalidate/tag?tag=show-4' ,
46
+ expectedH1Content : 'Hello, Statically fetched show 4' ,
47
+ } ,
48
+ ] ) {
49
+ test ( label , async ( { page, pollUntilHeadersMatch, serverComponents } ) => {
50
+ // in case there is retry or some other test did hit that path before
51
+ // we want to make sure that cdn cache is not warmed up
52
+ const purgeCdnCache = await page . goto (
53
+ new URL ( `/api/purge-cdn?path=${ pagePath } ` , serverComponents . url ) . href ,
54
+ )
55
+ expect ( purgeCdnCache ?. status ( ) ) . toBe ( 200 )
56
+
57
+ // wait a bit until cdn cache purge propagates
58
+ await page . waitForTimeout ( 500 )
59
+
60
+ const response1 = await pollUntilHeadersMatch ( new URL ( pagePath , serverComponents . url ) . href , {
19
61
headersToMatch : {
20
62
// either first time hitting this route or we invalidated
21
63
// just CDN node in earlier step
22
- // we will invoke function and see Next cache hit status \
23
- // in the response because it was prerendered at build time
64
+ // we will invoke function and see Next cache hit status
65
+ // in the response if it was prerendered at build time
24
66
// or regenerated in previous attempt to run this test
25
- 'cache-status' : [ / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m, / " N e x t .j s " ; h i t / m] ,
67
+ 'cache-status' : [
68
+ / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m,
69
+ prerendered ? / " N e x t .j s " ; h i t / m : / " N e x t .j s " ; ( h i t | f w d = m i s s ) / m,
70
+ ] ,
26
71
} ,
27
72
headersNotMatchedMessage :
28
73
'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
- )
74
+ } )
75
+ const headers1 = response1 ?. headers ( ) || { }
76
+ expect ( response1 ?. status ( ) ) . toBe ( 200 )
77
+ expect ( headers1 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
78
+ expect ( headers1 [ 'netlify-cdn-cache-control' ] ) . toBe (
79
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
80
+ )
37
81
38
- const date1 = await page . textContent ( '[data-testid="date-now"]' )
82
+ const date1 = await page . textContent ( '[data-testid="date-now"]' )
39
83
40
- const h1 = await page . textContent ( 'h1' )
41
- expect ( h1 ) . toBe ( 'Hello, Statically fetched show 1' )
84
+ const h1 = await page . textContent ( 'h1' )
85
+ expect ( h1 ) . toBe ( expectedH1Content )
42
86
43
- const response2 = await pollUntilHeadersMatch (
44
- new URL ( 'static-fetch/1' , serverComponents . url ) . href ,
45
- {
87
+ const response2 = await pollUntilHeadersMatch ( new URL ( pagePath , serverComponents . url ) . href , {
46
88
headersToMatch : {
47
89
// we are hitting the same page again and we most likely will see
48
90
// CDN hit (in this case Next reported cache status is omitted
@@ -52,36 +94,31 @@ test.describe('app router on-demand revalidation', () => {
52
94
} ,
53
95
headersNotMatchedMessage :
54
96
'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 ( / " N e x t .j s " ; h i t / 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
- {
97
+ } )
98
+ const headers2 = response2 ?. headers ( ) || { }
99
+ expect ( response2 ?. status ( ) ) . toBe ( 200 )
100
+ expect ( headers2 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
101
+ if ( ! headers2 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
102
+ // if we missed CDN cache, we will see Next cache hit status
103
+ // as we reuse cached response
104
+ expect ( headers2 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
105
+ }
106
+ expect ( headers2 [ 'netlify-cdn-cache-control' ] ) . toBe (
107
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
108
+ )
109
+
110
+ // the page is cached
111
+ const date2 = await page . textContent ( '[data-testid="date-now"]' )
112
+ expect ( date2 ) . toBe ( date1 )
113
+
114
+ const revalidate = await page . goto ( new URL ( revalidateApiPath , serverComponents . url ) . href )
115
+ expect ( revalidate ?. status ( ) ) . toBe ( 200 )
116
+
117
+ // wait a bit until cdn tags and invalidated and cdn is purged
118
+ await page . waitForTimeout ( 500 )
119
+
120
+ // now after the revalidation it should have a different date
121
+ const response3 = await pollUntilHeadersMatch ( new URL ( pagePath , serverComponents . url ) . href , {
85
122
headersToMatch : {
86
123
// revalidatePath just marks the page(s) as invalid and does NOT
87
124
// automatically refreshes the cache. This request will cause
@@ -92,22 +129,19 @@ test.describe('app router on-demand revalidation', () => {
92
129
} ,
93
130
headersNotMatchedMessage :
94
131
'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
- {
132
+ } )
133
+ const headers3 = response3 ?. headers ( ) || { }
134
+ expect ( response3 ?. status ( ) ) . toBe ( 200 )
135
+ expect ( headers3 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
136
+ expect ( headers3 [ 'netlify-cdn-cache-control' ] ) . toBe (
137
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
138
+ )
139
+
140
+ // the page has now an updated date
141
+ const date3 = await page . textContent ( '[data-testid="date-now"]' )
142
+ expect ( date3 ) . not . toBe ( date2 )
143
+
144
+ const response4 = await pollUntilHeadersMatch ( new URL ( pagePath , serverComponents . url ) . href , {
111
145
headersToMatch : {
112
146
// we are hitting the same page again and we most likely will see
113
147
// CDN hit (in this case Next reported cache status is omitted
@@ -117,157 +151,22 @@ test.describe('app router on-demand revalidation', () => {
117
151
} ,
118
152
headersNotMatchedMessage :
119
153
'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 ( / " N e x t .j s " ; h i t / 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' : [ / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m, / " N e x t .j s " ; h i t / 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' : / " N e t l i f y E d g e " ; ( h i t | f w d = m i s s | f w d = s t a l e ) / 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 ( / " N e x t .j s " ; h i t / 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' : [ / " N e x t .j s " ; f w d = m i s s / m, / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / 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
- {
245
- headersToMatch : {
246
- // we are hitting the same page again and we most likely will see
247
- // CDN hit (in this case Next reported cache status is omitted
248
- // as it didn't actually take place in handling this request)
249
- // or we will see CDN miss because different CDN node handled request
250
- 'cache-status' : / " N e t l i f y E d g e " ; ( h i t | f w d = m i s s | f w d = s t a l e ) / m,
251
- } ,
252
- headersNotMatchedMessage :
253
- '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 ( / " N e x t .j s " ; h i t / 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
- } )
154
+ } )
155
+ const headers4 = response4 ?. headers ( ) || { }
156
+ expect ( response4 ?. status ( ) ) . toBe ( 200 )
157
+ expect ( headers4 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
158
+ if ( ! headers4 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
159
+ // if we missed CDN cache, we will see Next cache hit status
160
+ // as we reuse cached response
161
+ expect ( headers4 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
162
+ }
163
+ expect ( headers4 [ 'netlify-cdn-cache-control' ] ) . toBe (
164
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
165
+ )
166
+
167
+ // the page is cached
168
+ const date4 = await page . textContent ( '[data-testid="date-now"]' )
169
+ expect ( date4 ) . toBe ( date3 )
170
+ } )
171
+ }
273
172
} )
0 commit comments