From 2db89e1eee83ce17d9ca649993f419bb63310b91 Mon Sep 17 00:00:00 2001 From: feugy Date: Mon, 8 Aug 2022 16:57:35 +0200 Subject: [PATCH 1/4] feat(middleware): augments / matcher with /index --- .../build/analysis/get-page-static-info.ts | 11 +++- test/e2e/middleware-matcher/index.test.ts | 61 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index d27ef9057b1b..6030f324de04 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -178,11 +178,12 @@ function getMiddlewareConfig( function getMiddlewareRegExpStrings( matcherOrMatchers: unknown, - nextConfig: NextConfig + nextConfig: NextConfig, + { doNotRecurseOnRoot }: { doNotRecurseOnRoot?: boolean } = {} ): string[] { if (Array.isArray(matcherOrMatchers)) { return matcherOrMatchers.flatMap((matcher) => - getMiddlewareRegExpStrings(matcher, nextConfig) + getMiddlewareRegExpStrings(matcher, nextConfig, { doNotRecurseOnRoot }) ) } @@ -198,6 +199,12 @@ function getMiddlewareRegExpStrings( throw new Error('`matcher`: path matcher must start with /') } + if (matcher === '/' && !doNotRecurseOnRoot) { + return getMiddlewareRegExpStrings(['/', '/index'], nextConfig, { + doNotRecurseOnRoot: true, + }) + } + if (nextConfig.i18n?.locales) { matcher = `/:nextInternalLocale(${nextConfig.i18n.locales .map((locale) => escapeStringRegexp(locale)) diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index d76a1b5d8ed0..75defe3d5bc0 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -217,6 +217,49 @@ describe('using a single matcher', () => { }) }) +describe('using root matcher', () => { + let next: NextInstance + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + export default function Home({ message }) { + return
Hi there!
+ } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + + export const config = { matcher: '/' }; + `, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('adds the header to the /', async () => { + const response = await fetchViaHTTP(next.url, '/') + expect(response.status).toBe(200) + expect(Object.fromEntries(response.headers)).toMatchObject({ + 'x-from-middleware': 'true', + }) + }) + + it('adds the header to the /index', async () => { + const response = await fetchViaHTTP(next.url, '/index') + expect(response.status).toBe(404) + expect(Object.fromEntries(response.headers)).toMatchObject({ + 'x-from-middleware': 'true', + }) + }) +}) + describe('using a single matcher with i18n', () => { let next: NextInstance beforeAll(async () => { @@ -276,6 +319,15 @@ describe('using a single matcher with i18n', () => { expect(res2.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') + }) + it(`adds the headers for a matched data path`, async () => { const res1 = await fetchViaHTTP( next.url, @@ -367,6 +419,15 @@ describe('using a single matcher with i18n and basePath', () => { expect(res2.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/root/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/root/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') + }) + it(`adds the headers for a matched data path`, async () => { const res1 = await fetchViaHTTP( next.url, From 04e418f3d7deb02842c6252eecf2be860877c9fb Mon Sep 17 00:00:00 2001 From: feugy Date: Mon, 8 Aug 2022 19:01:55 +0200 Subject: [PATCH 2/4] fix(#39396): not invoked with i18n and trailingSlash --- .../build/analysis/get-page-static-info.ts | 4 +- test/e2e/middleware-matcher/index.test.ts | 128 ++++++++++-------- 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index 6030f324de04..f6ee5542c362 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -208,9 +208,7 @@ function getMiddlewareRegExpStrings( if (nextConfig.i18n?.locales) { matcher = `/:nextInternalLocale(${nextConfig.i18n.locales .map((locale) => escapeStringRegexp(locale)) - .join('|')})${ - matcher === '/' && !nextConfig.trailingSlash ? '' : matcher - }` + .join('|')})${matcher === '/' ? '' : matcher}` } if (nextConfig.basePath) { diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index 75defe3d5bc0..b83990767167 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable jest/no-identical-title */ import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' import { check, fetchViaHTTP } from 'next-test-utils' @@ -260,7 +261,10 @@ describe('using root matcher', () => { }) }) -describe('using a single matcher with i18n', () => { +describe.each([ + { title: '' }, + { title: ' and trailingSlash', trailingSlash: true }, +])('using a single matcher with i18n$title', ({ trailingSlash }) => { let next: NextInstance beforeAll(async () => { next = await createNext({ @@ -297,6 +301,7 @@ describe('using a single matcher with i18n', () => { `, 'next.config.js': ` module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} i18n: { localeDetection: false, locales: ['es', 'en'], @@ -358,12 +363,17 @@ describe('using a single matcher with i18n', () => { }) }) -describe('using a single matcher with i18n and basePath', () => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` +describe.each([ + { title: '' }, + { title: ' and trailingSlash', trailingSlash: true }, +])( + 'using a single matcher with i18n and basePath$title', + ({ trailingSlash }) => { + let next: NextInstance + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` export default function Page({ message }) { return

root page

@@ -374,7 +384,7 @@ describe('using a single matcher with i18n and basePath', () => { props: { message: \`(\${locale}) Hello from /\` } }) `, - 'pages/[...route].js': ` + 'pages/[...route].js': ` export default function Page({ message }) { return

catchall page

@@ -385,7 +395,7 @@ describe('using a single matcher with i18n and basePath', () => { props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } }) `, - 'middleware.js': ` + 'middleware.js': ` import { NextResponse } from 'next/server' export const config = { matcher: '/' }; export default (req) => { @@ -394,8 +404,9 @@ describe('using a single matcher with i18n and basePath', () => { return res; } `, - 'next.config.js': ` + 'next.config.js': ` module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} basePath: '/root', i18n: { localeDetection: false, @@ -404,56 +415,57 @@ describe('using a single matcher with i18n and basePath', () => { } } `, - }, - dependencies: {}, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it(`adds the header for a matched path`, async () => { + const res1 = await fetchViaHTTP(next.url, `/root`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/root/es`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - }) - afterAll(() => next.destroy()) - - it(`adds the header for a matched path`, async () => { - const res1 = await fetchViaHTTP(next.url, `/root`) - expect(await res1.text()).toContain(`(en) Hello from /`) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP(next.url, `/root/es`) - expect(await res2.text()).toContain(`(es) Hello from /`) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - - it('adds the header for a mathed root path with /index', async () => { - const res1 = await fetchViaHTTP(next.url, `/root/index`) - expect(await res1.text()).toContain(`(en) Hello from /`) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP(next.url, `/root/es/index`) - expect(await res2.text()).toContain(`(es) Hello from /`) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - it(`adds the headers for a matched data path`, async () => { - const res1 = await fetchViaHTTP( - next.url, - `/root/_next/data/${next.buildId}/en.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res1.json()).toMatchObject({ - pageProps: { message: `(en) Hello from /` }, + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/root/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/root/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP( - next.url, - `/root/_next/data/${next.buildId}/es.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res2.json()).toMatchObject({ - pageProps: { message: `(es) Hello from /` }, + + it(`adds the headers for a matched data path`, async () => { + const res1 = await fetchViaHTTP( + next.url, + `/root/_next/data/${next.buildId}/en.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await res1.json()).toMatchObject({ + pageProps: { message: `(en) Hello from /` }, + }) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP( + next.url, + `/root/_next/data/${next.buildId}/es.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await res2.json()).toMatchObject({ + pageProps: { message: `(es) Hello from /` }, + }) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - it(`does not add the header for an unmatched path`, async () => { - const response = await fetchViaHTTP(next.url, `/root/about/me`) - expect(await response.text()).toContain('Hello from /about/me') - expect(response.headers.get('X-From-Middleware')).toBeNull() - }) -}) + it(`does not add the header for an unmatched path`, async () => { + const response = await fetchViaHTTP(next.url, `/root/about/me`) + expect(await response.text()).toContain('Hello from /about/me') + expect(response.headers.get('X-From-Middleware')).toBeNull() + }) + } +) From fba0cef96d80388a88b768f6fa88f495ae7208fc Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 9 Aug 2022 13:38:19 -0500 Subject: [PATCH 3/4] add test case --- test/e2e/middleware-matcher/index.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index b83990767167..6af81def9ab5 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -28,7 +28,7 @@ describe('Middleware can set the matcher in its config', () => { expect(await response.text()).toContain('This should run the middleware') }) - it('adds the header for a matched data path', async () => { + it('adds the header for a matched data path (with header)', async () => { const response = await fetchViaHTTP( next.url, `/_next/data/${next.buildId}/with-middleware.json`, @@ -43,6 +43,19 @@ describe('Middleware can set the matcher in its config', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/with-middleware.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'Hello, cruel world.', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + it('adds the header for another matched path', async () => { const response = await fetchViaHTTP(next.url, '/another-middleware') expect(response.headers.get('X-From-Middleware')).toBe('true') From 6082ae6797946e8b426933a25be70d66fe8a26a3 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 9 Aug 2022 17:34:15 -0500 Subject: [PATCH 4/4] tweak matcher a bit and add tests --- .../build/analysis/get-page-static-info.ts | 28 ++- test/e2e/middleware-matcher/index.test.ts | 169 +++++++++++------- 2 files changed, 121 insertions(+), 76 deletions(-) diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index f6ee5542c362..af30649b7edc 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -4,7 +4,6 @@ import { extractExportedConstValue, UnsupportedValueError, } from './extract-const-value' -import { escapeStringRegexp } from '../../shared/lib/escape-regexp' import { parseModule } from './parse-module' import { promises as fs } from 'fs' import { tryToParsePath } from '../../lib/try-to-parse-path' @@ -178,14 +177,14 @@ function getMiddlewareConfig( function getMiddlewareRegExpStrings( matcherOrMatchers: unknown, - nextConfig: NextConfig, - { doNotRecurseOnRoot }: { doNotRecurseOnRoot?: boolean } = {} + nextConfig: NextConfig ): string[] { if (Array.isArray(matcherOrMatchers)) { return matcherOrMatchers.flatMap((matcher) => - getMiddlewareRegExpStrings(matcher, nextConfig, { doNotRecurseOnRoot }) + getMiddlewareRegExpStrings(matcher, nextConfig) ) } + const { i18n } = nextConfig if (typeof matcherOrMatchers !== 'string') { throw new Error( @@ -198,24 +197,23 @@ function getMiddlewareRegExpStrings( if (!matcher.startsWith('/')) { throw new Error('`matcher`: path matcher must start with /') } + const isRoot = matcher === '/' - if (matcher === '/' && !doNotRecurseOnRoot) { - return getMiddlewareRegExpStrings(['/', '/index'], nextConfig, { - doNotRecurseOnRoot: true, - }) + if (i18n?.locales) { + matcher = `/:nextInternalLocale([^/.]{1,})${isRoot ? '' : matcher}` } - if (nextConfig.i18n?.locales) { - matcher = `/:nextInternalLocale(${nextConfig.i18n.locales - .map((locale) => escapeStringRegexp(locale)) - .join('|')})${matcher === '/' ? '' : matcher}` - } + matcher = `/:nextData(_next/data/[^/]{1,})?${matcher}${ + isRoot + ? `(${nextConfig.i18n ? '|\\.json|' : ''}/?index|/?index\\.json)?` + : '(.json)?' + }` if (nextConfig.basePath) { - matcher = `${nextConfig.basePath}${matcher === '/' ? '' : matcher}` + matcher = `${nextConfig.basePath}${matcher}` } - const parsedPage = tryToParsePath(matcher) + if (parsedPage.error) { throw new Error(`Invalid path matcher: ${matcher}`) } diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index 6af81def9ab5..d31d2e37aba9 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -209,7 +209,7 @@ describe('using a single matcher', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) - it('adds the headers for a matched data path', async () => { + it('adds the headers for a matched data path (with header)', async () => { const response = await fetchViaHTTP( next.url, `/_next/data/${next.buildId}/middleware/works.json`, @@ -224,6 +224,19 @@ describe('using a single matcher', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/middleware/works.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'Hello from /middleware/works', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + it('does not add the header for an unmatched path', async () => { const response = await fetchViaHTTP(next.url, '/about/me') expect(await response.text()).toContain('Hello from /about/me') @@ -237,6 +250,14 @@ describe('using root matcher', () => { next = await createNext({ files: { 'pages/index.js': ` + export function getStaticProps() { + return { + props: { + message: 'hello world' + } + } + } + export default function Home({ message }) { return
Hi there!
} @@ -267,22 +288,48 @@ describe('using root matcher', () => { it('adds the header to the /index', async () => { const response = await fetchViaHTTP(next.url, '/index') - expect(response.status).toBe(404) expect(Object.fromEntries(response.headers)).toMatchObject({ 'x-from-middleware': 'true', }) }) + + it('adds the header for a matched data path (with header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/index.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'hello world', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/index.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'hello world', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) }) -describe.each([ - { title: '' }, - { title: ' and trailingSlash', trailingSlash: true }, -])('using a single matcher with i18n$title', ({ trailingSlash }) => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` +describe.each([{ title: ' and trailingSlash', trailingSlash: true }])( + 'using a single matcher with i18n$title', + ({ trailingSlash }) => { + let next: NextInstance + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` export default function Page({ message }) { return

{message}

@@ -292,7 +339,7 @@ describe.each([ props: { message: \`(\${locale}) Hello from /\` } }) `, - 'pages/[...route].js': ` + 'pages/[...route].js': ` export default function Page({ message }) { return

catchall page

@@ -303,7 +350,7 @@ describe.each([ props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } }) `, - 'middleware.js': ` + 'middleware.js': ` import { NextResponse } from 'next/server' export const config = { matcher: '/' }; export default (req) => { @@ -312,7 +359,7 @@ describe.each([ return res; } `, - 'next.config.js': ` + 'next.config.js': ` module.exports = { ${trailingSlash ? 'trailingSlash: true,' : ''} i18n: { @@ -322,59 +369,59 @@ describe.each([ } } `, - }, - dependencies: {}, + }, + dependencies: {}, + }) }) - }) - afterAll(() => next.destroy()) - - it(`adds the header for a matched path`, async () => { - const res1 = await fetchViaHTTP(next.url, `/`) - expect(await res1.text()).toContain(`(en) Hello from /`) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP(next.url, `/es`) - expect(await res2.text()).toContain(`(es) Hello from /`) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) + afterAll(() => next.destroy()) - it('adds the header for a mathed root path with /index', async () => { - const res1 = await fetchViaHTTP(next.url, `/index`) - expect(await res1.text()).toContain(`(en) Hello from /`) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP(next.url, `/es/index`) - expect(await res2.text()).toContain(`(es) Hello from /`) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) + it(`adds the header for a matched path`, async () => { + const res1 = await fetchViaHTTP(next.url, `/`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/es`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') + }) - it(`adds the headers for a matched data path`, async () => { - const res1 = await fetchViaHTTP( - next.url, - `/_next/data/${next.buildId}/en.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res1.json()).toMatchObject({ - pageProps: { message: `(en) Hello from /` }, + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP( - next.url, - `/_next/data/${next.buildId}/es.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res2.json()).toMatchObject({ - pageProps: { message: `(es) Hello from /` }, + + it(`adds the headers for a matched data path`, async () => { + const res1 = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/en.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await res1.json()).toMatchObject({ + pageProps: { message: `(en) Hello from /` }, + }) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/es.json`, + undefined + ) + expect(await res2.json()).toMatchObject({ + pageProps: { message: `(es) Hello from /` }, + }) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - it(`does not add the header for an unmatched path`, async () => { - const response = await fetchViaHTTP(next.url, `/about/me`) - expect(await response.text()).toContain('Hello from /about/me') - expect(response.headers.get('X-From-Middleware')).toBeNull() - }) -}) + it(`does not add the header for an unmatched path`, async () => { + const response = await fetchViaHTTP(next.url, `/about/me`) + expect(await response.text()).toContain('Hello from /about/me') + expect(response.headers.get('X-From-Middleware')).toBeNull() + }) + } +) describe.each([ { title: '' },