@@ -69,3 +69,150 @@ test('json data rewrite works', async ({ middlewarePages }) => {
6969
7070 expect ( data . pageProps . message ) . toBeDefined ( )
7171} )
72+
73+ // those tests use `fetch` instead of `page.goto` intentionally to avoid potential client rendering
74+ // hiding any potential edge/server issues
75+ test . describe ( 'Middleware with i18n and excluded paths' , ( ) => {
76+ const DEFAULT_LOCALE = 'en'
77+
78+ /** helper function to extract JSON data from page rendering data with `<pre>{JSON.stringify(data)}</pre>` */
79+ function extractDataFromHtml ( html : string ) : Record < string , any > {
80+ const match = html . match ( / < p r e > (?< rawInput > [ ^ < ] + ) < \/ p r e > / )
81+ if ( ! match || ! match . groups ?. rawInput ) {
82+ console . error ( '<pre> not found in html input' , {
83+ html,
84+ } )
85+ throw new Error ( 'Failed to extract data from HTML' )
86+ }
87+
88+ const { rawInput } = match . groups
89+ const unescapedInput = rawInput . replaceAll ( '"' , '"' )
90+ try {
91+ return JSON . parse ( unescapedInput )
92+ } catch ( originalError ) {
93+ console . error ( 'Failed to parse JSON' , {
94+ originalError,
95+ rawInput,
96+ unescapedInput,
97+ } )
98+ }
99+ throw new Error ( 'Failed to extract data from HTML' )
100+ }
101+
102+ // those tests hit paths ending with `/json` which has special handling in middleware
103+ // to return JSON response from middleware itself
104+ test . describe ( 'Middleware response path' , ( ) => {
105+ test ( 'should match on non-localized not excluded page path' , async ( {
106+ middlewareI18nExcludedPaths,
107+ } ) => {
108+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /json` )
109+
110+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
111+ expect ( response . status ) . toBe ( 200 )
112+
113+ const { nextUrlPathname, nextUrlLocale } = await response . json ( )
114+
115+ expect ( nextUrlPathname ) . toBe ( '/json' )
116+ expect ( nextUrlLocale ) . toBe ( DEFAULT_LOCALE )
117+ } )
118+
119+ test ( 'should match on localized not excluded page path' , async ( {
120+ middlewareI18nExcludedPaths,
121+ } ) => {
122+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/json` )
123+
124+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
125+ expect ( response . status ) . toBe ( 200 )
126+
127+ const { nextUrlPathname, nextUrlLocale } = await response . json ( )
128+
129+ expect ( nextUrlPathname ) . toBe ( '/json' )
130+ expect ( nextUrlLocale ) . toBe ( 'fr' )
131+ } )
132+ } )
133+
134+ // those tests hit paths that don't end with `/json` while still satisfying middleware matcher
135+ // so middleware should pass them through to origin
136+ test . describe ( 'Middleware passthrough' , ( ) => {
137+ test ( 'should match on non-localized not excluded page path' , async ( {
138+ middlewareI18nExcludedPaths,
139+ } ) => {
140+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /html` )
141+
142+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
143+ expect ( response . status ) . toBe ( 200 )
144+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
145+
146+ const html = await response . text ( )
147+ const { locale, params } = extractDataFromHtml ( html )
148+
149+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
150+ expect ( locale ) . toBe ( DEFAULT_LOCALE )
151+ } )
152+
153+ test ( 'should match on localized not excluded page path' , async ( {
154+ middlewareI18nExcludedPaths,
155+ } ) => {
156+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/html` )
157+
158+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . toBe ( 'true' )
159+ expect ( response . status ) . toBe ( 200 )
160+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
161+
162+ const html = await response . text ( )
163+ const { locale, params } = extractDataFromHtml ( html )
164+
165+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
166+ expect ( locale ) . toBe ( 'fr' )
167+ } )
168+ } )
169+
170+ // those tests hit paths that don't satisfy middleware matcher, so should go directly to origin
171+ // without going through middleware
172+ test . describe ( 'Middleware skipping (paths not satisfying middleware matcher)' , ( ) => {
173+ test ( 'should NOT match on non-localized excluded API path' , async ( {
174+ middlewareI18nExcludedPaths,
175+ } ) => {
176+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /api/html` )
177+
178+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
179+ expect ( response . status ) . toBe ( 200 )
180+
181+ const { params } = await response . json ( )
182+
183+ expect ( params ) . toMatchObject ( { catchall : [ 'html' ] } )
184+ } )
185+
186+ test ( 'should NOT match on non-localized excluded page path' , async ( {
187+ middlewareI18nExcludedPaths,
188+ } ) => {
189+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /excluded` )
190+
191+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
192+ expect ( response . status ) . toBe ( 200 )
193+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
194+
195+ const html = await response . text ( )
196+ const { locale, params } = extractDataFromHtml ( html )
197+
198+ expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
199+ expect ( locale ) . toBe ( DEFAULT_LOCALE )
200+ } )
201+
202+ test ( 'should NOT match on localized excluded page path' , async ( {
203+ middlewareI18nExcludedPaths,
204+ } ) => {
205+ const response = await fetch ( `${ middlewareI18nExcludedPaths . url } /fr/excluded` )
206+
207+ expect ( response . headers . get ( 'x-test-used-middleware' ) ) . not . toBe ( 'true' )
208+ expect ( response . status ) . toBe ( 200 )
209+ expect ( response . headers . get ( 'content-type' ) ) . toMatch ( / t e x t \/ h t m l / )
210+
211+ const html = await response . text ( )
212+ const { locale, params } = extractDataFromHtml ( html )
213+
214+ expect ( params ) . toMatchObject ( { catchall : [ 'excluded' ] } )
215+ expect ( locale ) . toBe ( 'fr' )
216+ } )
217+ } )
218+ } )
0 commit comments