Repository: vercel/next.js
Pull request: Fix interception routes with trailing slash configuration

### What?

This PR fixes interception routes to work correctly with the `trailingSlash` configuration option.

### Why?

Interception routes were not working when users had `trailingSlash: true` in their Next.js config. This was because interception route rewrites were marked as `internal: true`, which caused them to bypass the `modifyRouteRegex` processing that handles trailing slash normalization.

When a route is marked as internal and provides its own regex, the code would skip the regex modification step that ensures routes work correctly with both `trailingSlash: true` and `trailingSlash: false` configurations. This meant interception routes would fail to match when trailing slashes were enabled.

### How?

1. **Removed `internal: true` flag** from interception route rewrites in `generate-interception-routes-rewrites.ts`. This ensures they go through the same regex modification process as regular routes, properly handling trailing slash normalization.

2. **Added comprehensive test coverage** by wrapping the parallel routes and interception test suite in `describe.each([true, false])` to test both trailing slash configurations. Updated all URL assertions to account for the trailing slash setting.

3. **Refactored `build-custom-route.ts`** to scope the `source` variable closer to where it's actually used, preventing unnecessary computation for internal routes that already provide a regex.

4. **Moved `next.config.js` inline** to the test file for better organization and to support the parameterized test structure.

Changed files:
filename: packages/next/src/lib/build-custom-route.ts
patch: @@ -37,14 +37,6 @@ export function buildCustomRoute(
     delimiter: '/', // default is `/#?`, but Next does not pass query info
   })
 
-  let source = compiled.source
-  if (!route.internal) {
-    source = modifyRouteRegex(
-      source,
-      type === 'redirect' ? restrictedRedirectPaths : undefined
-    )
-  }
-
   // If this is an internal rewrite and it already provides a regex, use it
   // otherwise, normalize the source to a regex.
   let regex: string
@@ -54,6 +46,14 @@ export function buildCustomRoute(
     !('regex' in route) ||
     typeof route.regex !== 'string'
   ) {
+    let source = compiled.source
+    if (!route.internal) {
+      source = modifyRouteRegex(
+        source,
+        type === 'redirect' ? restrictedRedirectPaths : undefined
+      )
+    }
+
     regex = normalizeRouteRegex(source)
   } else {
     regex = route.regex

filename: packages/next/src/lib/generate-interception-routes-rewrites.ts
patch: @@ -52,7 +52,6 @@ export function generateInterceptionRoutesRewrites(
             value: headerRegex,
           },
         ],
-        internal: true,
         regex: source.namedRegex,
       })
     }

filename: test/e2e/app-dir/parallel-routes-and-interception/next.config.js
patch: @@ -1,21 +0,0 @@
-/**
- * @type {import('next').NextConfig}
- */
-const nextConfig = {
-  async rewrites() {
-    return {
-      beforeFiles: [
-        {
-          source: '/foo',
-          destination: '/en/foo',
-        },
-        {
-          source: '/photos',
-          destination: '/en/photos',
-        },
-      ],
-    }
-  },
-}
-
-module.exports = nextConfig

filename: test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts
patch: @@ -1,926 +1,1060 @@
 import { nextTestSetup, FileRef } from 'e2e-utils'
+import { NextConfig } from 'next'
 import { check, retry } from 'next-test-utils'
 import path from 'path'
 
-describe('parallel-routes-and-interception', () => {
-  const { next, isNextDev, isNextStart } = nextTestSetup({
-    files: __dirname,
-  })
+const nextConfig: NextConfig = {
+  async rewrites() {
+    return {
+      beforeFiles: [
+        {
+          source: '/foo',
+          destination: '/en/foo',
+        },
+        {
+          source: '/photos',
+          destination: '/en/photos',
+        },
+      ],
+    }
+  },
+}
+
+describe.each([true, false])(
+  'parallel-routes-and-interception (trailingSlash: %s)',
+  (trailingSlash) => {
+    const { next, isNextDev, isNextStart } = nextTestSetup({
+      files: __dirname,
+      nextConfig: {
+        trailingSlash,
+        ...nextConfig,
+      },
+    })
+
+    describe('parallel routes', () => {
+      it('should support parallel route tab bars', async () => {
+        const browser = await next.browser('/parallel-tab-bar')
+
+        const hasHome = async () => {
+          await check(
+            () => browser.waitForElementByCss('#home').text(),
+            'Tab bar page (@children)'
+          )
+        }
+        const hasViewsHome = async () => {
+          await check(
+            () => browser.waitForElementByCss('#views-home').text(),
+            'Views home'
+          )
+        }
+        const hasViewDuration = async () => {
+          await check(
+            () => browser.waitForElementByCss('#view-duration').text(),
+            'View duration'
+          )
+        }
+        const hasImpressions = async () => {
+          await check(
+            () => browser.waitForElementByCss('#impressions').text(),
+            'Impressions'
+          )
+        }
+        const hasAudienceHome = async () => {
+          await check(
+            () => browser.waitForElementByCss('#audience-home').text(),
+            'Audience home'
+          )
+        }
+        const hasDemographics = async () => {
+          await check(
+            () => browser.waitForElementByCss('#demographics').text(),
+            'Demographics'
+          )
+        }
+        const hasSubscribers = async () => {
+          await check(
+            () => browser.waitForElementByCss('#subscribers').text(),
+            'Subscribers'
+          )
+        }
+        const checkUrlPath = async (path: string) => {
+          await check(
+            () => browser.url(),
+            `${next.url}/parallel-tab-bar${path}${trailingSlash ? '/' : ''}`
+          )
+        }
+
+        // Initial page
+        const step1 = async () => {
+          await hasHome()
+          await hasViewsHome()
+          await hasAudienceHome()
+          await checkUrlPath('')
+        }
+
+        await step1()
+
+        console.log('step1')
+        // Navigate to /views/duration
+        await browser.elementByCss('#view-duration-link').click()
+
+        const step2 = async () => {
+          await hasHome()
+          await hasViewDuration()
+          await hasAudienceHome()
+          await checkUrlPath('/view-duration')
+        }
+
+        await step2()
+        console.log('step2')
 
-  describe('parallel routes', () => {
-    it('should support parallel route tab bars', async () => {
-      const browser = await next.browser('/parallel-tab-bar')
+        // Navigate to /views/impressions
+        await browser.elementByCss('#impressions-link').click()
+
+        const step3 = async () => {
+          await hasHome()
+          await hasImpressions()
+          await hasAudienceHome()
+          await checkUrlPath('/impressions')
+        }
+
+        await step3()
+        console.log('step3')
+
+        // Navigate to /audience/demographics
+        await browser.elementByCss('#demographics-link').click()
+
+        const step4 = async () => {
+          await hasHome()
+          await hasImpressions()
+          await hasDemographics()
+          await checkUrlPath('/demographics')
+        }
+
+        await step4()
+        console.log('step4')
+
+        // Navigate to /audience/subscribers
+        await browser.elementByCss('#subscribers-link').click()
+
+        const step5 = async () => {
+          await hasHome()
+          await hasImpressions()
+          await hasSubscribers()
+          await checkUrlPath('/subscribers')
+        }
+
+        await step5()
+        console.log('step5')
+
+        // Navigate to /
+        await browser.elementByCss('#home-link-audience').click()
+
+        await checkUrlPath('')
+
+        // TODO: home link behavior
+        // await step1()
+
+        // TODO: fix back/forward navigation test
+        // Test that back navigation works as intended
+        await browser.back()
+        await step5()
+        console.log('step5 back')
+        await browser.back()
+        await step4()
+        console.log('step4 back')
+        await browser.back()
+        await step3()
+        console.log('step3 back')
+
+        await browser.back()
+        await step2()
+        console.log('step2 back')
+        await browser.back()
+        await step1()
+        console.log('step1 back')
+        console.log('step6')
+
+        // Test that forward navigation works as intended
+        await browser.forward()
+        await step2()
+        console.log('step2 forward')
+        await browser.forward()
+        await step3()
+        console.log('step3 forward')
+        await browser.forward()
+        await step4()
+        console.log('step4 forward')
+        await browser.forward()
+        await step5()
+      })
+
+      it('should match parallel routes', async () => {
+        const $ = await next.render$('/parallel/nested')
+        const pageText = $('#parallel-layout').text()
+        expect(pageText).toContain('parallel/layout')
+        expect(pageText).toContain('parallel/@foo/nested/layout')
+        expect(pageText).toContain('parallel/@foo/nested/@a/page')
+        expect(pageText).toContain('parallel/@foo/nested/@b/page')
+        expect(pageText).toContain('parallel/@bar/nested/layout')
+        expect(pageText).toContain('parallel/@bar/nested/@a/page')
+        expect(pageText).toContain('parallel/@bar/nested/@b/page')
+        expect(pageText).toContain('parallel/nested/page')
+      })
+
+      it('should match parallel routes in route groups', async () => {
+        const $ = await next.render$('/parallel/nested-2')
+        const pageText = $('#parallel-layout').text()
+        expect(pageText).toContain('parallel/layout')
+        expect(pageText).toContain('parallel/(new)/layout')
+        expect(pageText).toContain('parallel/(new)/@baz/nested/page')
+      })
 
-      const hasHome = async () => {
+      it('should throw a 404 when no matching parallel route is found', async () => {
+        const browser = await next.browser('/parallel-tab-bar')
+        // we make sure the page is available through navigating
         await check(
           () => browser.waitForElementByCss('#home').text(),
           'Tab bar page (@children)'
         )
-      }
-      const hasViewsHome = async () => {
-        await check(
-          () => browser.waitForElementByCss('#views-home').text(),
-          'Views home'
-        )
-      }
-      const hasViewDuration = async () => {
+        await browser.elementByCss('#view-duration-link').click()
         await check(
           () => browser.waitForElementByCss('#view-duration').text(),
           'View duration'
         )
-      }
-      const hasImpressions = async () => {
-        await check(
-          () => browser.waitForElementByCss('#impressions').text(),
-          'Impressions'
+
+        // fetch /parallel-tab-bar/view-duration
+        const res = await next.fetch(
+          `${next.url}/parallel-tab-bar/view-duration`
         )
-      }
-      const hasAudienceHome = async () => {
+        const html = await res.text()
+        expect(html).toContain('page could not be found')
+      })
+
+      it('should render nested parallel routes', async () => {
+        const browser = await next.browser('/parallel-side-bar/nested/deeper')
         await check(
-          () => browser.waitForElementByCss('#audience-home').text(),
-          'Audience home'
+          () => browser.waitForElementByCss('#nested-deeper-main').text(),
+          'Nested deeper page'
         )
-      }
-      const hasDemographics = async () => {
+
         await check(
-          () => browser.waitForElementByCss('#demographics').text(),
-          'Demographics'
+          () => browser.waitForElementByCss('#nested-deeper-sidebar').text(),
+          'Nested deeper sidebar here'
         )
-      }
-      const hasSubscribers = async () => {
+
+        await browser
+          .elementByCss(
+            `[href="/parallel-side-bar/nested${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+
         await check(
-          () => browser.waitForElementByCss('#subscribers').text(),
-          'Subscribers'
+          () => browser.waitForElementByCss('#nested-main').text(),
+          'Nested page'
         )
-      }
-      const checkUrlPath = async (path: string) => {
-        await check(() => browser.url(), `${next.url}/parallel-tab-bar${path}`)
-      }
 
-      // Initial page
-      const step1 = async () => {
-        await hasHome()
-        await hasViewsHome()
-        await hasAudienceHome()
-        await checkUrlPath('')
-      }
+        await check(
+          () => browser.waitForElementByCss('#nested-sidebar').text(),
+          'Nested sidebar here'
+        )
 
-      await step1()
+        await browser
+          .elementByCss(
+            `[href="/parallel-side-bar${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
 
-      console.log('step1')
-      // Navigate to /views/duration
-      await browser.elementByCss('#view-duration-link').click()
+        await check(
+          () => browser.waitForElementByCss('#main').text(),
+          'homepage'
+        )
 
-      const step2 = async () => {
-        await hasHome()
-        await hasViewDuration()
-        await hasAudienceHome()
-        await checkUrlPath('/view-duration')
-      }
+        await check(
+          () => browser.waitForElementByCss('#sidebar-main').text(),
+          'root sidebar here'
+        )
+      })
 
-      await step2()
-      console.log('step2')
+      it('should support layout files in parallel routes', async () => {
+        const browser = await next.browser('/parallel-layout')
+        await check(
+          () => browser.waitForElementByCss('#parallel-layout').text(),
+          'parallel layout'
+        )
 
-      // Navigate to /views/impressions
-      await browser.elementByCss('#impressions-link').click()
+        // navigate to /parallel-layout/subroute
+        await browser
+          .elementByCss(
+            `[href="/parallel-layout/subroute${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(
+          () => browser.waitForElementByCss('#parallel-layout').text(),
+          'parallel layout'
+        )
+        await check(
+          () => browser.waitForElementByCss('#parallel-subroute').text(),
+          'parallel subroute layout'
+        )
+      })
 
-      const step3 = async () => {
-        await hasHome()
-        await hasImpressions()
-        await hasAudienceHome()
-        await checkUrlPath('/impressions')
-      }
+      it('should only scroll to the parallel route that was navigated to', async () => {
+        const browser = await next.browser('/parallel-scroll')
 
-      await step3()
-      console.log('step3')
+        await browser.eval('window.scrollTo(0, 1000)')
+        const position = await browser.eval('window.scrollY')
+        console.log('position', position)
+        await browser
+          .elementByCss(
+            `[href="/parallel-scroll/nav${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await browser.waitForElementByCss('#modal')
+        // check that we didn't scroll back to the top
+        await check(() => browser.eval('window.scrollY'), position)
+      })
 
-      // Navigate to /audience/demographics
-      await browser.elementByCss('#demographics-link').click()
+      it('should apply the catch-all route to the parallel route if no matching route is found', async () => {
+        const browser = await next.browser('/parallel-catchall')
 
-      const step4 = async () => {
-        await hasHome()
-        await hasImpressions()
-        await hasDemographics()
-        await checkUrlPath('/demographics')
-      }
+        await browser
+          .elementByCss(
+            `[href="/parallel-catchall/bar${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(
+          () => browser.waitForElementByCss('#main').text(),
+          'bar slot'
+        )
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'slot catchall'
+        )
 
-      await step4()
-      console.log('step4')
+        await browser
+          .elementByCss(
+            `[href="/parallel-catchall/foo${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(() => browser.waitForElementByCss('#main').text(), 'foo')
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'foo slot'
+        )
 
-      // Navigate to /audience/subscribers
-      await browser.elementByCss('#subscribers-link').click()
+        await browser
+          .elementByCss(
+            `[href="/parallel-catchall/baz${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(
+          () => browser.waitForElementByCss('#main').text(),
+          /main catchall/
+        )
+        await check(
+          () => browser.waitForElementByCss('#main').text(),
+          /catchall page client component/
+        )
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'baz slot'
+        )
+      })
 
-      const step5 = async () => {
-        await hasHome()
-        await hasImpressions()
-        await hasSubscribers()
-        await checkUrlPath('/subscribers')
-      }
+      it('should match the catch-all routes of the more specific path, if there is more than one catch-all route', async () => {
+        const browser = await next.browser('/parallel-nested-catchall')
 
-      await step5()
-      console.log('step5')
-
-      // Navigate to /
-      await browser.elementByCss('#home-link-audience').click()
-
-      await checkUrlPath('')
-
-      // TODO: home link behavior
-      // await step1()
-
-      // TODO: fix back/forward navigation test
-      // Test that back navigation works as intended
-      await browser.back()
-      await step5()
-      console.log('step5 back')
-      await browser.back()
-      await step4()
-      console.log('step4 back')
-      await browser.back()
-      await step3()
-      console.log('step3 back')
-
-      await browser.back()
-      await step2()
-      console.log('step2 back')
-      await browser.back()
-      await step1()
-      console.log('step1 back')
-      console.log('step6')
-
-      // Test that forward navigation works as intended
-      await browser.forward()
-      await step2()
-      console.log('step2 forward')
-      await browser.forward()
-      await step3()
-      console.log('step3 forward')
-      await browser.forward()
-      await step4()
-      console.log('step4 forward')
-      await browser.forward()
-      await step5()
-    })
+        await browser
+          .elementByCss(
+            `[href="/parallel-nested-catchall/foo${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(() => browser.waitForElementByCss('#main').text(), 'foo')
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'foo slot'
+        )
 
-    it('should match parallel routes', async () => {
-      const $ = await next.render$('/parallel/nested')
-      const pageText = $('#parallel-layout').text()
-      expect(pageText).toContain('parallel/layout')
-      expect(pageText).toContain('parallel/@foo/nested/layout')
-      expect(pageText).toContain('parallel/@foo/nested/@a/page')
-      expect(pageText).toContain('parallel/@foo/nested/@b/page')
-      expect(pageText).toContain('parallel/@bar/nested/layout')
-      expect(pageText).toContain('parallel/@bar/nested/@a/page')
-      expect(pageText).toContain('parallel/@bar/nested/@b/page')
-      expect(pageText).toContain('parallel/nested/page')
-    })
+        await browser
+          .elementByCss(
+            `[href="/parallel-nested-catchall/bar${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(() => browser.waitForElementByCss('#main').text(), 'bar')
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'slot catchall'
+        )
 
-    it('should match parallel routes in route groups', async () => {
-      const $ = await next.render$('/parallel/nested-2')
-      const pageText = $('#parallel-layout').text()
-      expect(pageText).toContain('parallel/layout')
-      expect(pageText).toContain('parallel/(new)/layout')
-      expect(pageText).toContain('parallel/(new)/@baz/nested/page')
-    })
+        await browser
+          .elementByCss(
+            `[href="/parallel-nested-catchall/foo/123${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
+        await check(() => browser.waitForElementByCss('#main').text(), 'foo id')
+        await check(
+          () => browser.waitForElementByCss('#slot-content').text(),
+          'foo id catchAll'
+        )
+      })
 
-    it('should throw a 404 when no matching parallel route is found', async () => {
-      const browser = await next.browser('/parallel-tab-bar')
-      // we make sure the page is available through navigating
-      await check(
-        () => browser.waitForElementByCss('#home').text(),
-        'Tab bar page (@children)'
-      )
-      await browser.elementByCss('#view-duration-link').click()
-      await check(
-        () => browser.waitForElementByCss('#view-duration').text(),
-        'View duration'
-      )
-
-      // fetch /parallel-tab-bar/view-duration
-      const res = await next.fetch(`${next.url}/parallel-tab-bar/view-duration`)
-      const html = await res.text()
-      expect(html).toContain('page could not be found')
-    })
+      it('should navigate with a link with prefetch=false', async () => {
+        const browser = await next.browser('/parallel-prefetch-false')
 
-    it('should render nested parallel routes', async () => {
-      const browser = await next.browser('/parallel-side-bar/nested/deeper')
-      await check(
-        () => browser.waitForElementByCss('#nested-deeper-main').text(),
-        'Nested deeper page'
-      )
+        // check if the default view loads
+        await check(
+          () => browser.waitForElementByCss('#default-parallel').text(),
+          'default view for parallel'
+        )
 
-      await check(
-        () => browser.waitForElementByCss('#nested-deeper-sidebar').text(),
-        'Nested deeper sidebar here'
-      )
+        // check that navigating to /foo re-renders the layout to display @parallel/foo
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/parallel-prefetch-false/foo${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#parallel-foo')
+              .text(),
+          'parallel for foo'
+        )
+      })
 
-      await browser.elementByCss('[href="/parallel-side-bar/nested"]').click()
+      it('should display all parallel route params with useParams', async () => {
+        const browser = await next.browser('/parallel-dynamic/foo/bar')
 
-      await check(
-        () => browser.waitForElementByCss('#nested-main').text(),
-        'Nested page'
-      )
+        await check(
+          () => browser.waitForElementByCss('#foo').text(),
+          `{"slug":"foo","id":"bar"}`
+        )
 
-      await check(
-        () => browser.waitForElementByCss('#nested-sidebar').text(),
-        'Nested sidebar here'
-      )
+        await check(
+          () => browser.waitForElementByCss('#bar').text(),
+          `{"slug":"foo","id":"bar"}`
+        )
+      })
 
-      await browser.elementByCss('[href="/parallel-side-bar"]').click()
+      it('should load CSS for a default page that exports another page', async () => {
+        const browser = await next.browser('/default-css')
 
-      await check(() => browser.waitForElementByCss('#main').text(), 'homepage')
+        expect(
+          await browser.eval(
+            `window.getComputedStyle(document.getElementById("red-text")).color`
+          )
+        ).toBe('rgb(255, 0, 0)')
 
-      await check(
-        () => browser.waitForElementByCss('#sidebar-main').text(),
-        'root sidebar here'
-      )
-    })
+        // the more page will now be using the page's `default.tsx` file, which re-exports the root page.
+        await browser
+          .elementByCss(`[href="/default-css/more${trailingSlash ? '/' : ''}"]`)
+          .click()
 
-    it('should support layout files in parallel routes', async () => {
-      const browser = await next.browser('/parallel-layout')
-      await check(
-        () => browser.waitForElementByCss('#parallel-layout').text(),
-        'parallel layout'
-      )
-
-      // navigate to /parallel-layout/subroute
-      await browser.elementByCss('[href="/parallel-layout/subroute"]').click()
-      await check(
-        () => browser.waitForElementByCss('#parallel-layout').text(),
-        'parallel layout'
-      )
-      await check(
-        () => browser.waitForElementByCss('#parallel-subroute').text(),
-        'parallel subroute layout'
-      )
-    })
+        expect(
+          await browser.eval(
+            `window.getComputedStyle(document.getElementById("red-text")).color`
+          )
+        ).toBe('rgb(255, 0, 0)')
 
-    it('should only scroll to the parallel route that was navigated to', async () => {
-      const browser = await next.browser('/parallel-scroll')
+        // ensure that everything still works on a fresh load
+        await browser.refresh()
 
-      await browser.eval('window.scrollTo(0, 1000)')
-      const position = await browser.eval('window.scrollY')
-      console.log('position', position)
-      await browser.elementByCss('[href="/parallel-scroll/nav"]').click()
-      await browser.waitForElementByCss('#modal')
-      // check that we didn't scroll back to the top
-      await check(() => browser.eval('window.scrollY'), position)
-    })
+        expect(
+          await browser.eval(
+            `window.getComputedStyle(document.getElementById("red-text")).color`
+          )
+        ).toBe('rgb(255, 0, 0)')
+      })
 
-    it('should apply the catch-all route to the parallel route if no matching route is found', async () => {
-      const browser = await next.browser('/parallel-catchall')
-
-      await browser.elementByCss('[href="/parallel-catchall/bar"]').click()
-      await check(() => browser.waitForElementByCss('#main').text(), 'bar slot')
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'slot catchall'
-      )
-
-      await browser.elementByCss('[href="/parallel-catchall/foo"]').click()
-      await check(() => browser.waitForElementByCss('#main').text(), 'foo')
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'foo slot'
-      )
-
-      await browser.elementByCss('[href="/parallel-catchall/baz"]').click()
-      await check(
-        () => browser.waitForElementByCss('#main').text(),
-        /main catchall/
-      )
-      await check(
-        () => browser.waitForElementByCss('#main').text(),
-        /catchall page client component/
-      )
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'baz slot'
-      )
-    })
+      it('should handle a loading state', async () => {
+        const browser = await next.browser('/with-loading')
+        expect(await browser.elementById('slot').text()).toBe('Root Slot')
+        expect(await browser.elementById('children').text()).toBe('Root Page')
 
-    it('should match the catch-all routes of the more specific path, if there is more than one catch-all route', async () => {
-      const browser = await next.browser('/parallel-nested-catchall')
-
-      await browser
-        .elementByCss('[href="/parallel-nested-catchall/foo"]')
-        .click()
-      await check(() => browser.waitForElementByCss('#main').text(), 'foo')
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'foo slot'
-      )
-
-      await browser
-        .elementByCss('[href="/parallel-nested-catchall/bar"]')
-        .click()
-      await check(() => browser.waitForElementByCss('#main').text(), 'bar')
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'slot catchall'
-      )
-
-      await browser
-        .elementByCss('[href="/parallel-nested-catchall/foo/123"]')
-        .click()
-      await check(() => browser.waitForElementByCss('#main').text(), 'foo id')
-      await check(
-        () => browser.waitForElementByCss('#slot-content').text(),
-        'foo id catchAll'
-      )
-    })
+        // should have triggered a loading state
+        expect(
+          await browser
+            .elementByCss(
+              `[href="/with-loading/foo${trailingSlash ? '/' : ''}"]`
+            )
+            .click()
+            .waitForElementByCss('#loading-page')
+            .text()
+        ).toBe('Loading...')
+
+        // should eventually load the full page
+        await retry(async () => {
+          expect(await browser.elementById('slot').text()).toBe('Nested Slot')
+          expect(await browser.elementById('children').text()).toBe(
+            'Welcome to Foo Page'
+          )
+        })
+      })
 
-    it('should navigate with a link with prefetch=false', async () => {
-      const browser = await next.browser('/parallel-prefetch-false')
+      if (isNextDev) {
+        it('should support parallel routes with no page component', async () => {
+          const browser = await next.browser('/parallel-no-page/foo')
+          const timestamp = await browser.elementByCss('#timestamp').text()
 
-      // check if the default view loads
-      await check(
-        () => browser.waitForElementByCss('#default-parallel').text(),
-        'default view for parallel'
-      )
+          await new Promise((resolve) => {
+            setTimeout(resolve, 3000)
+          })
 
-      // check that navigating to /foo re-renders the layout to display @parallel/foo
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/parallel-prefetch-false/foo"]')
-            .click()
-            .waitForElementByCss('#parallel-foo')
-            .text(),
-        'parallel for foo'
-      )
-    })
+          await check(async () => {
+            // an invalid response triggers a fast refresh, so if the timestamp doesn't update, this behaved correctly
+            const newTimestamp = await browser.elementByCss('#timestamp').text()
+            return newTimestamp !== timestamp ? 'failure' : 'success'
+          }, 'success')
+        })
 
-    it('should display all parallel route params with useParams', async () => {
-      const browser = await next.browser('/parallel-dynamic/foo/bar')
+        it('should support nested parallel routes', async () => {
+          const browser = await next.browser('parallel-nested/home/nested')
+          const timestamp = await browser.elementByCss('#timestamp').text()
 
-      await check(
-        () => browser.waitForElementByCss('#foo').text(),
-        `{"slug":"foo","id":"bar"}`
-      )
+          await new Promise((resolve) => {
+            setTimeout(resolve, 3000)
+          })
 
-      await check(
-        () => browser.waitForElementByCss('#bar').text(),
-        `{"slug":"foo","id":"bar"}`
-      )
+          await check(async () => {
+            // an invalid response triggers a fast refresh, so if the timestamp doesn't update, this behaved correctly
+            const newTimestamp = await browser.elementByCss('#timestamp').text()
+            return newTimestamp !== timestamp ? 'failure' : 'success'
+          }, 'success')
+        })
+      }
     })
 
-    it('should load CSS for a default page that exports another page', async () => {
-      const browser = await next.browser('/default-css')
-
-      expect(
-        await browser.eval(
-          `window.getComputedStyle(document.getElementById("red-text")).color`
+    describe('route intercepting with dynamic routes', () => {
+      it('should render intercepted route', async () => {
+        const browser = await next.browser(
+          `/intercepting-routes-dynamic/photos${trailingSlash ? '/' : ''}`
         )
-      ).toBe('rgb(255, 0, 0)')
 
-      // the more page will now be using the page's `default.tsx` file, which re-exports the root page.
-      await browser.elementByCss('[href="/default-css/more"]').click()
+        // Check if navigation to modal route works
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-routes-dynamic/photos/next/123${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#user-intercept-page')
+              .text(),
+          'Intercepted Page'
+        )
 
-      expect(
-        await browser.eval(
-          `window.getComputedStyle(document.getElementById("red-text")).color`
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic/photos/next/123' +
+            (trailingSlash ? '/' : '')
         )
-      ).toBe('rgb(255, 0, 0)')
 
-      // ensure that everything still works on a fresh load
-      await browser.refresh()
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () =>
+            browser.refresh().waitForElementByCss('#user-regular-page').text(),
+          'Regular Page'
+        )
 
-      expect(
-        await browser.eval(
-          `window.getComputedStyle(document.getElementById("red-text")).color`
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic/photos/next/123' +
+            (trailingSlash ? '/' : '')
         )
-      ).toBe('rgb(255, 0, 0)')
+      })
     })
 
-    it('should handle a loading state', async () => {
-      const browser = await next.browser('/with-loading')
-      expect(await browser.elementById('slot').text()).toBe('Root Slot')
-      expect(await browser.elementById('children').text()).toBe('Root Page')
+    describe('route intercepting with prerendered dynamic routes ', () => {
+      it('should render intercepted route', async () => {
+        const browser = await next.browser(
+          '/intercepting-routes-dynamic-prerendered/photos'
+        )
 
-      // should have triggered a loading state
-      expect(
+        // Check if navigation to modal route works.
         await browser
-          .elementByCss('[href="/with-loading/foo"]')
+          .elementByCss(
+            `[href="/intercepting-routes-dynamic-prerendered/photos/1${trailingSlash ? '/' : ''}"]`
+          )
           .click()
-          .waitForElementByCss('#loading-page')
-          .text()
-      ).toBe('Loading...')
 
-      // should eventually load the full page
-      await retry(async () => {
-        expect(await browser.elementById('slot').text()).toBe('Nested Slot')
-        expect(await browser.elementById('children').text()).toBe(
-          'Welcome to Foo Page'
+        // This should load the intercepted page.
+        await retry(async () => {
+          expect(
+            await browser.waitForElementByCss('#photo-intercepted-1').text()
+          ).toBe('Photo INTERCEPTED 1')
+        })
+
+        // Check if url matches even though it was intercepted.
+        expect(await browser.url()).toBe(
+          next.url +
+            '/intercepting-routes-dynamic-prerendered/photos/1' +
+            (trailingSlash ? '/' : '')
         )
-      })
-    })
 
-    if (isNextDev) {
-      it('should support parallel routes with no page component', async () => {
-        const browser = await next.browser('/parallel-no-page/foo')
-        const timestamp = await browser.elementByCss('#timestamp').text()
+        // There must not be any errors from prefetching the intercepted page.
+        expect(
+          (await browser.log()).filter(({ source }) => source === 'error')
+        ).toEqual([])
 
-        await new Promise((resolve) => {
-          setTimeout(resolve, 3000)
-        })
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await browser.refresh()
+        expect(await browser.waitForElementByCss('#photo-page-1').text()).toBe(
+          'Photo PAGE 1'
+        )
 
-        await check(async () => {
-          // an invalid response triggers a fast refresh, so if the timestamp doesn't update, this behaved correctly
-          const newTimestamp = await browser.elementByCss('#timestamp').text()
-          return newTimestamp !== timestamp ? 'failure' : 'success'
-        }, 'success')
+        // Check if the url matches still.
+        expect(await browser.url()).toBe(
+          next.url +
+            '/intercepting-routes-dynamic-prerendered/photos/1' +
+            (trailingSlash ? '/' : '')
+        )
       })
+    })
 
-      it('should support nested parallel routes', async () => {
-        const browser = await next.browser('parallel-nested/home/nested')
-        const timestamp = await browser.elementByCss('#timestamp').text()
+    describe('route intercepting with dynamic optional catch-all routes', () => {
+      it('should render intercepted route', async () => {
+        const browser = await next.browser(
+          `/intercepting-routes-dynamic-catchall/photos${trailingSlash ? '/' : ''}`
+        )
 
-        await new Promise((resolve) => {
-          setTimeout(resolve, 3000)
-        })
+        // Check if navigation to modal route works
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-routes-dynamic-catchall/photos/optional-catchall/123${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#optional-catchall-intercept-page')
+              .text(),
+          'Intercepted Page'
+        )
 
-        await check(async () => {
-          // an invalid response triggers a fast refresh, so if the timestamp doesn't update, this behaved correctly
-          const newTimestamp = await browser.elementByCss('#timestamp').text()
-          return newTimestamp !== timestamp ? 'failure' : 'success'
-        }, 'success')
-      })
-    }
-  })
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic-catchall/photos/optional-catchall/123' +
+            (trailingSlash ? '/' : '')
+        )
 
-  describe('route intercepting with dynamic routes', () => {
-    it('should render intercepted route', async () => {
-      const browser = await next.browser('/intercepting-routes-dynamic/photos')
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () =>
+            browser
+              .refresh()
+              .waitForElementByCss('#optional-catchall-regular-page')
+              .text(),
+          'Regular Page'
+        )
 
-      // Check if navigation to modal route works
-      await check(
-        () =>
-          browser
-            .elementByCss(
-              '[href="/intercepting-routes-dynamic/photos/next/123"]'
-            )
-            .click()
-            .waitForElementByCss('#user-intercept-page')
-            .text(),
-        'Intercepted Page'
-      )
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes-dynamic/photos/next/123'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () =>
-          browser.refresh().waitForElementByCss('#user-regular-page').text(),
-        'Regular Page'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes-dynamic/photos/next/123'
-      )
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic-catchall/photos/optional-catchall/123' +
+            (trailingSlash ? '/' : '')
+        )
+      })
     })
-  })
 
-  describe('route intercepting with prerendered dynamic routes ', () => {
-    it('should render intercepted route', async () => {
-      const browser = await next.browser(
-        '/intercepting-routes-dynamic-prerendered/photos'
-      )
+    describe('route intercepting with dynamic catch-all routes', () => {
+      it('should render intercepted route', async () => {
+        const browser = await next.browser(
+          `/intercepting-routes-dynamic-catchall/photos${trailingSlash ? '/' : ''}`
+        )
 
-      // Check if navigation to modal route works.
-      await browser
-        .elementByCss(
-          '[href="/intercepting-routes-dynamic-prerendered/photos/1"]'
+        // Check if navigation to modal route works
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-routes-dynamic-catchall/photos/catchall/123${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#catchall-intercept-page')
+              .text(),
+          'Intercepted Page'
         )
-        .click()
 
-      // This should load the intercepted page.
-      await retry(async () => {
-        expect(
-          await browser.waitForElementByCss('#photo-intercepted-1').text()
-        ).toBe('Photo INTERCEPTED 1')
-      })
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic-catchall/photos/catchall/123' +
+            (trailingSlash ? '/' : '')
+        )
 
-      // Check if url matches even though it was intercepted.
-      expect(await browser.url()).toBe(
-        next.url + '/intercepting-routes-dynamic-prerendered/photos/1'
-      )
-
-      // There must not be any errors from prefetching the intercepted page.
-      expect(
-        (await browser.log()).filter(({ source }) => source === 'error')
-      ).toEqual([])
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await browser.refresh()
-      expect(await browser.waitForElementByCss('#photo-page-1').text()).toBe(
-        'Photo PAGE 1'
-      )
-
-      // Check if the url matches still.
-      expect(await browser.url()).toBe(
-        next.url + '/intercepting-routes-dynamic-prerendered/photos/1'
-      )
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () =>
+            browser
+              .refresh()
+              .waitForElementByCss('#catchall-regular-page')
+              .text(),
+          'Regular Page'
+        )
+
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes-dynamic-catchall/photos/catchall/123' +
+            (trailingSlash ? '/' : '')
+        )
+      })
     })
-  })
 
-  describe('route intercepting with dynamic optional catch-all routes', () => {
-    it('should render intercepted route', async () => {
-      const browser = await next.browser(
-        '/intercepting-routes-dynamic-catchall/photos'
-      )
+    describe('route intercepting', () => {
+      it('should render intercepted route', async () => {
+        const browser = await next.browser(
+          `/intercepting-routes/feed${trailingSlash ? '/' : ''}`
+        )
 
-      // Check if navigation to modal route works
-      await check(
-        () =>
-          browser
-            .elementByCss(
-              '[href="/intercepting-routes-dynamic-catchall/photos/optional-catchall/123"]'
-            )
-            .click()
-            .waitForElementByCss('#optional-catchall-intercept-page')
-            .text(),
-        'Intercepted Page'
-      )
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url +
-          '/intercepting-routes-dynamic-catchall/photos/optional-catchall/123'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () =>
-          browser
-            .refresh()
-            .waitForElementByCss('#optional-catchall-regular-page')
-            .text(),
-        'Regular Page'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url +
-          '/intercepting-routes-dynamic-catchall/photos/optional-catchall/123'
-      )
-    })
-  })
+        // Check if navigation to modal route works.
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-routes/feed/photos/1${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#photo-intercepted-1')
+              .text(),
+          'Photo INTERCEPTED 1'
+        )
 
-  describe('route intercepting with dynamic catch-all routes', () => {
-    it('should render intercepted route', async () => {
-      const browser = await next.browser(
-        '/intercepting-routes-dynamic-catchall/photos'
-      )
+        // Check if intercepted route was rendered while existing page content was removed.
+        // Content would only be preserved when combined with parallel routes.
+        // await check(() => browser.elementByCss('#feed-page').text()).not.toBe('Feed')
 
-      // Check if navigation to modal route works
-      await check(
-        () =>
-          browser
-            .elementByCss(
-              '[href="/intercepting-routes-dynamic-catchall/photos/catchall/123"]'
-            )
-            .click()
-            .waitForElementByCss('#catchall-intercept-page')
-            .text(),
-        'Intercepted Page'
-      )
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes-dynamic-catchall/photos/catchall/123'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () =>
-          browser
-            .refresh()
-            .waitForElementByCss('#catchall-regular-page')
-            .text(),
-        'Regular Page'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes-dynamic-catchall/photos/catchall/123'
-      )
-    })
-  })
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes/feed/photos/1' +
+            (trailingSlash ? '/' : '')
+        )
 
-  describe('route intercepting', () => {
-    it('should render intercepted route', async () => {
-      const browser = await next.browser('/intercepting-routes/feed')
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () => browser.refresh().waitForElementByCss('#photo-page-1').text(),
+          'Photo PAGE 1'
+        )
 
-      // Check if navigation to modal route works.
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-routes/feed/photos/1"]')
-            .click()
-            .waitForElementByCss('#photo-intercepted-1')
-            .text(),
-        'Photo INTERCEPTED 1'
-      )
-
-      // Check if intercepted route was rendered while existing page content was removed.
-      // Content would only be preserved when combined with parallel routes.
-      // await check(() => browser.elementByCss('#feed-page').text()).not.toBe('Feed')
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes/feed/photos/1'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () => browser.refresh().waitForElementByCss('#photo-page-1').text(),
-        'Photo PAGE 1'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes/feed/photos/1'
-      )
-    })
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes/feed/photos/1' +
+            (trailingSlash ? '/' : '')
+        )
+      })
 
-    it('should render an intercepted route from a slot', async () => {
-      const browser = await next.browser('/')
+      it('should render an intercepted route from a slot', async () => {
+        const browser = await next.browser('/')
 
-      await check(
-        () => browser.waitForElementByCss('#default-slot').text(),
-        'default from @slot'
-      )
+        await check(
+          () => browser.waitForElementByCss('#default-slot').text(),
+          'default from @slot'
+        )
 
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/nested"]')
-            .click()
-            .waitForElementByCss('#interception-slot')
-            .text(),
-        'interception from @slot/nested'
-      )
-
-      // Check if the client component is rendered
-      await check(
-        () => browser.waitForElementByCss('#interception-slot-client').text(),
-        'client component'
-      )
-
-      await check(
-        () => browser.refresh().waitForElementByCss('#nested').text(),
-        'hello world from /nested'
-      )
-    })
+        await check(
+          () =>
+            browser
+              .elementByCss(`[href="/nested${trailingSlash ? '/' : ''}"]`)
+              .click()
+              .waitForElementByCss('#interception-slot')
+              .text(),
+          'interception from @slot/nested'
+        )
 
-    it('should render an intercepted route at the top level from a nested path', async () => {
-      const browser = await next.browser('/nested-link')
+        // Check if the client component is rendered
+        await check(
+          () => browser.waitForElementByCss('#interception-slot-client').text(),
+          'client component'
+        )
 
-      await check(
-        () => browser.waitForElementByCss('#default-slot').text(),
-        'default from @slot'
-      )
+        await check(
+          () => browser.refresh().waitForElementByCss('#nested').text(),
+          'hello world from /nested'
+        )
+      })
 
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/nested"]')
-            .click()
-            .waitForElementByCss('#interception-slot')
-            .text(),
-        'interception from @slot/nested'
-      )
-
-      await check(
-        () => browser.refresh().waitForElementByCss('#nested').text(),
-        'hello world from /nested'
-      )
-    })
+      it('should render an intercepted route at the top level from a nested path', async () => {
+        const browser = await next.browser(
+          `/nested-link${trailingSlash ? '/' : ''}`
+        )
 
-    it('should render intercepted route from a nested route', async () => {
-      const browser = await next.browser('/intercepting-routes/feed/nested')
+        await check(
+          () => browser.waitForElementByCss('#default-slot').text(),
+          'default from @slot'
+        )
 
-      // Check if navigation to modal route works.
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-routes/feed/photos/1"]')
-            .click()
-            .waitForElementByCss('#photo-intercepted-1')
-            .text(),
-        'Photo INTERCEPTED 1'
-      )
-
-      // Check if intercepted route was rendered while existing page content was removed.
-      // Content would only be preserved when combined with parallel routes.
-      // await check(() => browser.elementByCss('#feed-page').text()).not.toBe('Feed')
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes/feed/photos/1'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () => browser.refresh().waitForElementByCss('#photo-page-1').text(),
-        'Photo PAGE 1'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-routes/feed/photos/1'
-      )
-    })
+        await check(
+          () =>
+            browser
+              .elementByCss(`[href="/nested${trailingSlash ? '/' : ''}"]`)
+              .click()
+              .waitForElementByCss('#interception-slot')
+              .text(),
+          'interception from @slot/nested'
+        )
 
-    it('should re-render the layout on the server when it had a default child route', async () => {
-      const browser = await next.browser('/parallel-non-intercepting')
+        await check(
+          () => browser.refresh().waitForElementByCss('#nested').text(),
+          'hello world from /nested'
+        )
+      })
 
-      // check if the default view loads
-      await check(
-        () => browser.waitForElementByCss('#default-parallel').text(),
-        'default view for parallel'
-      )
+      it('should render intercepted route from a nested route', async () => {
+        const browser = await next.browser(
+          `/intercepting-routes/feed/nested${trailingSlash ? '/' : ''}`
+        )
 
-      // check that navigating to /foo re-renders the layout to display @parallel/foo
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/parallel-non-intercepting/foo"]')
-            .click()
-            .waitForElementByCss('#parallel-foo')
-            .text(),
-        'parallel for foo'
-      )
+        // Check if navigation to modal route works.
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-routes/feed/photos/1${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#photo-intercepted-1')
+              .text(),
+          'Photo INTERCEPTED 1'
+        )
 
-      // check that navigating to /foo also re-renders the base children
-      await check(() => browser.elementByCss('#children-foo').text(), 'foo')
-    })
+        // Check if intercepted route was rendered while existing page content was removed.
+        // Content would only be preserved when combined with parallel routes.
+        // await check(() => browser.elementByCss('#feed-page').text()).not.toBe('Feed')
 
-    it('should render modal when paired with parallel routes', async () => {
-      const browser = await next.browser('/intercepting-parallel-modal/vercel')
-      // Check if navigation to modal route works.
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-parallel-modal/photo/1"]')
-            .click()
-            .waitForElementByCss('#photo-modal-1')
-            .text(),
-        'Photo MODAL 1'
-      )
-
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-parallel-modal/photo/2"]')
-            .click()
-            .waitForElementByCss('#photo-modal-2')
-            .text(),
-        'Photo MODAL 2'
-      )
-
-      // Check if modal was rendered while existing page content is preserved.
-      await check(
-        () => browser.elementByCss('#user-page').text(),
-        'Feed for vercel'
-      )
-
-      // Check if url matches even though it was intercepted.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-parallel-modal/photo/2'
-      )
-
-      // Trigger a refresh, this should load the normal page, not the modal.
-      await check(
-        () => browser.refresh().waitForElementByCss('#photo-page-2').text(),
-        'Photo PAGE 2'
-      )
-
-      // Check if the url matches still.
-      await check(
-        () => browser.url(),
-        next.url + '/intercepting-parallel-modal/photo/2'
-      )
-    })
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes/feed/photos/1' +
+            (trailingSlash ? '/' : '')
+        )
 
-    it('should support intercepting with beforeFiles rewrites', async () => {
-      const browser = await next.browser('/foo')
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () => browser.refresh().waitForElementByCss('#photo-page-1').text(),
+          'Photo PAGE 1'
+        )
 
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/photos"]')
-            .click()
-            .waitForElementByCss('#intercepted')
-            .text(),
-        'intercepted'
-      )
-    })
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-routes/feed/photos/1' +
+            (trailingSlash ? '/' : '')
+        )
+      })
 
-    it('should support intercepting local dynamic sibling routes', async () => {
-      const browser = await next.browser('/intercepting-siblings')
+      it('should re-render the layout on the server when it had a default child route', async () => {
+        const browser = await next.browser(
+          `/parallel-non-intercepting${trailingSlash ? '/' : ''}`
+        )
 
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-siblings/1"]')
-            .click()
-            .waitForElementByCss('#intercepted-sibling')
-            .text(),
-        '1'
-      )
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-siblings/2"]')
-            .click()
-            .waitForElementByCss('#intercepted-sibling')
-            .text(),
-        '2'
-      )
-      await check(
-        () =>
-          browser
-            .elementByCss('[href="/intercepting-siblings/3"]')
-            .click()
-            .waitForElementByCss('#intercepted-sibling')
-            .text(),
-        '3'
-      )
+        // check if the default view loads
+        await check(
+          () => browser.waitForElementByCss('#default-parallel').text(),
+          'default view for parallel'
+        )
 
-      await next.browser('/intercepting-siblings/1')
+        // check that navigating to /foo re-renders the layout to display @parallel/foo
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/parallel-non-intercepting/foo${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#parallel-foo')
+              .text(),
+          'parallel for foo'
+        )
 
-      await check(() => browser.waitForElementByCss('#main-slot').text(), '1')
-    })
+        // check that navigating to /foo also re-renders the base children
+        await check(() => browser.elementByCss('#children-foo').text(), 'foo')
+      })
 
-    it('should intercept on routes that contain hyphenated/special dynamic params', async () => {
-      const browser = await next.browser(
-        '/interception-route-special-params/some-random-param'
-      )
+      it('should render modal when paired with parallel routes', async () => {
+        const browser = await next.browser(
+          `/intercepting-parallel-modal/vercel${trailingSlash ? '/' : ''}`
+        )
+        // Check if navigation to modal route works.
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-parallel-modal/photo/1${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#photo-modal-1')
+              .text(),
+          'Photo MODAL 1'
+        )
 
-      await browser
-        .elementByCss(
-          "[href='/interception-route-special-params/some-random-param/some-page']"
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-parallel-modal/photo/2${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#photo-modal-2')
+              .text(),
+          'Photo MODAL 2'
         )
-        .click()
 
-      const interceptionText =
-        'Hello from [this-is-my-route]/@intercept/some-page. Param: some-random-param'
-      const pageText =
-        'Hello from [this-is-my-route]/some-page. Param: some-random-param'
+        // Check if modal was rendered while existing page content is preserved.
+        await check(
+          () => browser.elementByCss('#user-page').text(),
+          'Feed for vercel'
+        )
+
+        // Check if url matches even though it was intercepted.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-parallel-modal/photo/2' +
+            (trailingSlash ? '/' : '')
+        )
+
+        // Trigger a refresh, this should load the normal page, not the modal.
+        await check(
+          () => browser.refresh().waitForElementByCss('#photo-page-2').text(),
+          'Photo PAGE 2'
+        )
 
-      await retry(async () => {
-        expect(await browser.elementByCss('body').text()).toContain(
-          interceptionText
+        // Check if the url matches still.
+        await check(
+          () => browser.url(),
+          next.url +
+            '/intercepting-parallel-modal/photo/2' +
+            (trailingSlash ? '/' : '')
         )
+      })
+
+      it('should support intercepting with beforeFiles rewrites', async () => {
+        const browser = await next.browser(`/foo${trailingSlash ? '/' : ''}`)
 
-        expect(await browser.elementByCss('body').text()).not.toContain(
-          pageText
+        await check(
+          () =>
+            browser
+              .elementByCss(`[href="/photos${trailingSlash ? '/' : ''}"]`)
+              .click()
+              .waitForElementByCss('#intercepted')
+              .text(),
+          'intercepted'
         )
       })
 
-      await browser.refresh()
+      it('should support intercepting local dynamic sibling routes', async () => {
+        const browser = await next.browser(
+          `/intercepting-siblings${trailingSlash ? '/' : ''}`
+        )
 
-      await retry(async () => {
-        expect(await browser.elementByCss('body').text()).toContain(pageText)
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-siblings/1${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#intercepted-sibling')
+              .text(),
+          '1'
+        )
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-siblings/2${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#intercepted-sibling')
+              .text(),
+          '2'
+        )
+        await check(
+          () =>
+            browser
+              .elementByCss(
+                `[href="/intercepting-siblings/3${trailingSlash ? '/' : ''}"]`
+              )
+              .click()
+              .waitForElementByCss('#intercepted-sibling')
+              .text(),
+          '3'
+        )
 
-        expect(await browser.elementByCss('body').text()).not.toContain(
-          interceptionText
+        await next.browser(
+          `/intercepting-siblings/1${trailingSlash ? '/' : ''}`
         )
+
+        await check(() => browser.waitForElementByCss('#main-slot').text(), '1')
       })
-    })
 
-    if (isNextStart) {
-      it('should not have /default paths in the prerender manifest', async () => {
-        const prerenderManifest = JSON.parse(
-          await next.readFile('.next/prerender-manifest.json')
+      it('should intercept on routes that contain hyphenated/special dynamic params', async () => {
+        const browser = await next.browser(
+          `/interception-route-special-params/some-random-param${trailingSlash ? '/' : ''}`
         )
 
-        const routes = Object.keys(prerenderManifest.routes)
+        await browser
+          .elementByCss(
+            `[href="/interception-route-special-params/some-random-param/some-page${trailingSlash ? '/' : ''}"]`
+          )
+          .click()
 
-        for (const route of routes) {
-          expect(route.endsWith('/default')).toBe(false)
-        }
+        const interceptionText =
+          'Hello from [this-is-my-route]/@intercept/some-page. Param: some-random-param'
+        const pageText =
+          'Hello from [this-is-my-route]/some-page. Param: some-random-param'
+
+        await retry(async () => {
+          expect(await browser.elementByCss('body').text()).toContain(
+            interceptionText
+          )
+
+          expect(await browser.elementByCss('body').text()).not.toContain(
+            pageText
+          )
+        })
+
+        await browser.refresh()
+
+        await retry(async () => {
+          expect(await browser.elementByCss('body').text()).toContain(pageText)
+
+          expect(await browser.elementByCss('body').text()).not.toContain(
+            interceptionText
+          )
+        })
       })
-    }
-  })
-})
+
+      if (isNextStart) {
+        it('should not have /default paths in the prerender manifest', async () => {
+          const prerenderManifest = JSON.parse(
+            await next.readFile('.next/prerender-manifest.json')
+          )
+
+          const routes = Object.keys(prerenderManifest.routes)
+
+          for (const route of routes) {
+            expect(route.endsWith('/default')).toBe(false)
+          }
+        })
+      }
+    })
+  }
+)
 
 describe('parallel-routes-and-interception-conflicting-pages', () => {
   const { next, skipped } = nextTestSetup({
@@ -935,6 +1069,7 @@ describe('parallel-routes-and-interception-conflicting-pages', () => {
        }
       `,
     },
+    nextConfig,
   })
 
   if (skipped) return