Skip to content

Commit 8981abf

Browse files
committed
test: add some test cases ensuring next.config redirects/rewrites don't result in cache poisoning
1 parent 695eced commit 8981abf

File tree

9 files changed

+138
-1
lines changed

9 files changed

+138
-1
lines changed

tests/e2e/simple-app.test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, type Locator } from '@playwright/test'
1+
import { expect, type Locator, type Response } from '@playwright/test'
22
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
33
import { test } from '../utils/playwright-helpers.js'
44

@@ -282,3 +282,59 @@ test('can require CJS module that is not bundled', async ({ simple }) => {
282282
expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false)
283283
expect(parsedBody.bundledCJSModule.isBundled).toEqual(true)
284284
})
285+
286+
test.describe('RSC cache poisoning', () => {
287+
test('Next.config.js rewrite', async ({ page, simple }) => {
288+
const prefetchResponsePromise = new Promise<Response>((resolve) => {
289+
page.on('response', (response) => {
290+
if (response.url().includes('/config-rewrite/source')) {
291+
resolve(response)
292+
}
293+
})
294+
})
295+
await page.goto(`${simple.url}/config-rewrite`)
296+
297+
// ensure prefetch
298+
await page.hover('text=NextConfig.rewrite')
299+
300+
// wait for prefetch request to finish
301+
const prefetchResponse = await prefetchResponsePromise
302+
303+
// ensure prefetch respond with RSC data
304+
expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/)
305+
expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
306+
307+
const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`)
308+
309+
// ensure we get HTML response
310+
expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/)
311+
expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
312+
})
313+
314+
test('Next.config.js redirect', async ({ page, simple }) => {
315+
const prefetchResponsePromise = new Promise<Response>((resolve) => {
316+
page.on('response', (response) => {
317+
if (response.url().includes('/config-redirect/dest')) {
318+
resolve(response)
319+
}
320+
})
321+
})
322+
await page.goto(`${simple.url}/config-redirect`)
323+
324+
// ensure prefetch
325+
await page.hover('text=NextConfig.redirect')
326+
327+
// wait for prefetch request to finish
328+
const prefetchResponse = await prefetchResponsePromise
329+
330+
// ensure prefetch respond with RSC data
331+
expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/)
332+
expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
333+
334+
const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`)
335+
336+
// ensure we get HTML response
337+
expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/)
338+
expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
339+
})
340+
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default function Page() {
2+
return (
3+
<main>
4+
<h1>Hello redirect target</h1>
5+
</main>
6+
)
7+
}
8+
9+
export const dynamic = 'force-static'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Link from 'next/link'
2+
3+
export default function Home() {
4+
return (
5+
<div>
6+
<Link href="/config-redirect/source">NextConfig.redirect</Link>
7+
</div>
8+
)
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default function Page() {
2+
return (
3+
<main>
4+
<h1>Hello rewrite target</h1>
5+
</main>
6+
)
7+
}
8+
9+
export const dynamic = 'force-static'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Link from 'next/link'
2+
3+
export default function Home() {
4+
return (
5+
<div>
6+
<Link href="/config-rewrite/source">NextConfig.rewrite</Link>
7+
</div>
8+
)
9+
}

tests/fixtures/simple/next-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

tests/fixtures/simple/next.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ const nextConfig = {
2929
destination: 'https://example.vercel.sh',
3030
basePath: false,
3131
},
32+
{
33+
source: '/config-rewrite/source',
34+
destination: '/config-rewrite/dest',
35+
},
36+
]
37+
},
38+
async redirects() {
39+
return [
40+
{
41+
source: '/config-redirect/source',
42+
destination: '/config-redirect/dest',
43+
permanent: true,
44+
},
3245
]
3346
},
3447
}

tests/fixtures/simple/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
"next": "latest",
1212
"react": "18.2.0",
1313
"react-dom": "18.2.0"
14+
},
15+
"devDependencies": {
16+
"@types/react": "19.1.2"
1417
}
1518
}

tests/fixtures/simple/tsconfig.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2017",
4+
"lib": ["dom", "dom.iterable", "esnext"],
5+
"allowJs": true,
6+
"skipLibCheck": true,
7+
"strict": false,
8+
"noEmit": true,
9+
"incremental": true,
10+
"module": "esnext",
11+
"esModuleInterop": true,
12+
"moduleResolution": "node",
13+
"resolveJsonModule": true,
14+
"isolatedModules": true,
15+
"jsx": "preserve",
16+
"plugins": [
17+
{
18+
"name": "next"
19+
}
20+
]
21+
},
22+
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
23+
"exclude": ["node_modules"]
24+
}

0 commit comments

Comments
 (0)