diff --git a/tests/e2e/simple-app.test.ts b/tests/e2e/simple-app.test.ts index 23455ba567..84247bbf30 100644 --- a/tests/e2e/simple-app.test.ts +++ b/tests/e2e/simple-app.test.ts @@ -1,4 +1,4 @@ -import { expect, type Locator } from '@playwright/test' +import { expect, type Locator, type Response } from '@playwright/test' import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs' import { test } from '../utils/playwright-helpers.js' @@ -282,3 +282,59 @@ test('can require CJS module that is not bundled', async ({ simple }) => { expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false) expect(parsedBody.bundledCJSModule.isBundled).toEqual(true) }) + +test.describe('RSC cache poisoning', () => { + test('Next.config.js rewrite', async ({ page, simple }) => { + const prefetchResponsePromise = new Promise((resolve) => { + page.on('response', (response) => { + if (response.url().includes('/config-rewrite/source')) { + resolve(response) + } + }) + }) + await page.goto(`${simple.url}/config-rewrite`) + + // ensure prefetch + await page.hover('text=NextConfig.rewrite') + + // wait for prefetch request to finish + const prefetchResponse = await prefetchResponsePromise + + // ensure prefetch respond with RSC data + expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/) + expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/) + + const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`) + + // ensure we get HTML response + expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/) + expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/) + }) + + test('Next.config.js redirect', async ({ page, simple }) => { + const prefetchResponsePromise = new Promise((resolve) => { + page.on('response', (response) => { + if (response.url().includes('/config-redirect/dest')) { + resolve(response) + } + }) + }) + await page.goto(`${simple.url}/config-redirect`) + + // ensure prefetch + await page.hover('text=NextConfig.redirect') + + // wait for prefetch request to finish + const prefetchResponse = await prefetchResponsePromise + + // ensure prefetch respond with RSC data + expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/) + expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/) + + const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`) + + // ensure we get HTML response + expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/) + expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/) + }) +}) diff --git a/tests/fixtures/simple/app/config-redirect/dest/page.js b/tests/fixtures/simple/app/config-redirect/dest/page.js new file mode 100644 index 0000000000..df1c207417 --- /dev/null +++ b/tests/fixtures/simple/app/config-redirect/dest/page.js @@ -0,0 +1,9 @@ +export default function Page() { + return ( +
+

Hello redirect target

+
+ ) +} + +export const dynamic = 'force-static' diff --git a/tests/fixtures/simple/app/config-redirect/page.js b/tests/fixtures/simple/app/config-redirect/page.js new file mode 100644 index 0000000000..5aff0291a1 --- /dev/null +++ b/tests/fixtures/simple/app/config-redirect/page.js @@ -0,0 +1,9 @@ +import Link from 'next/link' + +export default function Home() { + return ( +
+ NextConfig.redirect +
+ ) +} diff --git a/tests/fixtures/simple/app/config-rewrite/dest/page.js b/tests/fixtures/simple/app/config-rewrite/dest/page.js new file mode 100644 index 0000000000..a54df422f4 --- /dev/null +++ b/tests/fixtures/simple/app/config-rewrite/dest/page.js @@ -0,0 +1,9 @@ +export default function Page() { + return ( +
+

Hello rewrite target

+
+ ) +} + +export const dynamic = 'force-static' diff --git a/tests/fixtures/simple/app/config-rewrite/page.js b/tests/fixtures/simple/app/config-rewrite/page.js new file mode 100644 index 0000000000..a7ae9c6c84 --- /dev/null +++ b/tests/fixtures/simple/app/config-rewrite/page.js @@ -0,0 +1,9 @@ +import Link from 'next/link' + +export default function Home() { + return ( +
+ NextConfig.rewrite +
+ ) +} diff --git a/tests/fixtures/simple/next.config.js b/tests/fixtures/simple/next.config.js index 57e41bf927..8c408d8120 100644 --- a/tests/fixtures/simple/next.config.js +++ b/tests/fixtures/simple/next.config.js @@ -29,6 +29,19 @@ const nextConfig = { destination: 'https://example.vercel.sh', basePath: false, }, + { + source: '/config-rewrite/source', + destination: '/config-rewrite/dest', + }, + ] + }, + async redirects() { + return [ + { + source: '/config-redirect/source', + destination: '/config-redirect/dest', + permanent: true, + }, ] }, } diff --git a/tests/integration/simple-app.test.ts b/tests/integration/simple-app.test.ts index 790836fdf8..62b1ddd09a 100644 --- a/tests/integration/simple-app.test.ts +++ b/tests/integration/simple-app.test.ts @@ -101,6 +101,10 @@ test('Test that the simple next app is working', async (ctx) '/404', '/api/cached-permanent', '/api/cached-revalidate', + '/config-redirect', + '/config-redirect/dest', + '/config-rewrite', + '/config-rewrite/dest', '/image/local', '/image/migration-from-v4-runtime', '/image/remote-domain',