d) {
    return
  }

  it('should generate route validation correctly', async () => {
    if (isNextDev) {
      await next.start()
      await next.fetch('/')
    } else {
      await next.build()
    }

    try {
      const dts = await next.readFile(`${getDistDir()}/types/validator.ts`)
      // sanity check that dev generation is working
      expect(dts).toContain('const handler = {} as typeof import(')
    } finally {
      if (isNextDev) {
        await next.stop()
      }
    }
  })

  it('should ha

nerated asynchronously after the server starts.
        // Might take a few tries before all the relevant types exist.
        await retry(async () => {
          const { stdout, stderr } = await execa('pnpm', ['tsc', '--noEmit'], {
            cwd: next.t

ve passing tsc after the server generated types', async () => {
    if (isNextDev) {
      await next.start()
    } else {
      await next.build()
    }
    try {
      if (isNextDev) {
        // In dev mode, route types are ge


nk'

export const links = [
  { href: '/', tag: 'pre-generated' },
  { href: '/metadata', tag: 'pre-generated' },
  { href: '/nested/a', tag: 'pre-generated' },
  { href: '/nested/b', tag: 'on-demand' },
  { href: '/nested/c', tag: 'on-demand' },
  { href: '/on-demand/a', tag: 'on-demand, no-gsp' },
  { href: '/on-demand/b', tag: 'on-demand, no-gsp' },
  { href: '/on-demand/c', tag: 'on-demand, no-gsp' },
  { href: '/loading/a', tag: 'loading.jsx, pre-generated' },
  { href: '/loading/b', tag: 'loading.jsx,

  { href: '/no-suspense/nested/b', tag: 'no suspense, on-demand' },
  { href: '/no-suspense/nested/c', tag: 'no suspense, on-demand' },
  { href: '/dynamic/force-dynamic', tag: "dynamic = 'force-dynamic'" },
  {
    href: '/dynamic/force-dynamic/nested/a'

 on-demand' },
  { href: '/loading/c', tag: 'loading.jsx, on-demand' },
  { href: '/static', tag: 'static' },
  { href: '/no-suspense', tag: 'no suspense' },
  { href: '/no-suspense/nested/a', tag: 'no suspense, pre-generated' },


ooth scroll optimization', () => {
  const { next } = nextTestSetup({
    files: __dirname + '/fixtures/optimized',
  })

  const getTopScroll = async (browser: any) =>
    await browser.eval('document.documentElement.scrollTop')

  const waitForScrollToComplete = async (browser: any, expectedY: number) => {
    await retry(async () => {
      const top = await getTopScroll(browser)
      expect(top).toBe(expectedY)
    })
  }

  const scrollTo = async (browser: any, y: number) => {
    await browser.eval(`

h scroll CSS and data attribute without warning', async () => {
    const browser = await next.browser('/page1')

    await scrollTo(browser, 1000)
    expect(await getTopScroll(browser)).toBe(1000)

    // Wait for page to be rendered
    await browser.wa

window.scrollTo(0, ${y})`)
    // Add a small delay for scroll to complete
    await browser.eval('new Promise(resolve => setTimeout(resolve, 100))')
    await waitForScrollToComplete(browser, y)
  }

  it('should work with smoot


connection } from 'next/server'

/// Page is suspended and being caught by the layout Suspense boundary
export default function Page() {
  return (

      <SuspendedComponent />

  )
}

async function SuspendedComponent() {
  await connection()
  await new Promise((resolve) => setTimeout(resolve, 500))
  return (

outer suspended component

      <NestedSuspendedComponent />

  )
}

async function NestedSuspendedComponent() {
  awa

Timeout(resolve, 500))
  return
nested suspended component

}

export async function generateMetadata() {
  // Slow but static metadata
  await new Promise((resolve) => setTimeout(resolve, 2 * 1000))
  return {
    title: 'dynamic-page - partial'


mport { nextTestSetup } from 'e2e-utils'

describe('rewrite-with-search-params', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  it('should not contain params in search params after rewrite', async () => {
    const deploymentHost = isNextDeploy ? new URL(next.url).hostname : null
    const shouldForceHostHeader =
      !isNextDeploy ||
      deploymentHost === 'localhost' ||
      deploymentHost === '127.0.0.1'

    const $ = await next.render$(
      '/galleries/123'

ON.parse($('#search-params-value').text())
    const params = JSON.parse($('#params-value').text())

    expect(searchParams).toEqual({
      param: 'value',
    })

    expect(params).toEqual({
      domain: expect.stringMatching(/[\w-]+/),
      section:

,
      {
        param: 'value',
      },
      {
        headers: shouldForceHostHeader
          ? {
              host: 'vercel-test.vercel.app',
            }
          : undefined,
      }
    )

    const searchParams = JS


dboxTotalErrorCount,
} from 'next-test-utils'
import stripAnsi from 'strip-ansi'

const expectedTimeoutErrorMessage =
  'Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".'

describe('use-cache-hanging-inputs', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    skipStart: process.env.NEXT_TEST_MODE !== 'dev',
  })

  if (skip

meout', async () => {
        const outputIndex = next.cliOutput.length
        const browser = await next.browser('/uncached-promise')

        // The request is pending while we stall on the hanging inputs, and
        // playwright will wait for the loa

ped) {
    return
  }

  if (isNextDev) {
    // TODO(restart-on-cache-miss): reenable when fixed
    describe.skip('when an uncached promise is used inside of "use cache"', () => {
      it('should show an error toast after a ti


rt { nextTestSetup } from 'e2e-utils'
import { ElementHandle } from 'playwright'

describe('scripts', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  // TODO: fix test case in webpack
  // It's failing with `Could not find the module ".../app/client#component.tsx#" in the React Client Manifest. This is probably a bug in the React Server Components bundler.`
  ;(process.env.IS_TURBOPACK_TEST ? it : it.skip).each(['app', 'pages'])(
    'encodes characters in %s router',
    async (rou

cript'
      )) as ElementHandle<HTMLScriptElement>[]
      expect(scripts.length).toBeGreaterThan(0)
      for (const script of scripts) {
        const src = await script.evaluate((script) => script.src)
        expect(src).not.toContain('#')
        exp

terType) => {
      const browser = await next.browser(routerType === 'app' ? '/' : '/pages')
      expect(await browser.elementByCss('p').text()).toBe('hello world')
      const scripts = (await browser.elementsByCss(
        's


 { nextTestSetup } from 'e2e-utils'

describe('app-dir static-generation-status', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should render the page using notFound with status 404', async () => {
    const { status } = await next.fetch('/not-found-page')
    expect(status).toBe(404)
  })

  it('should render the page using redirect with status 307', async () => {
    const { status } = await next.fetch('/redirect-page', {
      redirect: 'manual',
    })
    expect(status).t

})

  it('should respond with 308 status code if permanent flag is set', async () => {
    const { status } = await next.fetch('/redirect-permanent', {
      redirect: 'manual',
    })
    expect(status).toBe(308)
  })

  it('should render the non existed

oBe(307)
  })

  it('should render the client page using redirect with status 307', async () => {
    const { status } = await next.fetch('/redirect-client-page', {
      redirect: 'manual',
    })
    expect(status).toBe(307)


'
import { getPrerenderOutput } from './utils'
import { retry } from 'next-test-utils'

describe('Cache Components Errors', () => {
  const { next, isTurbopack, isNextStart, skipped } = nextTestSetup({
    files: __dirname + '/fixtures/console-patch',
    skipDeployment: true,
    skipStart: !isNextDev,
    env: {
      NODE_OPTIONS: '--require ./patch-console.js',
    },
  })

  if (skipped) {
    return
  }

  let cliOutputLength: number

  beforeEach(async () => {
    cliOutputLength = next.cliOutput.len

warn about sync IO if console.log is patched to call new Date() internally', async () => {
          await next.fetch('/404')

          // Wait for after 404 is rendered to the console.
          await retry(async () => {
            expect(next.cliOutput

gth
  })

  afterEach(async () => {
    if (isNextStart) {
      await next.stop()
    }
  })

  describe('Sync IO in console methods', () => {
    describe('Console Patching', () => {
      if (isNextDev) {
        it('does not


f (skipped) {
    return
  }

  describe('Warning for Bypassing Caches in Dev', () => {
    async function enableDraftMode() {
      const draftRes = await next.fetch('/api/draft/enable', {
        redirect: 'manual',
      })
      const setCookie = draftRes.headers.get('set-cookie')
      const cookie = setCookie?.split(';', 1)[0]

      expect(cookie).toBeTruthy()
      return cookie!
    }

    if (isNextDev) {
      it('warns if you render with cache-control: no-cache in dev on initial page load', asyn

'/', {
          headers: { 'cache-control': 'no-cache' },
        })

        expect(stripGetLines(next.cliOutput.slice(from)))
          .toMatchInlineSnapshot(`
         "Route / is rendering with server caches disabled. For this navigation, Component M

c () => {
        // Wait for pages to be loaded to ensure the logging for the test page is the only thing that can be logged.
        await next.fetch('/404')
        const from = next.cliOutput.length

        await next.fetch(


} = nextTestSetup({
    files: __dirname + '/fixtures/http-access-fallback-prerender',
    skipStart: !isNextDev,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  let cliOutputLength: number

  beforeEach(() => {
    cliOutputLength = next.cliOutput.length
  })

  afterEach(async () => {
    if (isNextStart) {
      await next.stop()
    }
  })

  const testCases: { isDebugPrerender: boolean; name: string }[] = []

  if (isNextDev) {
    testCases.push({ isDebugPrerender: false, name: 'Dev

der',
      })
    }
    if (!prerenderMode || prerenderMode === 'false') {
      testCases.push({
        isDebugPrerender: false,
        name: 'Build Without --debug-prerender',
      })
    }
  }

  describe.each(testCases)('$name', ({ isDebugPrerender

' })
  } else {
    const prerenderMode = process.env.NEXT_TEST_DEBUG_PRERENDER
    if (!prerenderMode || prerenderMode === 'true') {
      testCases.push({
        isDebugPrerender: true,
        name: 'Build With --debug-preren


tTestSetup } from 'e2e-utils'

describe('Lazy Module Init', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname + '/fixtures/lazy-module-init',
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isNextDev) {
    it('does not run in dev', () => {})
    return
  }

  it('should build statically even if module scope uses sync APIs like current time and random', async () => {
    try {
      await next.start()
    } catch {
      throw new

xpect(next.cliOutput).toContain('◐ /[dyn]')
    let $

    $ = await next.render$('/server')
    expect($('#id').text().length).toBeGreaterThan(0)

    $ = await next.render$('/client')
    expect($('#id').text().length).toBeGreaterThan(0)

    $ = await n

 Error('expected build not to fail for fully static project')
    }

    expect(next.cliOutput).toContain('○ /server')
    expect(next.cliOutput).toContain('○ /client')
    expect(next.cliOutput).toContain('○ /client-page')
    e


t: true,
  })

  if (skipped) {
    return
  }

  if (isNextDev) {
    // In next dev there really isn't a prospective render but we still assert we error on the first visit to each page
    it('should error on the first visit to each page', async () => {
      let res

      res = await next.fetch('/error')
      expect(res.status).toBe(500)
      res = await next.fetch('/error')
      expect(res.status).toBe(200)
      res = await next.fetch('/error')
      expect(res.status).toBe(200)

      res = await

   res = await next.fetch('/null')
      expect(res.status).toBe(500)
      res = await next.fetch('/null')
      expect(res.status).toBe(200)
      res = await next.fetch('/null')
      expect(res.status).toBe(200)

      // To make disambiguating cli out

next.fetch('/routes/error')
      expect(res.status).toBe(500)
      res = await next.fetch('/routes/error')
      expect(res.status).toBe(200)
      res = await next.fetch('/routes/error')
      expect(res.status).toBe(200)


 the data access
                 - If the runtime data is \`params\` and they're known, prerender them with \`generateStaticParams\`
                 - Set \`export const instant = false\` to allow a blocking route

               Learn more: https://nextjs.org/docs/messages/blocking-route
                   at a (<next-dist-dir>)
                   at b (<next-dist-dir>)
                   at c (<next-dist-dir>)
                   at d (<next-dist-dir>)
                   at e (<next-dist-dir>)

anonymous>)
                   at body (<anonymous>)
                   at html (<anonymous>)
                   at k (<next-dist-dir>)
                   at l (<next-dist-dir>)
                   at m (<next-dist-dir>)
                   at n (<next-dist-

         at f (<next-dist-dir>)
                   at g (<next-dist-dir>)
                   at h (<next-dist-dir>)
                   at i (<next-dist-dir>)
                   at j (<next-dist-dir>)
                   at main (<


 { isNextDev, nextTestSetup } from 'e2e-utils'

describe('Cache Components Errors', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname + '/fixtures/unstable-deprecations',
  })

  if (skipped) {
    return
  }

  describe('Deprecating `unstable` prefix for `cacheLife` and `cacheTag`', () => {
    it('warns if you use `cacheLife` through `unstable_cacheLife`', async () => {
      if (isNextDev) {
        await next.browser('/life')

        expect(next.cliOutput).toContain(
          'E

use `cacheTag` through `unstable_cacheTag`', async () => {
      if (isNextDev) {
        await next.browser('/tag')

        expect(next.cliOutput).toContain(
          'Error: `unstable_cacheTag` was recently stabilized'
        )
      } else {

rror: `unstable_cacheLife` was recently stabilized'
        )
      } else {
        expect(next.cliOutput).toContain(
          'Error: `unstable_cacheLife` was recently stabilized'
        )
      }
    })
    it('warns if you


nopqrstuvwxyz'
const hostElementsUsedInFixtures = ['html', 'body', 'main', 'div']
const ignoredLines = [
  'Generating static pages',
  'Inlining static env',
  'Finalizing page optimization',
]

/**
 * Converts a module function sequence expression, e.g.:
 * - (0 , __TURBOPACK__imported__module__1836__.cookies)(...)
 * - (0 , c.cookies)(...)
 * - (0 , cookies.U)(...)
 * - (0 , e.U)(...)
 * to a deterministic, bundler-agnostic representation.
 */
export function convertModuleFunctionSequenceExpression(
  ou

 boolean; startingLineMatch?: string }
): string {
  const lines: string[] = []

  // If no starting line match is provided, we start from the beginning.
  let foundStartingLine = !startingLineMatch
  let a = 0
  let n = 0

  const replaceNextDistStackFram

tput: string
): string {
  return output.replace(/\(0 , \w+\.(\w+)\)\(\.\.\.\)/, '<module-function>()')
}

export function getDeterministicOutput(
  cliOutput: string,
  {
    isMinified,
    startingLineMatch,
  }: { isMinified:


ionOne, IndirectionTwo } from './indirection'
import { cookies } from 'next/headers'

export default async function Page() {
  return (
    <>
      <p>
        This page calls fetches eight times. Four are cached and Four are not.
        In each set of Four, two are wrapped in Suspense. This leaves two
        fetches that are uncached and not wrapped in Suspense which is
        considered an error when cacheComponents is enabled. We expect the build
        to fail with two component stacks that point t

 </IndirectionOne>
      <IndirectionTwo>
        <FetchingComponent nonce="c" />
        <Suspense fallback="loading...">
          <FetchingComponent nonce="d" />
        </Suspense>
      </IndirectionTwo>
      <FetchingComponent nonce="e" />
      <Su

o the offending IO
      </p>
      <IndirectionOne>
        <FetchingComponent nonce="a" cached={true} />
        <Suspense fallback="loading...">
          <FetchingComponent nonce="b" cached={true} />
        </Suspense>


se

export async function generateMetadata() {
  await new Promise((r) => setTimeout(r, 0))
  return { title: 'Dynamic Metadata' }
}

export default async function Page() {
  return (
    <>
      <p>
        This page is static except for `generateMetadata`. It opts into a fully
        dynamic, blocking route via `export const unstable_instant = false`.
        That opt-in is a synonym for wrapping the document body in a Suspense
        boundary — it says the user accepts a blocking route — but it is NOT

teMetadata`. So even with the
        opt-in, we still expect the dynamic-metadata error to be shown to nudge
        users back toward making `generateMetadata` static (or, secondarily,
        making the page partially dynamic).
      </p>
      <span id

 a
        documented mitigation for dynamic `genera


from 'react'

import { IndirectionOne, IndirectionTwo, IndirectionThree } from './indirection'

type SearchParams = { foo: string | string[] | undefined }
export default async function Page(props: {
  searchParams: Promise<SearchParams>
}) {
  return (
    <>
      <p>
        This page accesses Math.random() while prerendering and does so before
        something that is not wrapped in a Suspense boundary has finished
        rendering
      </p>
      <Suspense fallback={<Fallback />}>
        <Indirectio

   </IndirectionThree>
    </>
  )
}

function getRandomNumber() {
  return Math.random()
}

function RandomReadingComponent() {
  if (typeof window === 'undefined') {
    use(new Promise((r) => process.nextTick(r)))
  }

  const random = getRandomNumber()

nOne>
          <RandomReadingComponent />
        </IndirectionOne>
      </Suspense>
      <IndirectionTwo>
        <LongRunningComponent />
      </IndirectionTwo>
      <IndirectionThree>
        <ShortRunningComponent />


 { draftMode } from 'next/headers'
import { connection } from 'next/server'
import { Suspense } from 'react'

export default async function Page() {
  return (
    <>
      <p>
        This page accesses draftMode.isEnabled synchronously. This does not
        trigger dynamic, and the build should succeed. In dev mode, we do log an
        error for the sync access though.
      </p>
      <DraftModeReadingComponent />
      <Suspense>
        <Dynamic />
      </Suspense>
    </>
  )
}

async function Draf

rn (

      this component read the draftMode isEnabled status synchronously:{' '}
      {String(isEnabled)}

  )
}

// This component ensures that we're creating a partially prerendered page, so
// that we a

tModeReadingComponent() {
  await new Promise((r) => process.nextTick(r))
  // Cast to any as we removed UnsafeUnwrapped types, but still need to test with the sync access
  const isEnabled = (draftMode() as any).isEnabled
  retu


tion generateMetadata() {
  await new Promise((r) => setTimeout(r, 0))
  return { title: 'Dynamic Metadata' }
}

export default async function Page() {
  return (
    <>
      <p>
        This page is static except for `generateMetadata`. The root layout wraps
        the document body in a Suspense boundary. Wrapping the body in Suspense
        is the opt-in for a fully dynamic shell, which is the documented
        mitigation for dynamic `generateViewport`, but is NOT a documented
        mitigation for

ove
        body, we still expect the dynamic-metadata error to be shown to nudge
        users back toward making `generateMetadata` static (or, secondarily,
        making the page partially dynamic).

      sentinel

dynamic `generateMetadata`. So even with Suspense ab


 'next/cache'

async function innerCache() {
  'use cache'
  cacheLife({ expire: 60 }) // 1 minute, under the 5 minute threshold
  return Math.random()
}

async function outerCache() {
  'use cache'
  // Explicitly not setting a `cacheLife` here means this will use the implicit
  // default cache life, i.e. the shortest cache life of any nested 'use cache'
  // will be applied, or the values of the 'default' profile if none are nested.
  return innerCache()
}

export default async function Page() {
  let re

 cacheLife throws an error during
        prerendering.
      </p>
      <p>
        The inner cache function is cached with a 60 second expire time. Such a
        short-lived cache would normally create a dynamic hole and be excluded
        from prerend

sult: number | undefined
  try {
    result = await outerCache()
  } catch {}

  return (
    <>
      <p>
        This page tests that a nested "use cache" with low expire time inside
        another "use cache" without explicit


se((r) => setTimeout(r, 0))
  return { themeColor: 'black' }
}

export default async function Page() {
  return (
    <>
      <p>
        This page is dynamic and also has dynamic `generateViewport`. This is a
        build error because anything dynamic must be wrapped in a Suspense
        boundary. While you aren't directly in control of where viewport renders
        it semantically renders as part of the page preamble and so you must put
        a Suspense boundary around the root layout to opt into a

/p>
      <Suspense fallback={<Fallback />}>
        <Dynamic />
      </Suspense>
    </>
  )
}

function Fallback() {
  return
loading...

}

async function Dynamic() {
  await new Promise((r) => setTimeout(r))
  return <p id="

llowing dynamic
        in generateViewport.
      <


 connection } from 'next/server'
import { Suspense } from 'react'

export default async function Page() {
  return (
    <>
      <p>
        This page accesses headers synchronously at runtime. This triggers a
        type error. In dev mode, we also log an explicit error that `headers()`
        should be awaited.
      </p>
      <Suspense>
        <HeadersReadingComponent />
      </Suspense>
    </>
  )
}

async function HeadersReadingComponent() {
  // Await a connection to test the subsequent sync he

ast to any as we removed UnsafeUnwrapped types, but still need to test with the sync access
  const userAgent = (headers() as any).get('user-agent')
  return (
    <div>
      this component reads the `user-agent` header synchronously: {userAgent}
    </di

aders access at runtime.
  await connection()
  // C


e SearchParams = { foo: string | string[] | undefined }
export default function Page(props: { searchParams: Promise<SearchParams> }) {
  return (
    <>
      <p>
        This page accesses searchParams synchronously. This does not trigger
        dynamic, and the build should succeed. In dev mode, we do log an error
        for the sync access though.
      </p>
      <SearchParamsReadingComponent searchParams={props.searchParams} />
    </>
  )
}

function SearchParamsReadingComponent({
  searchParams,
}:

ast to any as we removed UnsafeUnwrapped types, but still need to test with the sync access
  const fooParam = (searchParams as any).foo
  return (

      this component reads the `foo` search param:{' '}
      {String(fooPara

 {
  searchParams: Promise<SearchParams>
}) {
  // C


 'next/cache'

async function innerCache() {
  'use cache'
  cacheLife({ revalidate: 0 })
  return Math.random()
}

async function outerCache() {
  'use cache'
  // Explicitly not setting a `cacheLife` here means this will use the implicit
  // default cache life, i.e. the shortest cache life of any nested 'use cache'
  // will be applied, or the values of the 'default' profile if none are nested.
  return innerCache()
}

export default async function Page() {
  let result: number | undefined
  try {
    re

   prerendering.
      </p>
      <p>
        The inner cache function is cached with a zero revalidate time. Such a
        short-lived cache would normally create a dynamic hole and be excluded
        from prerenders. However, when nested inside another

sult = await outerCache()
  } catch {}

  return (
    <>
      <p>
        This page tests that a nested "use cache" with zero revalidate inside
        another "use cache" without explicit cacheLife throws an error during


import { cookies } from 'next/headers'

import { SyncIO } from './client'

export default async function Page() {
  return (

          In this test we have two components. One reads `new Date()`
          synchronously in a client component render. The other awaits cookies
          in a Server Component.

          In this version neither component is wrapped in a Suspense boundary.
          There are no defined semantics about which reason is

        <RequestData />

  )
}

async function RequestData() {
  ;(await cookies()).get('foo')
  return (

Request Data Access

This component accesses request data without a Suspense bo

 more "valid" than
          another but with the current implementation we report the `new Date()`
          reason when failing the build.
        </p>
      </section>
      <section>
        <SyncIO />
      </section>


mport { cookies } from 'next/headers'
import { Suspense } from 'react'

import { SyncIO } from './client'

export default async function Page() {
  return (

          In this test we have two components. One reads `new Date()`
          synchronously in a client component render. The other awaits cookies
          in a Server Component.

          In this version the client component is not wrapped in a Suspense
          boundary. We expect thi

>
          <RequestData />
        </Suspense>

  )
}

async function RequestData() {
  ;(await cookies()).get('foo')
  return (

Request Data Access

This component accesses request data without

s to be a build failure with the reason
          pointing to the line where `new Date()` was called.

        <SyncIO />

        <Suspense fallback=""


mport { cookies } from 'next/headers'
import { Suspense } from 'react'

import { SyncIO } from './client'

export default async function Page() {
  return (

          In this test we have two components. One reads `new Date()`
          synchronously in a client component render. The other awaits cookies
          in a Server Component.

          In this version both components are wrapped in Suspense. We expect
          that this page will pr

<RequestData />
        </Suspense>

  )
}

async function RequestData() {
  ;(await cookies()).get('foo')
  return (

Request Data Access

This component accesses request data without a

erender with fallbacks around both components

        <Suspense fallback="">
          <SyncIO />
        </Suspense>

        <Suspense fallback="">


ort { cookies } from 'next/headers'

import { SyncIO } from './client'
import { Suspense } from 'react'

export default async function Page() {
  return (

          In this test we have two components. One reads `new Date()`
          synchronously in a client component render. The other awaits cookies
          in a Server Component.

          In this version the server component is not wrapped in a Suspense
          boundary. We expect the b

</Suspense>

        <RequestData />

  )
}

async function RequestData() {
  ;(await cookies()).get('foo')
  return (

Request Data Access

This component

uild to error with the reason referencing
          some dynamic data access. It should not mention `new Date()`.

        <Suspense fallback={
Loading...
}>
          <SyncIO />


'react'

import { IndirectionOne, IndirectionTwo, IndirectionThree } from './indirection'

export default async function Page() {
  return (
    <>
      <p>
        This page calls fetch three times. One is cached and outside of a
        Suspense boundary. The other two are uncached but inside Suspense
        boundaries. We expect this page to be dynamic but not produce a build
        error. If PPR is enabled we expect this page to be partially static.
        uncached.
      </p>
      <IndirectionOne>

>
      <IndirectionThree>
        <Suspense fallback={<Fallback />}>
          <FetchingComponent nonce="c" />
        </Suspense>
      </IndirectionThree>
    </>
  )
}

async function FetchingComponent({
  nonce,
  cached,
}: {
  nonce: string
  cached

        <FetchingComponent nonce="a" cached={true} />
      </IndirectionOne>
      <IndirectionTwo>
        <Suspense fallback={<Fallback />}>
          <FetchingComponent nonce="b" />
        </Suspense>
      </IndirectionTwo


from 'react'

import { IndirectionOne, IndirectionTwo, IndirectionThree } from './indirection'

type SearchParams = { foo: string | string[] | undefined }
export default async function Page(props: {
  searchParams: Promise<SearchParams>
}) {
  return (
    <>
      <p>
        This page accesses Math.random() while prerendering but it does so late
        enough in the render that all unfinished sub-trees have a defined
        Suspense boundary. This is fine and doesn't need to error the build.
      </p>

unningComponent />
        </IndirectionTwo>
      </Suspense>
      <IndirectionThree>
        <ShortRunningComponent />
      </IndirectionThree>
    </>
  )
}

function RandomReadingComponent() {
  if (typeof window === 'undefined') {
    use(new Promis

      <Suspense fallback={<Fallback />}>
        <IndirectionOne>
          <RandomReadingComponent />
        </IndirectionOne>
      </Suspense>
      <Suspense fallback={<Fallback />}>
        <IndirectionTwo>
          <LongR


rver'
import { Suspense } from 'react'

export default async function Page() {
  return (
    <>

        This page uses `connection()` inside `'use cache: private'`, which
        triggers an error at runtime.

      <Suspense fallback={
Loading...
}>
        <Private />
      </Suspense>
    </>
  )
}

async function Private() {
  'use cache: private'

  // TODO: this should be displayed as a Runtime Error even with this delay,
  // but right now we might not read it in time and lo

ise((resolve) => setTimeout(resolve))

  // Calling connection() in a cache context is not allowed. We're try/catching
  // here to ensure that, in dev mode, this error is shown even when it's caught
  // in userland.
  try {
    await connection()
  } cat

g it as as a console error instead.
  await new Prom


 { connection } from 'next/server'
import { Suspense } from 'react'

type SearchParams = { foo: string | string[] | undefined }
export default async function Page(props: {
  searchParams: Promise<SearchParams>
}) {
  return (
    <>
      <p>
        This page accesses searchParams synchronously. This does not trigger
        dynamic, and the build should succeed. In dev mode, we do log an error
        for the sync access though.
      </p>
      <SearchParamsReadingComponent searchParams={props.searchPara

pped types, but still need to test with the sync access
  const fooParam = (searchParams as any).foo
  return (

      this component reads the `foo` search param:{' '}
      {String(fooParam)}

  )
}

// This

ms} />
      <Suspense>
        <Dynamic />
      </Suspense>
    </>
  )
}

async function SearchParamsReadingComponent({
  searchParams,
}: {
  searchParams: Promise<SearchParams>
}) {
  // Cast to any as we removed UnsafeUnwra


xtTestSetup } from 'e2e-utils'

describe('app-dir - fetch warnings', () => {
  const { next, skipped, isNextDev } = nextTestSetup({
    skipDeployment: true,
    files: __dirname,
  })

  if (skipped) {
    return
  }

  beforeAll(async () => {
    // we don't need verbose logging (enabled by default in this Next app) for these tests to work
    // we avoid enabling it since it's not currently compatible with Turbopack.
    await next.stop()
    await next.deleteFile('next.config.js')
    await next.start()

xpect(next.cliOutput).toInclude(`
 │ GET https://next-data-api-endpoint.vercel.app/api/random?request-string
 │ │ ⚠ Specified "cache: force-cache" and "revalidate: 0", only one should be specified.`)
        })
      })

      it('should log when request i

    await next.fetch('/cache-revalidate')
  })

  if (isNextDev) {
    describe('force-cache and revalidate: 0', () => {
      it('should log when request input is a string', async () => {
        await retry(() => {
          e


t fetchCache = 'default-cache'

async function AnotherRsc() {
  const data = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?another-no-cache',
    {
      cache: 'no-cache',
    }
  ).then((res) => res.text())

  return
"another-no-cache" {data}

}

async function FirstRsc() {
  const dataNoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?no-cache',
    {
      cache: 'no-cache',
    }
  ).then((res) => res.text())

  const da

ata-api-endpoint.vercel.app/api/random?revalidate-0',
    {
      next: {
        revalidate: 0,
      },
    }
  ).then((res) => res.text())

  const dataRevalidateCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?revalidate-3

taForceCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?force-cache',
    {
      cache: 'force-cache',
    }
  ).then((res) => res.text())

  const dataRevalidate0 = await fetch(
    'https://next-d


 const fetchCache = 'default-cache'

export default async function Page() {
  await fetch(
    new Request(
      'https://next-data-api-endpoint.vercel.app/api/random?request-input'
    ),
    {
      next: {
        revalidate: 0,
      },
    }
  )

  await fetch(
    new Request(
      'https://next-data-api-endpoint.vercel.app/api/random?request-input-cache-override',
      {
        cache: 'force-cache',
      }
    ),
    {
      next: {
        revalidate: 0,
      },
    }
  )

  await fetch(
    '

t.vercel.app/api/random?no-store-request-input-cache-override',
      {
        cache: 'no-store',
      }
    ),
    {
      next: {
        revalidate: 3,
      },
    }
  )

  await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?no-sto

https://next-data-api-endpoint.vercel.app/api/random?request-string',
    {
      next: {
        revalidate: 0,
      },
      cache: 'force-cache',
    }
  )

  await fetch(
    new Request(
      'https://next-data-api-endpoin


ort { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('app-dir - server-actions-redirect-middleware-rewrite.test', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should redirect correctly in nodejs runtime with middleware rewrite', async () => {
    const browser = await next.browser('/server-action/node')
    await browser.waitForElementByCss('button').click()

    await retry(async () => {
      expect(await browser.waitForElementByCss('#red

ser = await next.browser('/server-action/edge')
    await browser.waitForElementByCss('button').click()

    await retry(async () => {
      expect(await browser.waitForElementByCss('#redirected').text()).toBe(
        'Redirected'
      )

      expect(aw

irected').text()).toBe(
        'Redirected'
      )
    })
    expect(await browser.url()).toBe(`${next.url}/redirect`)
  })

  it('should redirect correctly in edge runtime with middleware rewrite', async () => {
    const brow


on ShallowLayout({ children }) {
  return (
    <>

Shallow Routing

            Hash Navigation (non-Link)

          <Link href="/a" id="to-a">
            To A
          </Link>

            To A MPA Navigation

          <Link href="/b" id="to-b">
            To B

ynamic 1
          </Link>

          <Link href="/dynamic/2" id="to-dynamic-2">
            To Dynamic 2
          </Link>

          <Link href="/pushstate-data" id="to-pushstate-data">

</Link>

            To B MPA Navigation

          <Link href="/dynamic/1" id="to-dynamic-1">
            To D


ent'

import { Suspense } from 'react'
import { useSearchParams } from 'next/navigation'

function InnerPage() {
  const searchParams = useSearchParams()
  return (
    <>

PushState SearchParams

{searchParams.get('query')}

      <button
        onClick={() => {
          const url = new URL(window.location.href)
          const previousQuery = url.searchParams.get('query')
          url.searchParams.set(
            'query',

         )
          window.history.pushState({}, '', url)
        }}
        id="push-searchparams"
      >
        Push searchParam
      </button>
    </>
  )
}

export default function Page() {
  return (
    <Suspense>
      <InnerPage />
    </Suspen

previousQuery ? previousQuery + '-added' : 'foo'


use client'
import { useEffect, useState } from 'react'

export default function Page() {
  const [data, setData] = useState(null)
  const [updated, setUpdated] = useState(false)
  useEffect(() => {
    setData(window.history.state.myData)
  }, [])
  return (
    <>

PushState Data

      {updated ?

 : null}

{JSON.stringify(data)}

      <button
        onClick={() => {
          setData(window.history.state.myData)

et latest data
      </button>
      <button
        onClick={() => {
          window.history.pushState({ myData: { foo: 'bar' } }, '')
          setUpdated(true)
        }}
        id="push-state"
      >
        Push state
      </button>
    </>
  )
}

        }}
        id="get-latest"
      >
        G


spense } from 'react'
import { useSearchParams } from 'next/navigation'

function InnerPage() {
  const searchParams = useSearchParams()
  return (
    <>

PushState String Url

{searchParams.get('query')}

      <button
        onClick={() => {
          const previousQuery = new URL(window.location.href).searchParams.get(
            'query'
          )
          const url = `?query=${
            previousQuery ? previousQuery + '-added'

st previousQuery = new URL(window.location.href).searchParams.get(
            'query'
          )
          const url = `?query=${
            previousQuery ? previousQuery + '-added' : 'foo'
          }`

          window.history.pushState(null, '', url)

: 'foo'
          }`

          window.history.pushState({}, '', url)
        }}
        id="push-string-url"
      >
        Push searchParam using string url
      </button>

      <button
        onClick={() => {
          con


ageSize from 'image-size'
import path from 'path'

describe('app dir - Metadata API on the Edge runtime', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
  })

  describe('OG image route', () => {
    if (isNextStart) {
      it('should not bundle `ImageResponse` into the page worker', async () => {
        const middlewareManifest = JSON.parse(
          await next.readFile('.next/server/middleware-manifest.json')
        )

        const uniquePageFiles = [
          ...new S

', file))
              .includes('loadAdditionalAsset')
          }
        )
        expect(pageFilesThatHaveImageResponse).not.toBeEmpty()

        const uniqueAnotherFiles = [
          ...new Set<string>(
            middlewareManifest.functions['/ano

et<string>(middlewareManifest.functions['/page'].files),
        ]

        const pageFilesThatHaveImageResponse = uniquePageFiles.filter(
          (file) => {
            return next
              .readFileSync(path.join('.next


 from 'react'

// Validation level is 'experimental-manual-error' (set in next.config.ts).
// No implicit validation — only segments that explicitly opt in via
// `unstable_instant` are validated. When validated, the level is error,
// applying in dev AND build (build fails on violations).
//
// Children are wrapped in Suspense so that pages with runtime data
// accessed at the top of the page don't fail static-shell validation
// (the Suspense fallback renders into the static shell). Instant
// validation

an
// instant-specific violation when it runs.
export const unstable_instant = false

export default function RootLayout({ children }: { children: ReactNode }) {
  return (

        <Suspense fallback={
loading…
}>{children}</Su

flags "Suspense too high for instant navigation" as


('next/dynamic with CSP nonce', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should include nonce attribute on preload links generated by next/dynamic', async () => {
    const $ = await next.render$('/')

    // Check that preload links have the nonce attribute
    const preloadLinks = $('link[rel="preload"]')

    const dynamicPreloadLinks = preloadLinks.filter((_, element) => {
      const $element = $(element)
      const href = $element.attr('href')
      return href &&

> {
      const $element = $(element)
      const href = $element.attr('href')

      // Only check preload links for dynamic chunks
      if (href && /_next\/static\/(immutable\/)?chunks\//.test(href)) {
        expect($element.attr('nonce')).toBe('test-n

/_next\/static\/(immutable\/)?chunks\//.test(href)
    })

    // There should be at least one preload link for dynamic chunks
    expect(dynamicPreloadLinks.length).toBeGreaterThan(0)

    dynamicPreloadLinks.each((_, element) =


xSource,
  openRedbox,
} from 'next-test-utils'
import stripAnsi from 'strip-ansi'

describe('use-cache-close-over-function', () => {
  const { next, isNextDev, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    skipStart: process.env.NEXT_TEST_MODE !== 'dev',
  })

  if (skipped) {
    return
  }

  if (isNextDev) {
    it('should show an error toast for client-side usage', async () => {
      const outputIndex = next.cliOutput.length
      const browser = await ne

shot(`
        "Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.
          [function fn]
           ^^^^^^^^^^^"

xt.browser('/client')

      await openRedbox(browser)

      const errorDescription = await getRedboxDescription(browser)
      const errorSource = await getRedboxSource(browser)

      expect(errorDescription).toMatchInlineSnap


 error happens during rendering', async () => {
    const browser = await next.browser('/client')
    await browser
      .waitForElementByCss('#error-trigger-button')
      .elementByCss('#error-trigger-button')
      .click()

    if (isNextDev) {
      await expect(browser).toDisplayRedbox(`
       {
         "description": "Client error",
         "environmentLabel": null,
         "label": "Runtime Error",
         "source": "app/client/page.js (8:11) @ Page
       >  8 |     throw new Error('Client er

nt error'
    )
  })

  it('should render global error for error in server components', async () => {
    const browser = await next.browser('/rsc')
    expect(await browser.elementByCss('h1').text()).toBe('Global Error')

    if (isNextDev) {
      await

ror')
            |           ^",
         "stack": [
           "Page app/client/page.js (8:11)",
         ],
       }
      `)
    }
    expect(await browser.elementByCss('#error').text()).toBe(
      'Global error: Error: Clie


tForRedbox, getRedboxHeader } from 'next-test-utils'
import { nextTestSetup } from 'e2e-utils'

async function testDev(browser, errorRegex) {
  await waitForRedbox(browser)
  expect(await getRedboxHeader(browser)).toMatch(errorRegex)
}

describe('app dir - global error - with style import', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should render global error with correct styles', async () => {

er = await next.browser('/')

    if (isNextDev) {
      await testDev(browser, /Root Layout Error/)
      return
    }

    const h2 = await browser.elementByCss('h2')
    expect(await h2.getComputedCss('color')).toBe('rgb(255, 255, 0)') // yellow
  })
})


waitForRedbox } from 'next-test-utils'

describe('app dir - global-error - error-in-global-error', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  it('should be able to use nextjs navigation hook in global-error', async () => {
    const browser = await next.browser('/')
    const text = await browser.elementByCss('h1').text()
    expect(text).toBe('Custom Global Error')

    if (isNextDev) {
      await waitForRedbox(browser)
      await expect(browser).toDisplayRedbox(`

           |           ^",
         "stack": [
           "Page.useEffect app/page.js (7:11)",
         ],
       }
      `)
    }
  })

  it('should render fallback UI when error occurs in global-error', async () => {
    const browser = await next.browse

       {
         "description": "error in page",
         "environmentLabel": null,
         "label": "Runtime Error",
         "source": "app/page.js (7:11) @ Page.useEffect
       >  7 |     throw new Error('error in page')


urn str.split(substr).length - 1
}

// TODO(NAR-423): Migrate to Cache Components.
describe.skip('ppr-metadata-streaming', () => {
  const { next, isNextDev, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  // No dynamic APIs used in metadata
  describe('static metadata', () => {
    it('should generate metadata in head when page is fully static', async () => {
      const rootSelector = isNextDev ? 'body' : 'head'
      const $ = await next.render$('/fully-static')
      expect($(`${rootSelec

await browser
          .waitForElementByCss(`${rootSelector} title`, { state: 'attached' })
          .text()
      ).toBe('fully static')
      await assertNoConsoleErrors(browser)
    })

    it('should insert metadata in body when page is dynamic page

tor} title`).text()).toBe('fully static')
      expect(countSubstring($.html(), '<title>')).toBe(1)

      const browser = await next.browser('/fully-static', {
        pushErrorAsConsoleLog: true,
      })
      expect(


(cliOutput)
    .split('\n')
    .filter((log) => cacheReasonRegex.test(log) || log.includes('GET'))

  return logs.reduce<ParsedLog[]>((parsedLogs, log) => {
    if (cacheReasonRegex.test(log)) {
      // cache miss/skip reason
      // Example of `log`: "│ │ Cache skipped reason: (cache: no-cache)"
      const reasonSegment = log.split(cacheReasonRegex, 3)[2].trim()
      const reason = reasonSegment.slice(1, -1)
      parsedLogs[parsedLogs.length - 1].cache = reason
    } else {
      // request info

),
        responseTime: parseInt(responseTime, 10),
        cache: undefined,
      })
    }
    return parsedLogs
  }, [])
}

describe('app-dir - fetch logging', () => {
  const { next, isNextDev } = nextTestSetup({
    skipDeployment: true,
    files: _

   const trimmedLog = log.replace(/^[^a-zA-Z]+/, '')
      const [method, url, statusCode, responseTime] = trimmedLog.split(' ', 5)

      parsedLogs.push({
        method,
        url,
        statusCode: parseInt(statusCode, 10


escribe(`mdx ${type}`, () => {
    const { next } = nextTestSetup({
      files: __dirname,
      dependencies: {
        '@next/mdx': 'canary',
        '@mdx-js/loader': '^2.2.1',
        '@mdx-js/react': '^2.2.1',
        'recma-export-filepath': '1.2.0',
        'rehype-katex': '7.0.1',
        'rehype-slug': '6.0.0',
        'remark-gfm': '4.0.1',
        'remark-toc': '9.0.0',
      },
      env: {
        WITH_MDX_RS: type === 'with-mdx-rs' ? 'true' : 'false',
      },
    })

    describe('app direct

t('should work using browser', async () => {
        const browser = await next.browser('/')
        expect(await browser.elementByCss('h1').text()).toBe('Hello World')
        expect(await browser.elementByCss('p').text()).toBe('This is MDX!')
      })

ory', () => {
      it('should work in initial html', async () => {
        const $ = await next.render$('/')
        expect($('h1').text()).toBe('Hello World')
        expect($('p').text()).toBe('This is MDX!')
      })

      i


ck()
          .waitForElementByCss('#pushstate-data')
          .text()
      ).toBe('PushState Data')
      await browser
        .elementByCss('#push-state')
        .click()
        .waitForElementByCss('#state-updated', { state: 'attached' })
        .elementByCss('#get-latest')
        .click()
      await check(
        () => browser.elementByCss('#my-data').text(),
        `{"foo":"bar"}`
      )
    })

    it('should support setting a different pathname reflected on usePathname', async () => {

  ).toBe('PushState Pathname')

      await browser.elementByCss('#push-pathname').click()

      // Check usePathname value is the new pathname
      await check(
        () => browser.elementByCss('#my-data').text(),
        '/my-non-existent-path'

   const browser = await next.browser('/a')
      expect(
        await browser
          .elementByCss('#to-pushstate-new-pathname')
          .click()
          .waitForElementByCss('#pushstate-pathname')
          .text()


spense } from 'react'
import { useSearchParams } from 'next/navigation'

function InnerPage() {
  const searchParams = useSearchParams()
  return (
    <>

ReplaceState String Url

{searchParams.get('query')}

      <button
        onClick={() => {
          const previousQuery = new URL(window.location.href).searchParams.get(
            'query'
          )
          const url = `?query=${
            previousQuery ? previousQuery + '-a

{
          const previousQuery = new URL(window.location.href).searchParams.get(
            'query'
          )
          const url = `?query=${
            previousQuery ? previousQuery + '-added' : 'foo'
          }`

          window.history.replaceSt

dded' : 'foo'
          }`

          window.history.replaceState({}, '', url)
        }}
        id="replace-string-url"
      >
        Replace searchParam using string url
      </button>

      <button
        onClick={() =>


ent'
import { useEffect, useState } from 'react'

export default function Page() {
  const [data, setData] = useState(null)
  const [updated, setUpdated] = useState(false)
  useEffect(() => {
    setData(window.history.state.myData)
  }, [])
  return (
    <>

ReplaceState Data

      {updated ?

 : null}

{JSON.stringify(data)}

      <button
        onClick={() => {
          setData(window.history.state.myData)

t latest data
      </button>
      <button
        onClick={() => {
          window.history.replaceState({ myData: { foo: 'bar' } }, '')
          setUpdated(true)
        }}
        id="replace-state"
      >
        Replace state
      </button>
    </

       }}
        id="get-latest"
      >
        Ge


retry } from 'next-test-utils'

describe('interception-segments-two-levels-above', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should work when interception route is paired with segments two levels above', async () => {
    const browser = await next.browser('/foo/bar')

    await browser.elementByCss('[href="/hoge"]').click()
    await retry(async () => {
      expect(await browser.elementById('intercepted').text()).toMatch(
        /intercepted/
      )
    })
  })

  it('

ion
    await browser.elementByCss('[href="/hoge"]').click()
    await retry(async () => {
      expect(await browser.elementById('intercepted').text()).toMatch(
        /intercepted/
      )
    })

    // Go back
    await browser.back()
    await retry(

should intercept consistently with back/forward navigation', async () => {
    // Test that interception works correctly with browser back/forward
    const browser = await next.browser('/foo/bar')

    // Navigate with intercept


 { nextTestSetup } from 'e2e-utils'

describe('app dir - global error - layout error', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should render global error for error in server components', async () => {
    const browser = await next.browser('/')

    if (isNextDev) {
      await expect(browser).toDisplayRedbox(`
       {
         "description": "layout error",
         "environmentLabel": "Serv

  ],
       }
      `)
    }

    expect(await browser.elementByCss('h1').text()).toBe('Global Error')
    expect(await browser.elementByCss('#error').text()).toBe(
      isNextDev
        ? 'Global error: layout error'
        : 'Global error: Minified Re

er",
         "label": "Runtime Error",
         "source": "app/layout.js (2:9) @ layout
       > 2 |   throw new Error('layout error')
           |         ^",
         "stack": [
           "layout app/layout.js (2:9)",


  waitForRedbox,
  getRedboxDescription,
  getRedboxSource,
} from 'next-test-utils'

describe('cache-components-route-handler-errors', () => {
  const { next, skipped, isNextDev, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it("should error when route handlers use segment configs that aren't supported by cacheComponents", async () => {
    try {
      await next.start()
    } catch {
      // we expect the buil

escription: await getRedboxDescription(browser),
        source: await getRedboxSource(browser),
      }

      if (isTurbopack) {
        expect(redbox.description).toMatchInlineSnapshot(
          `"Route segment config "dynamic" is not compatible with \

d to fail
    }

    if (isNextDev) {
      // Test the first route handler with "dynamic" config
      const browser = await next.browser('/route-with-dynamic')
      await waitForRedbox(browser)
      const redbox = {
        d


ge is suspended and being caught by the layout Suspense boundary
export default function Page() {
  return (

      <SuspendedComponent />

  )
}

async function SuspendedComponent() {
  await connection()
  await new Promise((resolve) => setTimeout(resolve, 500))
  return (

suspended component

      <NestedSuspendedComponent />

  )
}

async function NestedSuspendedComponent() {
  await connection()
  await new Promise((resolve)

spended component</div>
}

export async function generateMetadata() {
  await connection()
  await new Promise((resolve) => setTimeout(resolve, 3 * 1000))
  return {
    title: 'dynamic-metadata - partial',
    description: `dynamic metadata - ${Math.rando

=> setTimeout(resolve, 500))
  return <div>nested su


('searchparams-static-bailout', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
    dependencies: {
      nanoid: '4.0.1',
    },
  })

  describe('server component', () => {
    it('should bailout when using searchParams', async () => {
      const url = '/server-component-page?search=hello'
      const $ = await next.render$(url)
      expect($('h1').text()).toBe('Parameter: hello')

      // Check if the page is not statically generated.
      if (isNextStart) {
        cons

{
      const url = '/server-component-without-searchparams?search=hello'

      const $ = await next.render$(url)
      expect($('h1').text()).toBe('No searchParams used')

      // Check if the page is not statically generated.
      if (isNextStart) {

t id = $('#nanoid').text()
        const $2 = await next.render$(url)
        const id2 = $2('#nanoid').text()
        expect(id).not.toBe(id2)
      }
    })

    it('should not bailout when not using searchParams', async () =>


ort { Suspense } from 'react'
import { useSearchParams } from 'next/navigation'

function InnerPage() {
  const searchParams = useSearchParams()
  return (
    <>

ReplaceState SearchParams

{searchParams.get('query')}

      <button
        onClick={() => {
          const url = new URL(window.location.href)
          const previousQuery = url.searchParams.get('query')
          url.searchParams.set(
            'query',
            p

      )
          window.history.replaceState({}, '', url)
        }}
        id="replace-searchparams"
      >
        Replace searchParam
      </button>
    </>
  )
}

export default function Page() {
  return (
    <Suspense>
      <InnerPage />
    </

reviousQuery ? previousQuery + '-added' : 'foo'


from 'e2e-utils'
import { waitForRedbox, getRedboxHeader } from 'next-test-utils'

const errorMessage =
  'images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config'

async function testDev(browser, errorRegex) {
  await waitForRedbox(browser)
  expect(await getRedboxHeader(browser)).toMatch(errorRegex)
}

describe('Error test if the loader file export a named function', () => {
  describe('in Development', () => {
    const { next,

nc () => {
        const browser = await next.browser('/')
        await testDev(browser, errorMessage)
      })

      it('should show the error when using `getImageProps` method', async () => {
        const browser = await next.browser('/get-img-props')

 isNextDev } = nextTestSetup({
      skipDeployment: true,
      files: __dirname,
    })

    ;(isNextDev ? describe : describe.skip)('development only', () => {
      it('should show the error when using `Image` component', asy


link'

export const fetchCache = 'default-cache'

export default function Layout({ children }) {
  return (

          <Link id="nav-link" href={'/link'}>
            /link
          </Link>

          <Link id="nav-headers" href={'/headers'}>
            /headers
          </Link>

          <Link id="nav-default-cache" href={'/default-cache'}>
            /default-cache
          </Link>

          <Link id="nav-cache-

/cache-revalidate
          </Link>

          <Link id="nav-many-requests" href={'/many-requests'}>
            /many-requests
          </Link>

{children}

  )


kipDeployment: true,
    env: {
      NEXT_TEST_LOG_VALIDATION: '1',
    },
  })
  if (skipped) return

  if (isNextStart && !isTurbopack) {
    it.skip('TODO: snapshot tests for webpack', () => {})
    return
  }

  if (isNextStart) {
    beforeAll(async () => {
      await next.build({ args: ['--experimental-build-mode', 'compile'] })
    })
    afterEach(async () => {
      await next.stop()
    })
  } else {
    beforeAll(async () => {
      await next.start()
    })
  }

  const prerender = async (path

is 'experimental-manual-error'. Implicit validation
  // does NOT fire on bare pages — only segments that explicitly opt in via
  // `unstable_instant` are validated. When they are validated, the level is
  // error (applies in dev AND build), unless de-es

name: string) => {
    return await next.build({
      args: [
        '--experimental-build-mode',
        'generate',
        '--debug-build-paths',
        `app${pathname}/page.tsx`,
      ],
    })
  }

  // Validation level


 { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('app-dir - draft-mode-middleware', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should be able to enable draft mode with middleware present', async () => {
    const browser = await next.browser(
      '/api/draft?secret=secret-token&slug=preview-page'
    )

    await retry(async () => {
      expect(next.cliOutput).toConta

t).toBe('draft')
  })

  it('should be able to disable draft mode with middleware present', async () => {
    const browser = await next.browser('/api/disable-draft')
    await retry(async () => {
      expect(next.cliOutput).toContain(
        'draftMode(

in(
        'draftMode().isEnabled from middleware: true'
      )
    })

    await browser.loadPage(new URL('/preview-page', next.url).toString())
    const draftText = await browser.elementByCss('h1').text()
    expect(draftTex


'use no memo'

import { useReducer, useState } from 'react'
import Image from 'next/image'

export default function Page() {
  const [, setLoadEvent] = useState<unknown>(null)
  const [showClientImage, setShowClientImage] = useState(false)
  const [, rerender] = useReducer((i) => i + 1, 0)

  return (
    <>
      <Image
        alt="foo"
        width={5}
        height={5}
        unoptimized
        src="/test.png?hydrated"
        onError={() => {}}
        onLoad={(event) => {
          console.error(

ender}>
        rerender Page
      </button>
      <button type="button" onClick={() => setShowClientImage(true)}>
        Show Client image
      </button>
      {showClientImage && (
        <Image
          alt="bar"
          width={5}
          heigh

'hydrated image load')
          // This doesn't really make sense. We just want to check rerendering
          // doesn't infinitely loop
          setLoadEvent(event)
        }}
      />
      <button type="button" onClick={rer


'use no memo'

import { useReducer, useState } from 'react'
import Image from 'next/image'

export default function Page() {
  const [, setErrorEvent] = useState<unknown>(null)
  const [showClientImage, setShowClientImage] = useState(false)
  const [, rerender] = useReducer((i) => i + 1, 0)

  return (
    <>
      <Image
        alt="foo"
        width={5}
        height={5}
        unoptimized
        src="/will-never-exist.png"
        onError={(event) => {
          console.error('hydrated image error'

nder Page
      </button>
      <button type="button" onClick={() => setShowClientImage(true)}>
        Show Client image
      </button>
      {showClientImage && (
        <Image
          alt="bar"
          width={5}
          height={5}
          unop

)
          // This doesn't really make sense. We just want to check rerendering
          // doesn't infinitely loop
          setErrorEvent(event)
        }}
      />
      <button type="button" onClick={rerender}>
        rere


from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('no-double-tailwind-execution', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      '@tailwindcss/postcss': '^4',
      tailwindcss: '^4',
    },
    env: {
      DEBUG: 'tailwindcss',
      ...process.env,
    },
  })

  if (skipped) {
    return
  }

  it('should run tailwind only once initially and per change', async () => {
    const browser = await next

s.css/g
        ),
      ].length
    }

    expect(getTailwindProcessingCount()).toBe(1) // initial

    if (isNextDev) {
      await next.patchFile(
        'app/page.tsx',
        (content) => content.replace('hello world', 'hello hmr'),
        async (

.browser('/')
    expect(await browser.elementByCss('p').text()).toBe('hello world')

    function getTailwindProcessingCount() {
      return [
        ...next.cliOutput.matchAll(
          /\[@tailwindcss\/postcss\] app\/global


ts
   * and tracks them for later assertion.
   */
  function createNavigationInterceptor() {
    const navigationRequests: Request[] = []

    const beforePageLoad = (page: Page) => {
      page.on('request', (request) => {
        if (request.resourceType() === 'document') {
          navigationRequests.push(request)
        }
      })
    }

    const getNavigationRequests = () => navigationRequests

    return { beforePageLoad, getNavigationRequests }
  }

  /**
   * Helper to test that a javascript: UR

t,
    initialUrl: string,
    getNavigationRequests: () => Request[]
  ) {
    const errorMessage =
      'has blocked a javascript: URL as a security precaution.'
    // Wait for the security error to appear in logs, confirming the click was processed

L is blocked.
   * Waits for the security error to appear in logs (confirming the click was processed),
   * then asserts no navigation requests were made.
   */
  async function expectJavascriptUrlBlocked(
    browser: Playwrigh


ables gesture-driven navigation
 * via React's Gesture Transitions feature.
 *
 * The test simulates a gesture by using two buttons:
 * - "Start Gesture" calls experimental_gesturePush and begins an async
 *   transition that doesn't complete until "End Gesture" is clicked
 * - "End Gesture" resolves the pending promise and triggers the canonical
 *   router.push
 *
 * This allows us to observe the intermediate gesture state before the
 * canonical navigation completes.
 */
import { nextTestSetup } from 'e2

xt.browser('/')

    // Verify we're on the home page
    expect(
      await browser.elementByCss('[data-testid="home-page"]').text()
    ).toContain('Home')

    // Click "Start Gesture" to begin the optimistic navigation
    await browser.elementByCss('

e-utils'

describe('gesture-transitions', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('shows optimistic state during gesture, then canonical state after', async () => {
    const browser = await ne


 from 'react'
import { useRouter, usePathname } from 'next/navigation'
import Link from 'next/link'

const pendingResolvers = new Set<() => void>()

export default function Root({ children }: { children: ReactNode }) {
  const router = useRouter()
  const pathname = usePathname()
  const isOnHomePage = pathname === '/'

  const startGesture = (href: string) => {
    // NOTE: In a real scenario, this would be startGestureTransition,
    // not startTransition.
    startTransition(async () => {
      // Call

that takes time to complete.
      await new Promise<void>((resolve) => {
        pendingResolvers.add(resolve)
      })

      // After the gesture ends, perform the canonical navigation
      router.push(href)
    })
  }

  const endGesture = () => {

gesture push to show prefetched content immediately
      ;(router as any).experimental_gesturePush(href)

      // Create a promise that won't resolve until the "end gesture" button
      // is clicked. This simulates a gesture


tTestSetup } from 'e2e-utils'

describe('app dir - not-found - conflict route', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  const runTests = () => {
    it('should use the not-found page for non-matching routes', async () => {
      const browser = await next.browser('/random-content')
      expect(await browser.elementByCss('h1').text()).toContain(
        'This Is The Not Found Page'
      )
      // should co

d')
      expect(html).toContain('I am still a valid page')
    })
  }

  describe('with default runtime', () => {
    runTests()
  })

  describe('with runtime = edge', () => {
    let originalLayout = ''

    beforeAll(async () => {
      await next.stop

ntain root layout content
      expect(await browser.elementByCss('#layout-nav').text()).toBe('Navbar')
    })

    it('should allow to have a valid /not-found route', async () => {
      const html = await next.render('/not-foun


rt { nextTestSetup } from 'e2e-utils'

const isPPREnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'

describe('app dir - not-found - default', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  it('should has noindex in the head html', async () => {
    const $ = await next.render$('/does-not-exist')
    expect(await $('meta[name="robots"]').attr('content')).toBe('noindex')
  })

  if (isNextStart) {
    it('should contain noindex contain i

next.readFile('.next/server/app/_not-found.html')
      const rsc = isPPREnabled
        ? 'noindex'
        : await next.readFile(`.next/server/app/_not-found.rsc`)

      expect(html).toContain('noindex')
      expect(rsc).toContain('noindex')
    })
  }

n the page', async () => {
      const html = await


iles: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it("should propagate notFound errors past a segment's error boundary", async () => {
    let browser = await next.browser('/error-boundary')
    await browser.elementByCss('button').click()
    expect(await browser.elementByCss('h1').text()).toBe('Root Not Found')

    browser = await next.browser('/error-boundary/nested/nested-2')
    await browser.elementByCss('button').click()
    expect(await browser.elementByCss('h1').t

/nested)'
    )
  })

  it('should return 404 status code for custom not-found page', async () => {
    const res = await next.fetch('/_not-found')
    expect(res.status).toBe(404)
  })

  if (isNextStart) {
    it('should include not found client referenc

ext()).toBe(
      'Not Found (error-boundary/nested)'
    )

    browser = await next.browser('/error-boundary/nested/trigger-not-found')
    expect(await browser.elementByCss('h1').text()).toBe(
      'Not Found (error-boundary


 { nextTestSetup } from 'e2e-utils'
import { check } from 'next-test-utils'

describe('not-found app dir css', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      sass: 'latest',
    },
  })

  if (skipped) {
    return
  }

  it('should load css while navigation between not-found and page', async () => {
    const browser = await next.browser('/')
    await check(
      async () =>
        await browser.eval(
          `window.getCom

      async () =>
        await browser.eval(
          `window.getComputedStyle(document.querySelector('#go-to-index')).backgroundColor`
        ),
      'rgb(0, 128, 0)'
    )
    await browser.elementByCss('#go-to-index').click()
    await browser.wait

putedStyle(document.querySelector('#go-to-404')).backgroundColor`
        ),
      'rgb(0, 128, 0)'
    )
    await browser.elementByCss('#go-to-404').click()
    await browser.waitForElementByCss('#go-to-index')
    await check(


ort { nextTestSetup } from 'e2e-utils'

describe('app dir - not-found - group route', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  const runTests = () => {
    it('should use the not-found page under group routes', async () => {
      const browser = await next.browser('/blog')
      expect(await browser.elementByCss('h1').text()).toContain('Group Layout')
      expect(await browser.elementByCss('#not-found').tex

it next.stop()
      originalLayout = await next.readFile('app/layout.js')
      await next.patchFile(
        'app/layout.js',
        `export const runtime = 'edge'\n${originalLayout}`
      )
      await next.start()
    })
    afterAll(async () => {

t()).toContain(
        'Not found!'
      )
    })
  }

  describe('with default runtime', () => {
    runTests()
  })

  describe('with runtime = edge', () => {
    let originalLayout = ''

    beforeAll(async () => {
      awa


lient'

import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'
import { useServerInsertedHTML } from 'next/navigation'
import { useState } from 'react'

export default function RootStyleRegistry({ children }) {
  const [{ cache, flush }] = useState(() => {
    const cache = createCache({ key: 'emotion-cache' })
    cache.compat = true
    const prevInsert = cache.insert
    let inserted = []
    cache.insert = (...args) => {
      const serialized = args[1]
      if (cache.i

serted
    }
    return { cache, flush }
  })

  useServerInsertedHTML(() => {
    const names = flush()
    if (names.length === 0) return null
    let styles = ''
    for (const name of names) {
      styles += cache.inserted[name]
    }
    return (

nserted[serialized.name] === undefined) {
        inserted.push(serialized.name)
      }
      return prevInsert(...args)
    }
    const flush = () => {
      const prevInserted = inserted
      inserted = []
      return prevIn


outes with root not-found', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should render default 404 with root layout for non-existent page', async () => {
    const browser = await next.browser('/non-existent')
    expect(await browser.elementByCss('p').text()).toBe('Not found placeholder')
    expect(await browser.elementByCss('h1').text()).toBe('Root layout')
  })

  it('should render root not found for grou

r = await next.browser('/group-dynamic/123')
    expect(await browser.elementByCss('p').text()).toBe('group-dynamic [id]')

    await browser.loadPage(next.url + '/group-dynamic/404')
    expect(await browser.elementByCss('p').text()).toBe('Not found place

p routes if hit 404', async () => {
    const browse


 { nextTestSetup } from 'e2e-utils'

describe('ppr-root-param-fallback', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should have use-cache content in fallback shells for all pregenerated locales', async () => {
    // Setup: The app has a [locale] param with generateStaticParams returning
    // ['en', 'fr'], and a nested /[locale]/blog/[slug] route. The layout uses
    // a 'use cache' function to fetch locale-specific translations.
    //
    // This test ensures that we g

 next.render$(`/${locale}/blog/new-post`)

      // The shell should have the locale-header with cached content,
      // NOT the locale-loading Suspense fallback
      expect($('#locale-header').length).toBe(1)
      expect($('#locale-header').text()).toC

enerate fallback shells with the correct locale
    // filled in for all pregenerated locales.

    for (const locale of ['en', 'fr']) {
      // next.render$ doesn't stream, so we get just the shell content
      const $ = await


'react'
import { cookies } from 'next/headers'

async function getLocaleConfig(localeParam: string) {
  'use cache'
  await new Promise((resolve) => setTimeout(resolve, 800))
  return {
    locale: localeParam,
    translations: {
      home: `Home (${localeParam})`,
      blog: `Blog (${localeParam})`,
      about: `About (${localeParam})`,
    },
  }
}

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ locale: string }>
}) {
  return (

>

        <Suspense
          fallback={
Loading locale info...
}
        >
          <LocaleInfo params={params} />
        </Suspense>

        <Suspense fallback={
Loading user...</di

Welcome to our Blog Platform

Home

Blog

About

          </ul


 from 'next/headers'
import Link from 'next/link'

async function getBlogPost(locale: string, slug: string) {
  'use cache'
  await new Promise((resolve) => setTimeout(resolve, 700))
  return {
    title: `Blog Post: ${slug}`,
    locale,
    content: 'This content was fetched from the CMS...',
    relatedPosts: ['Related Post 1', 'Related Post 2', 'Related Post 3'],
  }
}

export default async function BlogPost({
  params,
}: {
  params: Promise<{ locale: string; slug: string }>
}) {
  return (
    <main>

arams={params} />
      </Suspense>

      <Suspense fallback={
Loading comments...
}>
        <DynamicComments />
      </Suspense>

  )
}

// This component depends on locale and slug, so it's in Suspense
async fu

Blog Article

Estimated reading time: 5 minutes

      <Suspense fallback={
Loading article...
}>
        <BlogContent p


utils'
import { retry } from 'next-test-utils'

describe('prefetch-searchparam', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })
  it('should set prefetch cache properly on different search params', async () => {
    // load WITH search param
    const browser = await next.browser('/?q=foo')
    expect(await browser.elementByCss('p').text()).toBe('{"q":"foo"}')

    // navigate to different search param, should update the search param
    await browser.elementByCss('[href="/?q=bar"]').

wait browser.elementByCss('p').text()).toBe('{"q":"bar"}')
    })

    // navigate to home, should clear the searchParams value
    await browser.elementByCss('[href="/"]').click()
    await retry(async () => {
      expect(await browser.elementByCss('p').


 { nextTestSetup } from 'e2e-utils'

describe('require-context', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  // Recommended for tests that check HTML. Cheerio is a HTML parser that has a jQuery like API.
  it('should get correct require context when using regex filtering', async () => {
    const $ = await next.render$('/require-context-with-regex')
    expect($('pre').text()).toBe(
      JSON.stringify([
        './parent/file1.js',
        './parent/file2.js',
        './paren

e context when using no regex', async () => {
  //   const $ = await next.render$('/require-context-with-no-regex')
  //   expect($('pre').text()).toBe(
  //     JSON.stringify([
  //       './parent/file1',
  //       './parent/file1.js',
  //       './pa

t2/file3.js',
      ])
    )
  })

  // TODO: This test is already scaffolded and just needs to be turned on when turbopack supports it.
  // eslint-disable-next-line jest/no-commented-out-tests
  // it('should get correct requir


      '*.test-file.js': [
        {
          condition: { all: ['browser', 'foreign'] },
          loaders: [
            {
              loader: require.resolve('./test-file-loader.js'),
              options: { browser: true, foreign: true },
            },
          ],
        },
        {
          condition: { all: ['browser', { not: 'foreign' }] },
          loaders: [
            {
              loader: require.resolve('./test-file-loader.js'),
              options: { browser: true },
            }

ion: { not: { any: ['browser', 'foreign'] } },
          loaders: [
            {
              loader: require.resolve('./test-file-loader.js'),
              options: { default: true },
            },
          ],
        },
      ],
    },
  },
}

modul

,
          ],
        },
        {
          condit


 { nextTestSetup } from 'e2e-utils'

// Specifically tests turbopack.rules.*.foreign config
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'webpack-loader-conditions',
  () => {
    const { next, skipped } = nextTestSetup({
      files: __dirname,
      skipDeployment: true,
    })

    if (skipped) return

    it('should render correctly on server site', async () => {
      const res = await next.fetch('/')
      const html = (await res.text()).replaceAll(/<!-- -->/g, '')
      expect(html)

() => {
      const browser = await next.browser('/')
      const text = await browser.elementByCss('body').text()
      expect(text).toContain(`server: ${JSON.stringify({ default: true })}`)
      expect(text).toContain(`client: ${JSON.stringify({ browser

.toContain(`server: {"default":true}`)
      expect(html).toContain(`client: {"default":true}`)
      expect(html).toContain(`foreignClient: {}`)
    })

    it('should render correctly on client side', async


tTestSetup } from 'e2e-utils'

describe('sitemap-group', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should not add suffix to sitemap under group routes', async () => {
    const res = await next.fetch('/foo/sitemap.xml')
    expect(res.status).toBe(200)
    expect(res.headers.get('content-type')).toBe('application/xml')
    const text = await res.text()
    expect(text).toMatchInlineSnapshot(`
      "<?xml version="1.0" encoding="UTF-8"?>
      <urlset xmlns="http://www.sit

should not add suffix to static sitemap.xml under group routes', async () => {
    const res = await next.fetch('/bar/sitemap.xml')
    expect(res.status).toBe(200)
    expect(res.headers.get('content-type')).toBe('application/xml')
    const text = await

emaps.org/schemas/sitemap/0.9">
      <url>
      <loc>https://www.vercel.com</loc>
      <lastmod>2024-12-05T23:45:13.405Z</lastmod>
      <changefreq>monthly</changefreq>
      </url>
      </urlset>
      "
    `)
  })

  it('


tTestSetup } from 'e2e-utils'

const pprEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'

describe('use-cache-private', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  it('excludes private caches from prerenders', async () => {
    const browser = await next.browser('/')

    expect(await browser.elementById('page-sentinel').text()).toBe(
      isNextDev || !pprEnabled ? 'runtime' : 'buildtime'
    )

    expect(await browser.elementById('private-sentinel').text()

wser.addCookie({ name: 'test-cookie', value: 'foo' })
    await browser.refresh()

    expect(await browser.elementById('test-cookie').text()).toBe('foo')
  })

  it('allows reading search params in private caches', async () => {
    const browser = await

).toBe('runtime')
  })

  it('allows reading cookies in private caches', async () => {
    const browser = await next.browser('/cookies')

    expect(await browser.elementById('test-cookie').text()).toBe('<empty>')

    await bro


ort { nextTestSetup } from 'e2e-utils'

describe('app-dir revalidate-dynamic', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
  })

  if (isNextStart) {
    it('should correctly mark a route handler that uses revalidateTag as dynamic', async () => {
      expect(next.cliOutput).toContain('ƒ /api/revalidate-path')
      expect(next.cliOutput).toContain('ƒ /api/revalidate-tag')
    })
  }

  it.each(['/api/revalidate-path', '/api/revalidate-tag'])(
    `should revalidate the dat

mentById('data-value').text()

      expect(randomNumber).toEqual(randomNumber2)

      const revalidateRes = await next.fetch(path)
      expect((await revalidateRes.json()).revalidated).toBe(true)

      await browser.refresh()

      const randomNumber3

a with %s`,
    async (path) => {
      const browser = await next.browser('/')
      const randomNumber = await browser.elementById('data-value').text()
      await browser.refresh()
      const randomNumber2 = await browser.ele


oyment: true,
    env: {
      NEXT_TEST_LOG_VALIDATION: '1',
    },
  })
  if (skipped) return

  if (isNextStart && !isTurbopack) {
    it.skip('TODO: snapshot tests for webpack', () => {})
    return
  }

  if (isNextStart) {
    beforeAll(async () => {
      await next.build({ args: ['--experimental-build-mode', 'compile'] })
    })
    afterEach(async () => {
      await next.stop()
    })
  } else {
    beforeAll(async () => {
      await next.start()
    })
  }

  const prerender = async (pathname: s

erimental-error'. Implicit validation fires on
  // bare pages in dev AND build — error level applies to both modes.
  // Per-segment overrides (`level`, `true`, `false`) layer on top of this.
  // A `level: 'warning'` override de-escalates a specific rout

tring) => {
    return await next.build({
      args: [
        '--experimental-build-mode',
        'generate',
        '--debug-build-paths',
        `app${pathname}/page.tsx`,
      ],
    })
  }

  // Validation level is 'exp


t { Suspense, type ReactNode } from 'react'

// Validation level is 'experimental-error' (set in next.config.ts).
// Bare page/default segments get implicit validation in dev AND build.
// Build fails when violations are found.
//
// Children are wrapped in Suspense so that pages with runtime data
// accessed at the top of the page don't fail static-shell validation
// (the Suspense fallback renders into the static shell). Instant
// validation flags "Suspense too high for instant navigation" as an
// insta

olation when it runs.
export const unstable_instant = false

export default function RootLayout({ children }: { children: ReactNode }) {
  return (

        <Suspense fallback={
loading…
}>{children}</Suspense>


from 'e2e-utils'
import { retry } from 'next-test-utils'

// Explicitly don't mix route handlers with pages in this test app, to make sure
// that this also works in isolation.
describe('use-cache-route-handler-only', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  it('should cache results in node route handlers', async () => {
    const response = await next.fetch('/node')
    const { date1, date2 } = await response.json()

    expect(date1).toBe(date2)
  })

  if (!i

rior test activity
      // can affect request timing enough for the concurrent requests to no
      // longer overlap reliably.
      await next.stop()
      await next.start()

      const [{ rand: first }, { rand: second }] = await Promise.all([

sNextDeploy) {
    // In deploy mode, concurrent requests could hit different lambdas.
    it('should dedupe concurrent cache invocations in route handlers', async () => {
      // Restart to ensure a cold server. Without this, p


llHandler =
  process.env.__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER === 'true'

describe('navigation-focus', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('navigation to an interactive segment', async () => {
    const browser = await next.browser('/')
    await browser.elementByCss('a[href="/interactive-segment"]').click()

    await retry(async () => {
      // Good debug info is a moving target. Use Playwright traces to find out
      // what was focused if this fails

iveElement.getAttribute('data-testid')
          )
        ).toBe('segment-container')
      }
    })
  })

  it('navigation to a scrollable segment', async () => {
    const browser = await next.browser('/')
    await browser.elementByCss('a[href="/scroll

 if (enableNewScrollHandler) {
        expect(await browser.eval(() => document.activeElement.localName)).toBe(
          'body'
        )
      } else {
        expect(
          await browser.eval(() =>
            document.act


ort type { ReactNode } from 'react'
import Link from 'next/link'
import './styles.css'

export default function Root({ children }: { children: ReactNode }) {
  return (

              <Link href="/">Home</Link>

              <Link href="/interactive-segment">Interactive Segment</Link>

              <Link href="/scrollable-segment">Scrollable

     <Link href="/uri-fragments#section-2">
                to URI fragment in different Segment
              </Link>
            </li>
            <li>
              <Link href="/uri-fragments">Segment with URI fragments</Link>
            </li>

Segment</Link>
            </li>
            <li>
              <Link href="/segment-with-focusable-descendant">
                Segment with focusable descendant
              </Link>
            </li>
            <li>


() {
  return (
    <>

            <Link href="#section-1">Section 1</Link>

            <Link href="#section-2">Section 2</Link>

            <Link href="#section-3">Section 3</Link>

A post

some long intro

e={{ height: '100vh' }}>bla

Section 2

bla

Section 3

bla

Section 4


('app-dir assetPrefix handling', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should redirect route when requesting it directly', async () => {
    const res = await next.fetch('/a/', {
      redirect: 'manual',
    })
    expect(res.status).toBe(308)
    expect(new URL(res.headers.get('location'), next.url).pathname).toBe('/a')
  })

  it('should render link', async () => {
    const $ = await next.render$('/')
    expect($('#to-a-trailing-slash').attr('href')).toBe('/a')

 redirect route when clicking link', async () => {
    const browser = await next.browser('/')
    await browser
      .elementByCss('#to-a-trailing-slash')
      .click()
      .waitForElementByCss('#a-page')
    expect(await browser.waitForElementByCss('

})

  it('should redirect route when requesting it directly by browser', async () => {
    const browser = await next.browser('/a')
    expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
  })

  it('should


('react@experimental', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    overrideFiles: {
      'next.config.js': `
        module.exports = {
          experimental: {
            taint: true,
          }
        }
      `,
    },
  })

  it('should opt into the react@experimental when enabling $flag', async () => {
    const resPages$ = await next.render$('/app-react')
    const [
      ssrReact,
      ssrReactDOM,
      ssrClientReact,
      ssrClientReactDOM,
      ssrClientReactDOMSe

 ]
    expect({
      ssrReact,
      ssrReactDOM,
      ssrClientReact,
      ssrClientReactDOM,
      ssrClientReactDOMServer,
    }).toEqual({
      ssrReact: expect.stringMatching('-experimental-'),
      ssrReactDOM: expect.stringMatching('-experiment

rver,
    ] = [
      resPages$('#react').text(),
      resPages$('#react-dom').text(),
      resPages$('#client-react').text(),
      resPages$('#client-react-dom').text(),
      resPages$('#client-react-dom-server').text(),


rn result
}

describe('app dir - rsc basics', () => {
  const { next, isNextDev, isNextStart, isTurbopack } = nextTestSetup({
    files: __dirname,
    resolutions: {
      '@babel/core': '7.22.18',
      '@babel/parser': '7.22.16',
      '@babel/types': '7.22.17',
      '@babel/traverse': '7.22.18',
    },
  })

  if (isNextDev && !isTurbopack) {
    it('should have correct client references keys in manifest', async () => {
      await next.render('/')
      await retry(() => {
        // Check that the cl

erenceManifest.clientModules
        )
        expect(clientModulesNames).toSatisfyAll((name) => {
          const [, key] = name.split('#', 2)
          return key === undefined || key === '' || key === 'default'
        })
      })
    })
  }

  describe

ient-side manifest is correct before any requests
        const clientReferenceManifest = getClientReferenceManifest(
          next,
          '/page'
        )
        const clientModulesNames = Object.keys(
          clientRef


Direct from '../../components/client'
import ClientFromShared from '../../components/shared'
import SharedFromClient from '../../components/shared-client'
import Bar from '../../components/bar'

export default function Page() {
  // All three client components should be rendered correctly, but only
  // shared component is a server component, and another is a client component.
  // These two shared components should be created as two module instances.

  // It's expected to have hydration mismatch here.
  r

{/* <Random /> */}

      <ClientFromDirect />

      <ClientFromShared />

      <ClientFromShared />

      <SharedFromClient />

      <SharedFromClient />

      <Bar />

eturn (
    <div id="main" suppressHydrationWarning>


xports
import { a, b, c, d, e } from '../../components/shared-exports'
// client default, named exports
import DefaultArrow, {
  Named as ClientNamed,
} from '../../components/client-exports'

import { Cjs as CjsShared } from '../../components/cjs-server'
import { Cjs as CjsClient } from '../../components/cjs-client'

// client exports all
import { One, Two, TwoAliased } from '../../components/export-all'

export default function Page() {
  return (

        {a}
        {b}
        {c}

<DefaultArrow />

        <ClientNamed />

        <CjsShared />

        <CjsClient />

        Export All: <One />, <Two />, <TwoAliased />
      </di


ponses haven't reached the client yet.
 */

import { nextTestSetup, type Playwright } from 'e2e-utils'
import { createRouterAct } from 'router-act'

/**
 * Reads the rendered route history from the page and returns an array of
 * {url, params} objects representing every route state the app rendered.
 */
async function getRenderedRouteHistory(
  browser: Playwright
): Promise<Array<{ url: string; params: Record<string, unknown> }>> {
  const el = await browser.elementById('rendered-route-history')
  const at

xtDev) {
    // Route prediction with static siblings requires production build
    // because dev mode uses on-demand compilation (staticChildren is null)
    test('skipped in dev mode', () => {})
    return
  }

  it('basic dynamic route prediction: show

tr = await el.getAttribute('data-history')
  return JSON.parse(attr).map((h: string) => JSON.parse(h))
}

describe('optimistic-routing', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  if (isNe


 Suspense, type ReactNode } from 'react'

// Validation level is 'warning' (set in next.config.ts).
// Bare page/default segments get implicit validation in dev only — build
// is unaffected unless a segment explicitly overrides with `level: 'experimental-error'`.
//
// Children are wrapped in Suspense so that pages with runtime data
// accessed at the top of the page don't fail static-shell validation
// (the Suspense fallback renders into the static shell). Instant
// validation flags "Suspense too high f

lation when it runs.
export const unstable_instant = false

export default function RootLayout({ children }: { children: ReactNode }) {
  return (

        <Suspense fallback={
loading…
}>{children}</Suspense>

or instant navigation" as an
// instant-specific vio


waitForNoErrorToast } from 'next-test-utils'
import { join } from 'node:path'

describe('instant validation - opting out of static shells', () => {
  const { next, skipped, isNextDev } = nextTestSetup({
    files: join(__dirname, 'fixtures', 'valid'),
    skipDeployment: true,
  })
  if (skipped) return

  // NOTE: if something's wrong in build, we'll fail before any tests run.
  // Visiting the pages is mostly just a sanity check.

  it('does not require a static shell if a root layouts is configured as bl

ayout is configured as blocking', async () => {
    const browser = await next.browser('/blocking-layout')
    await browser.elementByCss('main')
    if (isNextDev) await waitForNoErrorToast(browser)
  })
  it('does not require a static shell if a page is

ocking', async () => {
    const browser = await next.browser('/blocking-root-layout')
    await browser.elementByCss('main')
    if (isNextDev) await waitForNoErrorToast(browser)
  })
  it('does not require a static shell if a l


tTestSetup } from 'e2e-utils'

describe('app dir - crossOrigin config', () => {
  const { next, isNextStart, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isNextStart) {
    it('skip in start mode', () => {})
    return
  }
  it('should render correctly with assetPrefix: "/"', async () => {
    const $ = await next.render$('/')
    // Only potential external (assetPrefix) <script /> and <link /> should have crossorigin attribute

  // Inline <script /> (including RSC payload) and <link /> should not have crossorigin attribute
    $('script:not([src]), link:not([href])').each((_, el) => {
      const crossOrigin = $(el).attr('crossorigin')
      expect(crossOrigin).toBeUndefined()

$(
      'script[src*="https://example.vercel.sh"], link[href*="https://example.vercel.sh"]'
    ).each((_, el) => {
      const crossOrigin = $(el).attr('crossorigin')
      expect(crossOrigin).toBe('use-credentials')
    })


next-test-utils'

describe('app-dir refresh', () => {
  const { next, skipped, isNextDev } = nextTestSetup({
    files: __dirname,
    // We do not have access to runtime logs when deployed
    skipDeployment: true,
  })

  if (skipped) return

  it('should refresh client cache when refresh() is called in a server action', async () => {
    const browser = await next.browser('/refresh')

    const initialServerTimestamp = await browser
      .elementById('server-timestamp')
      .text()

    expect(initial

      .elementById('server-timestamp')
        .text()
      expect(newServerTimestamp).not.toBe(initialServerTimestamp)
      expect(Number(newServerTimestamp)).toBeGreaterThan(
        Number(initialServerTimestamp)
      )
    })
  })

  it('should thro

ServerTimestamp).toBeTruthy()

    await new Promise((resolve) => setTimeout(resolve, 100))

    await browser.elementById('refresh-button').click()

    await retry(async () => {
      const newServerTimestamp = await browser


ort { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('conflicting-page-segments', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    // we skip start & deploy because the build will fail and we won't be able to catch it
    // start is re-triggered but caught in the assertions below.
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should throw an error when a route groups causes a conflict w

th.'
      )
    } else {
      await expect(next.start()).rejects.toThrow('next build failed')

      await retry(() => {
        expect(next.cliOutput).toMatch(
          /You cannot have two parallel pages that resolve to the same path\. Please check \/

ith a parallel segment', async () => {
    if (isNextDev) {
      await next.start()
      const html = await next.render('/')

      expect(html).toContain(
        'You cannot have two parallel pages that resolve to the same pa


} from 'next/headers'
import { connection } from 'next/server'
import { Suspense } from 'react'

export default async function Page() {
  return (
    <>
      <p>
        This page accesses cookies synchronously at runtime. This triggers a
        type error. In dev mode, we also log an explicit error that `cookies()`
        should be awaited.
      </p>
      <Suspense>
        <CookiesReadingComponent />
      </Suspense>
    </>
  )
}

async function CookiesReadingComponent() {
  // Await a connection

e.
  await connection()
  // Cast to any as we removed UnsafeUnwrapped types, but still need to test with the sync access
  const token = (cookies() as any).get('token')

  return (
    <div>
      this component reads the `token` cookie synchronously: {to

to test the subsequent sync cookies access at runtim


ort { BuildID } from './build-id'
import Indirection from './indirection'
import { date as date1 } from './client-lazy-now-1'
import { date as date2 } from './client-lazy-now-2'
import { date as date3 } from './client-lazy-now-3'

export default async function Page() {
  return (
    <>
      <p>
        This page has several client modules that have sync IO in the module
        scope that will run in serial when prerendering to HTML. The point of
        this test is to assert that the serial nature does

sync IO when
        being initialized during the final render.

      <BuildID />
      <Indirection>

Date 1: {date1}

      </Indirection>
      <Indirection>

Date 2: {date2}

      </Indirection>
      <Indirection

not lead to
        unexpected sync IO errors. In the past this particular setup would show
        up as a sync IO error because the later modules did not initialize
        during the prospective render and thus appeared to use


rt { unstable_cacheTag, cacheTag } from 'next/cache'

export default async function Page() {
  const stable = await stableTag()
  const unstable1 = await unstableTag1()
  const unstable2 = await unstableTag2()
  return (
    <>

        This page calls a "use cache" function that uses the unstable_cacheTag
        API twice. We expect to see a warning once per server lifetime when
        using unstable_cacheTag.

{stable}

{unstable1}

{unst

ag1() {
  'use cache'
  unstable_cacheTag('tag')
  return Math.random()
}

async function unstableTag2() {
  'use cache'
  unstable_cacheTag('tag')
  return Math.random()
}

async function stableTag() {
  'use cache'
  cacheTag('tag')
  return Math.random(

able2}</div>
    </>
  )
}

async function unstableT