Skip to content

Commit ce97a32

Browse files
authored
test: add some timeouts and poll after on-demand revalidation in case background work taking longer than expected (#335)
1 parent 02b8e69 commit ce97a32

File tree

5 files changed

+627
-312
lines changed

5 files changed

+627
-312
lines changed

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

+135-62
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,35 @@ 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, serverComponents }) => {
5+
test('revalidatePath', async ({ page, pollUntilHeadersMatch, serverComponents }) => {
66
// in case there is retry or some other test did hit that path before
77
// we want to make sure that cdn cache is not warmed up
88
const purgeCdnCache = await page.goto(
99
new URL('/api/purge-cdn?path=/static-fetch/1', serverComponents.url).href,
1010
)
1111
expect(purgeCdnCache?.status()).toBe(200)
1212

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': [/"Netlify Edge"; fwd=(miss|stale)/m, /"Next.js"; hit/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+
)
1431
const headers1 = response1?.headers() || {}
1532
expect(response1?.status()).toBe(200)
1633
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(/"Netlify Edge"; fwd=(miss|stale)/m)
23-
expect(headers1['cache-status']).toMatch(/"Next.js"; hit/m)
2434
expect(headers1['netlify-cdn-cache-control']).toBe(
2535
's-maxage=31536000, stale-while-revalidate=31536000',
2636
)
@@ -30,16 +40,24 @@ test.describe('app router on-demand revalidation', () => {
3040
const h1 = await page.textContent('h1')
3141
expect(h1).toBe('Hello, Statically fetched show 1')
3242

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': /"Netlify Edge"; (hit|fwd=miss|fwd=stale)/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+
)
3457
const headers2 = response2?.headers() || {}
3558
expect(response2?.status()).toBe(200)
3659
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(/"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m)
42-
if (!headers2['cache-status'].includes('hit')) {
60+
if (!headers2['cache-status'].includes('"Netlify Edge"; hit')) {
4361
// if we missed CDN cache, we will see Next cache hit status
4462
// as we reuse cached response
4563
expect(headers2['cache-status']).toMatch(/"Next.js"; hit/m)
@@ -57,18 +75,28 @@ test.describe('app router on-demand revalidation', () => {
5775
)
5876
expect(revalidate?.status()).toBe(200)
5977

78+
// wait a bit until cdn tags and invalidated and cdn is purged
79+
await page.waitForTimeout(500)
80+
6081
// 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': [/"Next.js"; fwd=miss/m, /"Netlify Edge"; fwd=(miss|stale)/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+
)
6297
const headers3 = response3?.headers() || {}
6398
expect(response3?.status()).toBe(200)
6499
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(/"Next.js"; fwd=miss/m)
71-
expect(headers3['cache-status']).toMatch(/"Netlify Edge"; fwd=(miss|stale)/m)
72100
expect(headers3['netlify-cdn-cache-control']).toBe(
73101
's-maxage=31536000, stale-while-revalidate=31536000',
74102
)
@@ -77,16 +105,24 @@ test.describe('app router on-demand revalidation', () => {
77105
const date3 = await page.textContent('[data-testid="date-now"]')
78106
expect(date3).not.toBe(date2)
79107

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': /"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+
)
81122
const headers4 = response4?.headers() || {}
82123
expect(response4?.status()).toBe(200)
83124
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(/"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m)
89-
if (!headers4['cache-status'].includes('hit')) {
125+
if (!headers4['cache-status'].includes('"Netlify Edge"; hit')) {
90126
// if we missed CDN cache, we will see Next cache hit status
91127
// as we reuse cached response
92128
expect(headers4['cache-status']).toMatch(/"Next.js"; hit/m)
@@ -100,25 +136,35 @@ test.describe('app router on-demand revalidation', () => {
100136
expect(date4).toBe(date3)
101137
})
102138

103-
test('revalidateTag', async ({ page, serverComponents }) => {
139+
test('revalidateTag', async ({ page, pollUntilHeadersMatch, serverComponents }) => {
104140
// in case there is retry or some other test did hit that path before
105141
// we want to make sure that cdn cache is not warmed up
106142
const purgeCdnCache = await page.goto(
107143
new URL('/api/purge-cdn?path=/static-fetch-1', serverComponents.url).href,
108144
)
109145
expect(purgeCdnCache?.status()).toBe(200)
110146

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': [/"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+
)
112165
const headers1 = response1?.headers() || {}
113166
expect(response1?.status()).toBe(200)
114167
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(/"Netlify Edge"; fwd=(miss|stale)/m)
121-
expect(headers1['cache-status']).toMatch(/"Next.js"; hit/m)
122168
expect(headers1['netlify-cdn-cache-control']).toBe(
123169
's-maxage=31536000, stale-while-revalidate=31536000',
124170
)
@@ -128,16 +174,24 @@ test.describe('app router on-demand revalidation', () => {
128174
const h1 = await page.textContent('h1')
129175
expect(h1).toBe('Hello, Static Fetch 1')
130176

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': /"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+
)
132191
const headers2 = response2?.headers() || {}
133192
expect(response2?.status()).toBe(200)
134193
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(/"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m)
140-
if (!headers2['cache-status'].includes('hit')) {
194+
if (!headers2['cache-status'].includes('"Netlify Edge"; hit')) {
141195
// if we missed CDN cache, we will see Next cache hit status
142196
// as we reuse cached response
143197
expect(headers2['cache-status']).toMatch(/"Next.js"; hit/m)
@@ -155,18 +209,28 @@ test.describe('app router on-demand revalidation', () => {
155209
)
156210
expect(revalidate?.status()).toBe(200)
157211

212+
// wait a bit until cdn tags and invalidated and cdn is purged
213+
await page.waitForTimeout(500)
214+
158215
// 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': [/"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+
)
160231
const headers3 = response3?.headers() || {}
161232
expect(response3?.status()).toBe(200)
162233
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(/"Next.js"; fwd=miss/m)
169-
expect(headers3['cache-status']).toMatch(/"Netlify Edge"; fwd=(miss|stale)/m)
170234
expect(headers3['netlify-cdn-cache-control']).toBe(
171235
's-maxage=31536000, stale-while-revalidate=31536000',
172236
)
@@ -175,16 +239,25 @@ test.describe('app router on-demand revalidation', () => {
175239
const date3 = await page.textContent('[data-testid="date-now"]')
176240
expect(date3).not.toBe(date2)
177241

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': /"Netlify Edge"; (hit|fwd=miss|fwd=stale)/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+
)
179256
const headers4 = response4?.headers() || {}
180257
expect(response4?.status()).toBe(200)
181258
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(/"Netlify Edge"; (hit|fwd=miss|fwd=stale)/m)
187-
if (!headers4['cache-status'].includes('hit')) {
259+
260+
if (!headers4['cache-status'].includes('"Netlify Edge"; hit')) {
188261
// if we missed CDN cache, we will see Next cache hit status
189262
// as we reuse cached response
190263
expect(headers4['cache-status']).toMatch(/"Next.js"; hit/m)

0 commit comments

Comments
 (0)