next.js/test/e2e/app-dir/parallel-routes-root-param-dynamic-child/parallel-routes-root-param-dynamic-child.test.ts
parallel-routes-root-param-dynamic-child.test.ts201 lines6.5 KB
import { nextTestSetup } from 'e2e-utils'
import { createRouterAct } from 'router-act'
import { setTimeout } from 'node:timers/promises'
import { retry } from 'next-test-utils'

describe('parallel-routes-root-param-dynamic-child', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  async function createBrowserActor(
    url: string,
    {
      errorPage = false,
      waitForPrefetch = false,
    }: {
      errorPage?: boolean
      waitForPrefetch?: boolean
    } = {}
  ) {
    let act: ReturnType<typeof createRouterAct>
    const browser = await next.browser(url, {
      beforePageLoad(page) {
        act = createRouterAct(page, { allowErrorStatusCodes: [404] })
      },
      // throttling the CPU to rule out flakiness based on how quickly the page loads
      cpuThrottleRate: 6,
    })

    // If we're on the error page, we don't have a nav element to wait for.
    if (errorPage) {
      return { act, browser }
    }

    if (waitForPrefetch) {
      // The page has navigation links that will be automatically prefetched.
      // Some routes will 404 (like /es which isn't in generateStaticParams),
      // so we allow 404 status codes. Let's reveal the navigation links and let
      // the prefetching occur.
      await act(
        async () => {
          await browser.elementByCss('#reveal').click()

          // Ensure the navigation is visible
          await browser.elementByCss('#nav')

          // Wait for 500ms to ensure all links are visible
          await setTimeout(500)

          // Scroll to bottom to ensure all links enter viewport and trigger prefetches
          await browser.eval('window.scrollTo(0, document.body.scrollHeight)')

          // Wait for 500ms to ensure all links are visible
          await setTimeout(500)
        },
        // If we're in development, we don't have any prefetching to wait for.
        isNextDev ? 'no-requests' : undefined
      )
    } else {
      // Most assertions only need the nav revealed, and should not block on
      // background prefetch request completion.
      await browser.elementByCss('#reveal').click()
      await browser.elementByCss('#nav')
      await setTimeout(500)
      await browser.eval('window.scrollTo(0, document.body.scrollHeight)')
      await setTimeout(500)
    }

    return { act, browser }
  }

  if (!isNextDev) {
    describe('Prefetching', () => {
      it('prefetches the 404 pages correctly', async () => {
        await createBrowserActor('/en', { waitForPrefetch: true })

        // If we got here without errors, the prefetch responses completed successfully
        // without problematic redirects (like 307). Those will throw because
        // it'll hit the net::ERR_TOO_MANY_REDIRECTS error.
      })
    })
  }

  describe('Base Routes', () => {
    it.each(['en', 'fr'])(
      'should render a 200 for /%s (in generateStaticParams)',
      async (locale) => {
        const { browser } = await createBrowserActor(`/${locale}`)

        await retry(async () => {
          expect(await browser.elementByCss('#locale-page').text()).toBe(
            `Locale: ${locale}`
          )
        })
      }
    )

    it('should render a 404 for /es (not in generateStaticParams)', async () => {
      const { browser } = await createBrowserActor('/es', { errorPage: true })

      expect(await browser.elementByCss('.next-error-h1').text()).toBe('404')
    })
  })

  describe('Without generateStaticParams (no-gsp)', () => {
    it.each(['en', 'fr', 'es'])(
      'should render a 200 for /%s/no-gsp/stories/dynamic-123',
      async (locale) => {
        const { browser, act } = await createBrowserActor('/en')

        await act(async () => {
          await browser
            .elementByCss(`[href="/${locale}/no-gsp/stories/dynamic-123"]`)
            .click()
        })

        await retry(async () => {
          expect(await browser.elementByCss('#story-locale').text()).toBe(
            `Locale: ${locale}`
          )
          expect(await browser.elementByCss('#story-slug').text()).toBe(
            'Story: dynamic-123'
          )
        })
      }
    )

    it('should allow dynamic params even with /es locale', async () => {
      // Even though /es is not in the root generateStaticParams,
      // no-gsp routes should still work because they don't enforce static params
      const { browser } = await createBrowserActor(
        '/es/no-gsp/stories/dynamic-123'
      )

      expect(await browser.elementByCss('#story-locale').text()).toBe(
        'Locale: es'
      )
      expect(await browser.elementByCss('#story-slug').text()).toBe(
        'Story: dynamic-123'
      )
    })
  })

  describe('With generateStaticParams (gsp)', () => {
    describe('Static params (static-123)', () => {
      it.each(['en', 'fr'])(
        'should render a 200 for /%s/gsp/stories/static-123',
        async (locale) => {
          const { browser, act } = await createBrowserActor(`/${locale}`)

          await act(async () => {
            await browser
              .elementByCss(`[href="/${locale}/gsp/stories/static-123"]`)
              .click()
          })

          await retry(async () => {
            expect(await browser.elementByCss('#story-locale').text()).toBe(
              `Locale: ${locale}`
            )
            expect(await browser.elementByCss('#story-slug').text()).toBe(
              'Story: static-123'
            )
          })
        }
      )

      it('should render a 404 for /es/gsp/stories/static-123 (locale not in generateStaticParams)', async () => {
        const response = await next.fetch('/es/gsp/stories/static-123')
        expect(response.status).toBe(404)
      })
    })

    describe('Dynamic params (dynamic-123)', () => {
      it.each(['en', 'fr'])(
        'should render a 404 for /%s/gsp/stories/dynamic-123 (slug not in generateStaticParams)',
        async (locale) => {
          const { browser, act } = await createBrowserActor(`/${locale}`)

          await act(async () => {
            await browser
              .elementByCss(`[href="/${locale}/gsp/stories/dynamic-123"]`)
              .click()
          })

          await retry(async () => {
            expect(await browser.elementByCss('.next-error-h1').text()).toBe(
              '404'
            )
          })
        }
      )

      it('should render a 404 for /es/gsp/stories/dynamic-123 (both locale and slug not allowed)', async () => {
        const response = await next.fetch('/es/gsp/stories/dynamic-123')
        expect(response.status).toBe(404)
      })
    })
  })
})
Quest for Codev2.0.0
/
SIGN IN