From 81e660a98fd6c9b5551bbd6a329635b86b4067a7 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 2 Apr 2025 12:49:23 +0200 Subject: [PATCH] test: expand test fixture and cases for dynamic-cms tests with added i18n to fixture and testing i18n cases and json requests --- tests/e2e/dynamic-cms.test.ts | 154 ++++++++++++------ .../netlify/functions/{cms.ts => cms.mts} | 12 +- tests/fixtures/dynamic-cms/next.config.js | 4 + tests/fixtures/dynamic-cms/package.json | 1 - .../dynamic-cms/pages/content/[...slug].js | 9 +- 5 files changed, 119 insertions(+), 61 deletions(-) rename tests/fixtures/dynamic-cms/netlify/functions/{cms.ts => cms.mts} (57%) diff --git a/tests/e2e/dynamic-cms.test.ts b/tests/e2e/dynamic-cms.test.ts index a157b47727..ae179c68fc 100644 --- a/tests/e2e/dynamic-cms.test.ts +++ b/tests/e2e/dynamic-cms.test.ts @@ -2,57 +2,107 @@ import { expect } from '@playwright/test' import { test } from '../utils/playwright-helpers.js' test.describe('Dynamic CMS', () => { - test('Invalidates 404 pages from durable cache', async ({ page, dynamicCms }) => { - // 1. Verify the status and headers of the dynamic page - const response1 = await page.goto(new URL('/content/blog', dynamicCms.url).href) - const headers1 = response1?.headers() || {} - - expect(response1?.status()).toEqual(404) - expect(headers1['cache-control']).toEqual('public,max-age=0,must-revalidate') - expect(headers1['cache-status']).toEqual( - '"Next.js"; fwd=miss, "Netlify Durable"; fwd=uri-miss; stored, "Netlify Edge"; fwd=miss', - ) - expect(headers1['netlify-cache-tag']).toEqual('_n_t_/content/blog') - expect(headers1['netlify-cdn-cache-control']).toMatch( - /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, - ) - - // 2. Publish the blob, revalidate the dynamic page, and wait to regenerate - await page.goto(new URL('/cms/publish', dynamicCms.url).href) - await page.goto(new URL('/api/revalidate?path=/content/blog', dynamicCms.url).href) - await page.waitForTimeout(1000) - - // 3. Verify the status and headers of the dynamic page - const response2 = await page.goto(new URL('/content/blog', dynamicCms.url).href) - const headers2 = response2?.headers() || {} - - expect(response2?.status()).toEqual(200) - expect(headers2['cache-control']).toEqual('public,max-age=0,must-revalidate') - expect(headers2['cache-status']).toMatch( - /"Next.js"; hit, "Netlify Durable"; fwd=stale; ttl=[0-9]+; stored, "Netlify Edge"; fwd=(stale|miss)/, - ) - expect(headers2['netlify-cache-tag']).toEqual('_n_t_/content/blog') - expect(headers2['netlify-cdn-cache-control']).toMatch( - /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, - ) - - // 4. Unpublish the blob, revalidate the dynamic page, and wait to regenerate - await page.goto(new URL('/cms/unpublish', dynamicCms.url).href) - await page.goto(new URL('/api/revalidate?path=/content/blog', dynamicCms.url).href) - await page.waitForTimeout(1000) - - // 5. Verify the status and headers of the dynamic page - const response3 = await page.goto(new URL('/content/blog', dynamicCms.url).href) - const headers3 = response3?.headers() || {} - - expect(response3?.status()).toEqual(404) - expect(headers3['cache-control']).toEqual('public,max-age=0,must-revalidate') - expect(headers3['cache-status']).toMatch( - /"Next.js"; fwd=miss, "Netlify Durable"; fwd=stale; ttl=[0-9]+; stored, "Netlify Edge"; fwd=(stale|miss)/, - ) - expect(headers3['netlify-cache-tag']).toEqual('_n_t_/content/blog') - expect(headers3['netlify-cdn-cache-control']).toMatch( - /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, - ) + test.describe('Invalidates 404 pages from durable cache', () => { + // using postFix allows to rerun tests without having to redeploy the app because paths/keys will be unique for each test run + const postFix = Date.now() + for (const { label, contentKey, expectedCacheTag, urlPath, pathToRevalidate } of [ + { + label: 'Invalidates 404 html from durable cache (implicit default locale)', + urlPath: `/content/html-implicit-default-locale-${postFix}`, + contentKey: `html-implicit-default-locale-${postFix}`, + expectedCacheTag: `_n_t_/en/content/html-implicit-default-locale-${postFix}`, + }, + { + label: 'Invalidates 404 html from durable cache (explicit default locale)', + urlPath: `/en/content/html-explicit-default-locale-${postFix}`, + contentKey: `html-explicit-default-locale-${postFix}`, + expectedCacheTag: `_n_t_/en/content/html-explicit-default-locale-${postFix}`, + }, + // json paths don't have implicit locale routing + { + label: 'Invalidates 404 json from durable cache (default locale)', + urlPath: `/_next/data/build-id/en/content/json-default-locale-${postFix}.json`, + // for html, we can use html path as param for revalidate, + // for json we can't use json path and instead use one of html paths + // let's use implicit default locale here, as we will have another case for + // non-default locale which will have to use explicit one + pathToRevalidate: `/content/json-default-locale-${postFix}`, + contentKey: `json-default-locale-${postFix}`, + expectedCacheTag: `_n_t_/en/content/json-default-locale-${postFix}`, + }, + { + label: 'Invalidates 404 html from durable cache (non-default locale)', + urlPath: `/fr/content/html-non-default-locale-${postFix}`, + contentKey: `html-non-default-locale-${postFix}`, + expectedCacheTag: `_n_t_/fr/content/html-non-default-locale-${postFix}`, + }, + { + label: 'Invalidates 404 json from durable cache (non-default locale)', + urlPath: `/_next/data/build-id/fr/content/json-non-default-locale-${postFix}.json`, + pathToRevalidate: `/fr/content/json-non-default-locale-${postFix}`, + contentKey: `json-non-default-locale-${postFix}`, + expectedCacheTag: `_n_t_/fr/content/json-non-default-locale-${postFix}`, + }, + ]) { + test(label, async ({ page, dynamicCms }) => { + const routeUrl = new URL(urlPath, dynamicCms.url).href + const revalidateAPiUrl = new URL( + `/api/revalidate?path=${pathToRevalidate ?? urlPath}`, + dynamicCms.url, + ).href + + // 1. Verify the status and headers of the dynamic page + const response1 = await page.goto(routeUrl) + const headers1 = response1?.headers() || {} + + expect(response1?.status()).toEqual(404) + expect(headers1['cache-control']).toEqual('public,max-age=0,must-revalidate') + expect(headers1['cache-status']).toEqual( + '"Next.js"; fwd=miss, "Netlify Durable"; fwd=uri-miss; stored, "Netlify Edge"; fwd=miss', + ) + expect(headers1['netlify-cache-tag']).toEqual(expectedCacheTag) + expect(headers1['netlify-cdn-cache-control']).toMatch( + /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, + ) + + // 2. Publish the blob, revalidate the dynamic page, and wait to regenerate + await page.goto(new URL(`/cms/publish/${contentKey}`, dynamicCms.url).href) + await page.goto(revalidateAPiUrl) + await page.waitForTimeout(1000) + + // 3. Verify the status and headers of the dynamic page + const response2 = await page.goto(routeUrl) + const headers2 = response2?.headers() || {} + + expect(response2?.status()).toEqual(200) + expect(headers2['cache-control']).toEqual('public,max-age=0,must-revalidate') + expect(headers2['cache-status']).toMatch( + /"Next.js"; hit, "Netlify Durable"; fwd=stale; ttl=[0-9]+; stored, "Netlify Edge"; fwd=(stale|miss)/, + ) + expect(headers2['netlify-cache-tag']).toEqual(expectedCacheTag) + expect(headers2['netlify-cdn-cache-control']).toMatch( + /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, + ) + + // 4. Unpublish the blob, revalidate the dynamic page, and wait to regenerate + await page.goto(new URL(`/cms/unpublish/${contentKey}`, dynamicCms.url).href) + await page.goto(revalidateAPiUrl) + await page.waitForTimeout(1000) + + // 5. Verify the status and headers of the dynamic page + const response3 = await page.goto(routeUrl) + const headers3 = response3?.headers() || {} + + expect(response3?.status()).toEqual(404) + expect(headers3['cache-control']).toEqual('public,max-age=0,must-revalidate') + expect(headers3['cache-status']).toMatch( + /"Next.js"; fwd=miss, "Netlify Durable"; fwd=stale; ttl=[0-9]+; stored, "Netlify Edge"; fwd=(stale|miss)/, + ) + expect(headers3['netlify-cache-tag']).toEqual(expectedCacheTag) + expect(headers3['netlify-cdn-cache-control']).toMatch( + /s-maxage=31536000,( stale-while-revalidate=31536000,)? durable/, + ) + }) + } }) }) diff --git a/tests/fixtures/dynamic-cms/netlify/functions/cms.ts b/tests/fixtures/dynamic-cms/netlify/functions/cms.mts similarity index 57% rename from tests/fixtures/dynamic-cms/netlify/functions/cms.ts rename to tests/fixtures/dynamic-cms/netlify/functions/cms.mts index 243421e0be..fb3a9ad2d5 100644 --- a/tests/fixtures/dynamic-cms/netlify/functions/cms.ts +++ b/tests/fixtures/dynamic-cms/netlify/functions/cms.mts @@ -4,21 +4,25 @@ import { Context } from '@netlify/functions' // publish or unpublish "cms content" depending on the sent operation export default async function handler(_request: Request, context: Context) { const store = getDeployStore({ name: 'cms-content', consistency: 'strong' }) - const BLOB_KEY = 'key' const operation = context.params['operation'] + // root of optional catch-all route in Next.js sets 'index.html' as param + // while it's undefined in the Netlify function, because we need to declare + // path without wildcard + const contentKey = context.params['0'] ?? 'index.html' + if (operation === 'publish') { - await store.setJSON(BLOB_KEY, { content: true }) + await store.setJSON(contentKey, { content: true }) } if (operation === 'unpublish') { - await store.delete(BLOB_KEY) + await store.delete(contentKey) } return Response.json({ ok: true }) } export const config = { - path: '/cms/:operation', + path: ['/cms/:operation/*', '/cms/:operation'], } diff --git a/tests/fixtures/dynamic-cms/next.config.js b/tests/fixtures/dynamic-cms/next.config.js index 6346ab0742..fb5bd7c039 100644 --- a/tests/fixtures/dynamic-cms/next.config.js +++ b/tests/fixtures/dynamic-cms/next.config.js @@ -4,6 +4,10 @@ const nextConfig = { eslint: { ignoreDuringBuilds: true, }, + i18n: { + locales: ['en', 'fr'], + defaultLocale: 'en', + }, generateBuildId: () => 'build-id', } diff --git a/tests/fixtures/dynamic-cms/package.json b/tests/fixtures/dynamic-cms/package.json index 33ebc7f177..ba2eca67e8 100644 --- a/tests/fixtures/dynamic-cms/package.json +++ b/tests/fixtures/dynamic-cms/package.json @@ -10,7 +10,6 @@ "dependencies": { "@netlify/blobs": "^8.1.0", "@netlify/functions": "^2.7.0", - "@netlify/plugin-nextjs": "^5.10.1", "netlify-cli": "^19.0.3", "next": "latest", "react": "18.2.0", diff --git a/tests/fixtures/dynamic-cms/pages/content/[...slug].js b/tests/fixtures/dynamic-cms/pages/content/[...slug].js index 54abf74b16..070d222e99 100644 --- a/tests/fixtures/dynamic-cms/pages/content/[...slug].js +++ b/tests/fixtures/dynamic-cms/pages/content/[...slug].js @@ -8,11 +8,12 @@ const Content = ({ value }) => ( ) -export async function getStaticProps() { +export async function getStaticProps({ params }) { + const contentKey = params.slug.join('/') + const store = getDeployStore({ name: 'cms-content', consistency: 'strong' }) - const BLOB_KEY = 'key' - const value = await store.get(BLOB_KEY, { type: 'json' }) + const value = await store.get(contentKey, { type: 'json' }) if (!value) { return { @@ -22,7 +23,7 @@ export async function getStaticProps() { return { props: { - value: value, + value, }, } }