@@ -12,6 +12,7 @@ type ExtendedFixtures = {
1212 edgeOrNodeMiddleware : Fixture
1313 edgeOrNodeMiddlewarePages : Fixture
1414 edgeOrNodeMiddlewareI18n : Fixture
15+ edgeOrNodeMiddlewareI18nExcludedPaths : Fixture
1516}
1617
1718for ( const { expectedRuntime, label, testWithSwitchableMiddlewareRuntime } of [
@@ -43,6 +44,14 @@ for (const { expectedRuntime, label, testWithSwitchableMiddlewareRuntime } of [
4344 scope : 'worker' ,
4445 } ,
4546 ] ,
47+ edgeOrNodeMiddlewareI18nExcludedPaths : [
48+ async ( { middlewareI18nExcludedPaths } , use ) => {
49+ await use ( middlewareI18nExcludedPaths )
50+ } ,
51+ {
52+ scope : 'worker' ,
53+ } ,
54+ ] ,
4655 } ) ,
4756 } ,
4857 hasNodeMiddlewareSupport ( )
@@ -74,6 +83,14 @@ for (const { expectedRuntime, label, testWithSwitchableMiddlewareRuntime } of [
7483 scope : 'worker' ,
7584 } ,
7685 ] ,
86+ edgeOrNodeMiddlewareI18nExcludedPaths : [
87+ async ( { middlewareI18nExcludedPathsNode } , use ) => {
88+ await use ( middlewareI18nExcludedPathsNode )
89+ } ,
90+ {
91+ scope : 'worker' ,
92+ } ,
93+ ] ,
7794 } ) ,
7895 }
7996 : undefined ,
@@ -297,154 +314,161 @@ for (const { expectedRuntime, label, testWithSwitchableMiddlewareRuntime } of [
297314 } )
298315 } )
299316
300- if ( expectedRuntime !== 'node' ) {
301- // those tests use `fetch` instead of `page.goto` intentionally to avoid potential client rendering
302- // hiding any potential edge/server issues
303- test . describe ( 'Middleware with i18n and excluded paths' , ( ) => {
304- const DEFAULT_LOCALE = 'en'
305-
306- /** helper function to extract JSON data from page rendering data with `<pre>{JSON.stringify(data)}</pre>` */
307- function extractDataFromHtml ( html : string ) : Record < string , any > {
308- const match = html . match ( / < p r e > (?< rawInput > [ ^ < ] + ) < \/ p r e > / )
309- if ( ! match || ! match . groups ?. rawInput ) {
310- console . error ( '<pre> not found in html input' , {
311- html,
312- } )
313- throw new Error ( 'Failed to extract data from HTML' )
314- }
315-
316- const { rawInput } = match . groups
317- const unescapedInput = rawInput . replaceAll ( '"' , '"' )
318- try {
319- return JSON . parse ( unescapedInput )
320- } catch ( originalError ) {
321- console . error ( 'Failed to parse JSON' , {
322- originalError,
323- rawInput,
324- unescapedInput,
325- } )
326- }
317+ // those tests use `fetch` instead of `page.goto` intentionally to avoid potential client rendering
318+ // hiding any potential edge/server issues
319+ test . describe ( 'Middleware with i18n and excluded paths' , ( ) => {
320+ const DEFAULT_LOCALE = 'en'
321+
322+ /** helper function to extract JSON data from page rendering data with `<pre>{JSON.stringify(data)}</pre>` */
323+ function extractDataFromHtml ( html : string ) : Record < string , any > {
324+ const match = html . match ( / < p r e > (?< rawInput > [ ^ < ] + ) < \/ p r e > / )
325+ if ( ! match || ! match . groups ?. rawInput ) {
326+ console . error ( '<pre> not found in html input' , {
327+ html,
328+ } )
327329 throw new Error ( 'Failed to extract data from HTML' )
328330 }
329331
330- // those tests hit paths ending with `/json` which has special handling in middleware
331- // to return JSON response from middleware itself
332- test . describe ( 'Middleware response path' , ( ) => {
333- test ( 'should match on non-localized not excluded page path' , async ( {
334- middlewareI18nExcludedPaths,
335- } ) => {
336- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /json` )
332+ const { rawInput } = match . groups
333+ const unescapedInput = rawInput . replaceAll ( '"' , '"' )
334+ try {
335+ return JSON . parse ( unescapedInput )
336+ } catch ( originalError ) {
337+ console . error ( 'Failed to parse JSON' , {
338+ originalError,
339+ rawInput,
340+ unescapedInput,
341+ } )
342+ }
343+ throw new Error ( 'Failed to extract data from HTML' )
344+ }
337345
338- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
339- expect ( response . status ) . toBe ( 200 )
346+ // those tests hit paths ending with `/json` which has special handling in middleware
347+ // to return JSON response from middleware itself
348+ test . describe ( 'Middleware response path' , ( ) => {
349+ test ( 'should match on non-localized not excluded page path' , async ( {
350+ edgeOrNodeMiddlewareI18nExcludedPaths,
351+ } ) => {
352+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /json` )
340353
341- const { nextUrlPathname, nextUrlLocale } = await response . json ( )
354+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
355+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
356+ expect ( response . status ) . toBe ( 200 )
342357
343- expect ( nextUrlPathname ) . toBe ( '/json' )
344- expect ( nextUrlLocale ) . toBe ( DEFAULT_LOCALE )
345- } )
358+ const { nextUrlPathname, nextUrlLocale } = await response . json ( )
359+
360+ expect ( nextUrlPathname ) . toBe ( '/json' )
361+ expect ( nextUrlLocale ) . toBe ( DEFAULT_LOCALE )
362+ } )
346363
347- test ( 'should match on localized not excluded page path' , async ( {
348- middlewareI18nExcludedPaths ,
349- } ) => {
350- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/json` )
364+ test ( 'should match on localized not excluded page path' , async ( {
365+ edgeOrNodeMiddlewareI18nExcludedPaths ,
366+ } ) => {
367+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /fr/json` )
351368
352- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
353- expect ( response . status ) . toBe ( 200 )
369+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
370+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
371+ expect ( response . status ) . toBe ( 200 )
354372
355- const { nextUrlPathname, nextUrlLocale } = await response . json ( )
373+ const { nextUrlPathname, nextUrlLocale } = await response . json ( )
356374
357- expect ( nextUrlPathname ) . toBe ( '/json' )
358- expect ( nextUrlLocale ) . toBe ( 'fr' )
359- } )
375+ expect ( nextUrlPathname ) . toBe ( '/json' )
376+ expect ( nextUrlLocale ) . toBe ( 'fr' )
360377 } )
378+ } )
361379
362- // those tests hit paths that don't end with `/json` while still satisfying middleware matcher
363- // so middleware should pass them through to origin
364- test . describe ( 'Middleware passthrough' , ( ) => {
365- test ( 'should match on non-localized not excluded page path' , async ( {
366- middlewareI18nExcludedPaths ,
367- } ) => {
368- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /html` )
380+ // those tests hit paths that don't end with `/json` while still satisfying middleware matcher
381+ // so middleware should pass them through to origin
382+ test . describe ( 'Middleware passthrough' , ( ) => {
383+ test ( 'should match on non-localized not excluded page path' , async ( {
384+ edgeOrNodeMiddlewareI18nExcludedPaths ,
385+ } ) => {
386+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /html` )
369387
370- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
371- expect ( response . status ) . toBe ( 200 )
372- expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
388+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
389+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
390+ expect ( response . status ) . toBe ( 200 )
391+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
373392
374- const html = await response . text ( )
375- const { locale, params } = extractDataFromHtml ( html )
393+ const html = await response . text ( )
394+ const { locale, params } = extractDataFromHtml ( html )
376395
377- expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
378- expect ( locale ) . toBe ( DEFAULT_LOCALE )
379- } )
396+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
397+ expect ( locale ) . toBe ( DEFAULT_LOCALE )
398+ } )
380399
381- test ( 'should match on localized not excluded page path' , async ( {
382- middlewareI18nExcludedPaths ,
383- } ) => {
384- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/html` )
400+ test ( 'should match on localized not excluded page path' , async ( {
401+ edgeOrNodeMiddlewareI18nExcludedPaths ,
402+ } ) => {
403+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /fr/html` )
385404
386- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
387- expect ( response . status ) . toBe ( 200 )
388- expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
405+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
406+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
407+ expect ( response . status ) . toBe ( 200 )
408+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
389409
390- const html = await response . text ( )
391- const { locale, params } = extractDataFromHtml ( html )
410+ const html = await response . text ( )
411+ const { locale, params } = extractDataFromHtml ( html )
392412
393- expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
394- expect ( locale ) . toBe ( 'fr' )
395- } )
413+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
414+ expect ( locale ) . toBe ( 'fr' )
396415 } )
416+ } )
397417
398- // those tests hit paths that don't satisfy middleware matcher, so should go directly to origin
399- // without going through middleware
400- test . describe ( 'Middleware skipping (paths not satisfying middleware matcher)' , ( ) => {
401- test ( 'should NOT match on non-localized excluded API path' , async ( {
402- middlewareI18nExcludedPaths ,
403- } ) => {
404- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /api/html` )
418+ // those tests hit paths that don't satisfy middleware matcher, so should go directly to origin
419+ // without going through middleware
420+ test . describe ( 'Middleware skipping (paths not satisfying middleware matcher)' , ( ) => {
421+ test ( 'should NOT match on non-localized excluded API path' , async ( {
422+ edgeOrNodeMiddlewareI18nExcludedPaths ,
423+ } ) => {
424+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /api/html` )
405425
406- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
407- expect ( response . status ) . toBe ( 200 )
426+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
427+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
428+ expect ( response . status ) . toBe ( 200 )
408429
409- const { params } = await response . json ( )
430+ const { params } = await response . json ( )
410431
411- expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
412- } )
432+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
433+ } )
413434
414- test ( 'should NOT match on non-localized excluded page path' , async ( {
415- middlewareI18nExcludedPaths ,
416- } ) => {
417- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /excluded` )
435+ test ( 'should NOT match on non-localized excluded page path' , async ( {
436+ edgeOrNodeMiddlewareI18nExcludedPaths ,
437+ } ) => {
438+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /excluded` )
418439
419- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
420- expect ( response . status ) . toBe ( 200 )
421- expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
440+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
441+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
442+ expect ( response . status ) . toBe ( 200 )
443+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
422444
423- const html = await response . text ( )
424- const { locale, params } = extractDataFromHtml ( html )
445+ const html = await response . text ( )
446+ const { locale, params } = extractDataFromHtml ( html )
425447
426- expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
427- expect ( locale ) . toBe ( DEFAULT_LOCALE )
428- } )
448+ expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
449+ expect ( locale ) . toBe ( DEFAULT_LOCALE )
450+ } )
429451
430- test ( 'should NOT match on localized excluded page path' , async ( {
431- middlewareI18nExcludedPaths ,
432- } ) => {
433- const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/excluded` )
452+ test ( 'should NOT match on localized excluded page path' , async ( {
453+ edgeOrNodeMiddlewareI18nExcludedPaths ,
454+ } ) => {
455+ const response = await fetch ( `${ edgeOrNodeMiddlewareI18nExcludedPaths . url } /fr/excluded` )
434456
435- expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
436- expect ( response . status ) . toBe ( 200 )
437- expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
457+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
458+ expect ( response . headers . get ( 'x-runtime' ) ) . toEqual ( expectedRuntime )
459+ expect ( response . status ) . toBe ( 200 )
460+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
438461
439- const html = await response . text ( )
440- const { locale, params } = extractDataFromHtml ( html )
462+ const html = await response . text ( )
463+ const { locale, params } = extractDataFromHtml ( html )
441464
442- expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
443- expect ( locale ) . toBe ( 'fr' )
444- } )
465+ expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
466+ expect ( locale ) . toBe ( 'fr' )
445467 } )
446468 } )
469+ } )
447470
471+ if ( expectedRuntime !== 'node' ) {
448472 test ( "requests with x-middleware-subrequest don't skip middleware (GHSA-f82v-jwr5-mffw)" , async ( {
449473 middlewareSubrequestVuln,
450474 } ) => {
0 commit comments