@@ -2,25 +2,35 @@ 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, serverComponents } ) => {
5
+ test ( 'revalidatePath' , async ( { page, pollUntilHeadersMatch , serverComponents } ) => {
6
6
// in case there is retry or some other test did hit that path before
7
7
// we want to make sure that cdn cache is not warmed up
8
8
const purgeCdnCache = await page . goto (
9
9
new URL ( '/api/purge-cdn?path=/static-fetch/1' , serverComponents . url ) . href ,
10
10
)
11
11
expect ( purgeCdnCache ?. status ( ) ) . toBe ( 200 )
12
12
13
- const response1 = await page . goto ( new URL ( 'static-fetch/1' , serverComponents . url ) . href )
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
+ {
19
+ headersToMatch : {
20
+ // either first time hitting this route or we invalidated
21
+ // 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
24
+ // 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] ,
26
+ } ,
27
+ headersNotMatchedMessage :
28
+ 'First request to tested page should be a miss or stale on the Edge and hit in Next.js' ,
29
+ } ,
30
+ )
14
31
const headers1 = response1 ?. headers ( ) || { }
15
32
expect ( response1 ?. status ( ) ) . toBe ( 200 )
16
33
expect ( headers1 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
17
- // either first time hitting this route or we invalidated
18
- // just CDN node in earlier step
19
- // we will invoke function and see Next cache hit status \
20
- // in the response because it was prerendered at build time
21
- // or regenerated in previous attempt to run this test
22
- expect ( headers1 [ 'cache-status' ] ) . toMatch ( / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m)
23
- expect ( headers1 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
24
34
expect ( headers1 [ 'netlify-cdn-cache-control' ] ) . toBe (
25
35
's-maxage=31536000, stale-while-revalidate=31536000' ,
26
36
)
@@ -30,16 +40,24 @@ test.describe('app router on-demand revalidation', () => {
30
40
const h1 = await page . textContent ( 'h1' )
31
41
expect ( h1 ) . toBe ( 'Hello, Statically fetched show 1' )
32
42
33
- const response2 = await page . goto ( new URL ( 'static-fetch/1' , serverComponents . url ) . href )
43
+ const response2 = await pollUntilHeadersMatch (
44
+ new URL ( 'static-fetch/1' , serverComponents . url ) . href ,
45
+ {
46
+ headersToMatch : {
47
+ // we are hitting the same page again and we most likely will see
48
+ // CDN hit (in this case Next reported cache status is omitted
49
+ // as it didn't actually take place in handling this request)
50
+ // or we will see CDN miss because different CDN node handled request
51
+ '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,
52
+ } ,
53
+ headersNotMatchedMessage :
54
+ 'Second request to tested page should most likely be a hit on the Edge (optionally miss or stale if different CDN node)' ,
55
+ } ,
56
+ )
34
57
const headers2 = response2 ?. headers ( ) || { }
35
58
expect ( response2 ?. status ( ) ) . toBe ( 200 )
36
59
expect ( headers2 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
37
- // we are hitting the same page again and we most likely will see
38
- // CDN hit (in this case Next reported cache status is omitted
39
- // as it didn't actually take place in handling this request)
40
- // or we will see CDN miss because different CDN node handled request
41
- expect ( headers2 [ 'cache-status' ] ) . toMatch ( / " 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)
42
- if ( ! headers2 [ 'cache-status' ] . includes ( 'hit' ) ) {
60
+ if ( ! headers2 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
43
61
// if we missed CDN cache, we will see Next cache hit status
44
62
// as we reuse cached response
45
63
expect ( headers2 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
@@ -57,18 +75,28 @@ test.describe('app router on-demand revalidation', () => {
57
75
)
58
76
expect ( revalidate ?. status ( ) ) . toBe ( 200 )
59
77
78
+ // wait a bit until cdn tags and invalidated and cdn is purged
79
+ await page . waitForTimeout ( 500 )
80
+
60
81
// now after the revalidation it should have a different date
61
- const response3 = await page . goto ( new URL ( 'static-fetch/1' , serverComponents . url ) . href )
82
+ const response3 = await pollUntilHeadersMatch (
83
+ new URL ( 'static-fetch/1' , serverComponents . url ) . href ,
84
+ {
85
+ headersToMatch : {
86
+ // revalidatePath just marks the page(s) as invalid and does NOT
87
+ // automatically refreshes the cache. This request will cause
88
+ // Next.js cache miss and new response will be generated and cached
89
+ // Depending if we hit same CDN node as previous request, we might
90
+ // get either fwd=miss or fwd=stale
91
+ '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] ,
92
+ } ,
93
+ headersNotMatchedMessage :
94
+ '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
+ )
62
97
const headers3 = response3 ?. headers ( ) || { }
63
98
expect ( response3 ?. status ( ) ) . toBe ( 200 )
64
99
expect ( headers3 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
65
- // revalidatePath just marks the page(s) as invalid and does NOT
66
- // automatically refreshes the cache. This request will cause
67
- // Next.js cache miss and new response will be generated and cached
68
- // Depending if we hit same CDN node as previous request, we might
69
- // get either fwd=miss or fwd=stale
70
- expect ( headers3 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; f w d = m i s s / m)
71
- expect ( headers3 [ 'cache-status' ] ) . toMatch ( / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m)
72
100
expect ( headers3 [ 'netlify-cdn-cache-control' ] ) . toBe (
73
101
's-maxage=31536000, stale-while-revalidate=31536000' ,
74
102
)
@@ -77,16 +105,24 @@ test.describe('app router on-demand revalidation', () => {
77
105
const date3 = await page . textContent ( '[data-testid="date-now"]' )
78
106
expect ( date3 ) . not . toBe ( date2 )
79
107
80
- const response4 = await page . goto ( new URL ( 'static-fetch/1' , serverComponents . url ) . href )
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' : / " 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,
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
+ )
81
122
const headers4 = response4 ?. headers ( ) || { }
82
123
expect ( response4 ?. status ( ) ) . toBe ( 200 )
83
124
expect ( headers4 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
84
- // we are hitting the same page again and we most likely will see
85
- // CDN hit (in this case Next reported cache status is omitted
86
- // as it didn't actually take place in handling this request)
87
- // or we will see CDN miss because different CDN node handled request
88
- expect ( headers4 [ 'cache-status' ] ) . toMatch ( / " 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)
89
- if ( ! headers4 [ 'cache-status' ] . includes ( 'hit' ) ) {
125
+ if ( ! headers4 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
90
126
// if we missed CDN cache, we will see Next cache hit status
91
127
// as we reuse cached response
92
128
expect ( headers4 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
@@ -100,25 +136,35 @@ test.describe('app router on-demand revalidation', () => {
100
136
expect ( date4 ) . toBe ( date3 )
101
137
} )
102
138
103
- test ( 'revalidateTag' , async ( { page, serverComponents } ) => {
139
+ test ( 'revalidateTag' , async ( { page, pollUntilHeadersMatch , serverComponents } ) => {
104
140
// in case there is retry or some other test did hit that path before
105
141
// we want to make sure that cdn cache is not warmed up
106
142
const purgeCdnCache = await page . goto (
107
143
new URL ( '/api/purge-cdn?path=/static-fetch-1' , serverComponents . url ) . href ,
108
144
)
109
145
expect ( purgeCdnCache ?. status ( ) ) . toBe ( 200 )
110
146
111
- const response1 = await page . goto ( new URL ( 'static-fetch-1' , serverComponents . url ) . href )
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
+ )
112
165
const headers1 = response1 ?. headers ( ) || { }
113
166
expect ( response1 ?. status ( ) ) . toBe ( 200 )
114
167
expect ( headers1 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
115
- // either first time hitting this route or we invalidated
116
- // just CDN node in earlier step
117
- // we will invoke function and see Next cache hit status \
118
- // in the response because it was prerendered at build time
119
- // or regenerated in previous attempt to run this test
120
- expect ( headers1 [ 'cache-status' ] ) . toMatch ( / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m)
121
- expect ( headers1 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
122
168
expect ( headers1 [ 'netlify-cdn-cache-control' ] ) . toBe (
123
169
's-maxage=31536000, stale-while-revalidate=31536000' ,
124
170
)
@@ -128,16 +174,24 @@ test.describe('app router on-demand revalidation', () => {
128
174
const h1 = await page . textContent ( 'h1' )
129
175
expect ( h1 ) . toBe ( 'Hello, Static Fetch 1' )
130
176
131
- const response2 = await page . goto ( new URL ( 'static-fetch-1' , serverComponents . url ) . href )
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
+ )
132
191
const headers2 = response2 ?. headers ( ) || { }
133
192
expect ( response2 ?. status ( ) ) . toBe ( 200 )
134
193
expect ( headers2 [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
135
- // we are hitting the same page again and we most likely will see
136
- // CDN hit (in this case Next reported cache status is omitted
137
- // as it didn't actually take place in handling this request)
138
- // or we will see CDN miss because different CDN node handled request
139
- expect ( headers2 [ 'cache-status' ] ) . toMatch ( / " 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)
140
- if ( ! headers2 [ 'cache-status' ] . includes ( 'hit' ) ) {
194
+ if ( ! headers2 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
141
195
// if we missed CDN cache, we will see Next cache hit status
142
196
// as we reuse cached response
143
197
expect ( headers2 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
@@ -155,18 +209,28 @@ test.describe('app router on-demand revalidation', () => {
155
209
)
156
210
expect ( revalidate ?. status ( ) ) . toBe ( 200 )
157
211
212
+ // wait a bit until cdn tags and invalidated and cdn is purged
213
+ await page . waitForTimeout ( 500 )
214
+
158
215
// now after the revalidation it should have a different date
159
- const response3 = await page . goto ( new URL ( 'static-fetch-1' , serverComponents . url ) . href )
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
+ )
160
231
const headers3 = response3 ?. headers ( ) || { }
161
232
expect ( response3 ?. status ( ) ) . toBe ( 200 )
162
233
expect ( headers3 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
163
- // revalidateTag just marks the page(s) as invalid and does NOT
164
- // automatically refreshes the cache. This request will cause
165
- // Next.js cache miss and new response will be generated and cached
166
- // Depending if we hit same CDN node as previous request, we might
167
- // get either fwd=miss or fwd=stale
168
- expect ( headers3 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; f w d = m i s s / m)
169
- expect ( headers3 [ 'cache-status' ] ) . toMatch ( / " N e t l i f y E d g e " ; f w d = ( m i s s | s t a l e ) / m)
170
234
expect ( headers3 [ 'netlify-cdn-cache-control' ] ) . toBe (
171
235
's-maxage=31536000, stale-while-revalidate=31536000' ,
172
236
)
@@ -175,16 +239,25 @@ test.describe('app router on-demand revalidation', () => {
175
239
const date3 = await page . textContent ( '[data-testid="date-now"]' )
176
240
expect ( date3 ) . not . toBe ( date2 )
177
241
178
- const response4 = await page . goto ( new URL ( 'static-fetch-1' , serverComponents . url ) . href )
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
+ )
179
256
const headers4 = response4 ?. headers ( ) || { }
180
257
expect ( response4 ?. status ( ) ) . toBe ( 200 )
181
258
expect ( headers4 ?. [ 'x-nextjs-cache' ] ) . toBeUndefined ( )
182
- // we are hitting the same page again and we most likely will see
183
- // CDN hit (in this case Next reported cache status is omitted
184
- // as it didn't actually take place in handling this request)
185
- // or we will see CDN miss because different CDN node handled request
186
- expect ( headers4 [ 'cache-status' ] ) . toMatch ( / " 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)
187
- if ( ! headers4 [ 'cache-status' ] . includes ( 'hit' ) ) {
259
+
260
+ if ( ! headers4 [ 'cache-status' ] . includes ( '"Netlify Edge"; hit' ) ) {
188
261
// if we missed CDN cache, we will see Next cache hit status
189
262
// as we reuse cached response
190
263
expect ( headers4 [ 'cache-status' ] ) . toMatch ( / " N e x t .j s " ; h i t / m)
0 commit comments