from 'e2e-utils'
import { check, retry } from 'next-test-utils'
import { join } from 'path'

describe('custom-app-server-action-redirect', () => {
  const { next, skipped } = nextTestSetup({
    files: join(__dirname, 'custom-server'),
    skipDeployment: true,
    startCommand: 'node server.js',
    serverReadyPattern: /Next mode: (production|development)/,
    dependencies: {
      'get-port': '5.1.1',
    },
  })

  if (skipped) {
    return
  }

  it('redirects with basepath properly when server action

 browser.elementByCss('#increase-count').click().click()
    await retry(async () => {
      expect(await getCount()).toBe('Count: 2')
    })

    await browser.elementById('submit-server-action-redirect').click()

    expect(await browser.waitForElementBy

handler uses `redirect`', async () => {
    const browser = await next.browser('/base')
    const getCount = async () => browser.elementByCss('#current-count').text()

    // Increase count to track if the page reloaded
    await


http = require('http')
const { parse } = require('url')
const next = require('next')
const getPort = require('get-port')

async function main() {
  const dev = process.env.NEXT_TEST_MODE === 'dev'
  process.env.NODE_ENV = dev ? 'development' : 'production'

  const port = await getPort()

  const app = next({ dev, port })
  const handle = app.getRequestHandler()

  await app.prepare()

  const server = http.createServer(async (req, res) => {
    try {
      const parsedUrl = parse(req.url, true)
      const

    } else {
        res.end()
      }
    } catch (err) {
      res.statusCode = 500
      res.end('Internal Server Error')
    }
  })

  server.once('error', (err) => {
    console.error(err)
    process.exit(1)
  })

  server.listen(port, () => {
    co

 { pathname } = parsedUrl
      res.setHeader(
        'set-cookie',
        'custom-server-test-cookie=custom-server-test-cookie-val'
      )
      if (pathname.startsWith('/base')) {
        await handle(req, res, parsedUrl)


fetchViaHTTP } from 'next-test-utils'
import { NEXT_RSC_UNION_QUERY } from 'next/dist/client/components/app-router-headers'
import fs from 'fs'
import path from 'path'

const configPath = path.join(__dirname, 'next.config.js')

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

  it('should get 307 status code for document request', async () => {
    const response = await fetchViaHTTP(next.url, '/origin', undefined, {
      redirect: 'manual',
    })
    expect(

redirect: 'manual',
        headers: {
          rsc: '1',
        },
      }
    )
    expect(response.status).toBe(200)
  })
})

// TODO: these tests aren't currently run at all during testing
if (process.env.NODE_ENV === 'production') {
  describe.each(

response.status).toBe(307)
  })

  it('should get 200 status code for rsc request', async () => {
    const response = await fetchViaHTTP(
      next.url,
      `/origin?${NEXT_RSC_UNION_QUERY}`,
      undefined,
      {


port { nextTestSetup } from 'e2e-utils'

describe('parallel-routes-catchall-children-slot', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should match the @children slot for a page before attempting to match the catchall', async () => {
    let browser = await next.browser('/')
    await expect(browser.elementById('children').text()).resolves.toBe(
      'Hello from @children/page'
    )
    await expect(browser.elementById('slot').text()).resolves.toBe(
      '@slot content'

wser = await next.browser('/nested')

    await expect(browser.elementById('children').text()).resolves.toBe(
      'Hello from nested @children page'
    )
    await expect(browser.elementById('slot').text()).resolves.toBe(
      'Default @slot'
    )
  }


import fs from 'fs'
import path from 'path'
import { ImageResponse } from 'next/og'

export const contentType = 'image/png'

export default async function og() {
  const font = await fs.promises.readFile(
    path.join(process.cwd(), 'assets/typewr__.ttf')
  )
  return new ImageResponse(
    (
      <div
        style={{
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          fontSize: 88,
          backgroun

       color: '#000',
        }}
      >
        Typewriter og
      </div>
    ),
    {
      fonts: [
        {
          name: 'Typewriter',
          data: font,
          style: 'normal',
        },
      ],
    }
  )
}

export const runtime = 'nodejs


ver response. Skipped in dev because
 *    webpack compiles routes on-demand and only knows about visited routes.
 *    (Turbopack doesn't have this limitation since it builds the full
 *    directory tree from the file system.)
 *    TODO: Replace with end-to-end tests once client behavior is implemented.
 *
 * 2. Navigation tests (dev and prod): Navigate to the dynamic route first,
 *    then go back and click the static sibling link (with prefetch={false}).
 *    Verify the static page renders correctly.

e
 * to a static sibling URL.
 */

import { nextTestSetup } from 'e2e-utils'
// TODO: These imports are only needed for the temporary RSC payload tests.
// Remove once client behavior is implemented.
import {
  NEXT_RSC_UNION_QUERY,
  RSC_HEADER,
} from 'n

 *
 * The navigation flow ensures the client has seen the dynamic route before
 * attempting to navigate to the sibling. This simulates real-world usage
 * where a user might visit or prefetch a dynamic route, then later navigat


ult function Page() {
  return (

Static Siblings Test

About

          This test verifies that when a dynamic route has static siblings at
          the same URL level, the client can correctly navigate to the static
          sibling even after the dynamic route has been visited/prefetched.

Manual testing instructions

Click the checkbox next to a dynamic route to re

Verify the static sibling page renders, not the dynamic route

          The key behavior being tested: when navigating to a URL that matches
          both a dynamic route (e.g., /products/[id]) and a stat

veal it

Click the revealed link to navigate to the dynamic route

Use the browser back button to return to this page

Click the static sibling link (which has prefetch=false)


import { join } from 'path'
import { FileRef, nextTestSetup } from 'e2e-utils'

describe('multiple-lockfiles - has-output-file-tracing-root', () => {
  const { next, skipped } = nextTestSetup({
    files: {
      app: new FileRef(join(__dirname, 'app')),
      // This will silence the multiple lockfiles warning.
      'next.config.js': `module.exports = { outputFileTracingRoot: __dirname }`,
      // Write a package-lock.json file to the parent directory to simulate
      // multiple lockfiles.
      '../pa

,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should not have multiple lockfiles warnings', async () => {
    expect(next.cliOutput).not.toMatch(
      /We detected multiple lockfiles and selected the directory of .+ as the root

ckage-lock.json': JSON.stringify({
        name: 'parent-workspace',
        version: '1.0.0',
        lockfileVersion: 3,
      }),
    },
    // So that ../package-lock.json doesn't leave the isolated testDir
    subDir: 'test'


import { join } from 'path'
import { FileRef, nextTestSetup } from 'e2e-utils'

describe('multiple-lockfiles - has-turbo-root', () => {
  const { next, skipped } = nextTestSetup({
    files: {
      app: new FileRef(join(__dirname, 'app')),
      // This will silence the multiple lockfiles warning.
      'next.config.js': `module.exports = { turbopack: { root: __dirname } }`,
      // Write a package-lock.json file to the parent directory to simulate
      // multiple lockfiles.
      '../package-lock.json'

ment: true,
  })

  if (skipped) {
    return
  }

  it('should not have multiple lockfiles warnings', async () => {
    expect(next.cliOutput).not.toMatch(
      /We detected multiple lockfiles and selected the directory of .+ as the root directory\./

: JSON.stringify({
        name: 'parent-workspace',
        version: '1.0.0',
        lockfileVersion: 3,
      }),
    },
    // So that ../package-lock.json doesn't leave the isolated testDir
    subDir: 'test',
    skipDeploy


n } from 'path'
import { FileRef, nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('multiple-lockfiles', () => {
  const { next, skipped, isTurbopack } = nextTestSetup({
    files: {
      app: new FileRef(join(__dirname, 'app')),
      // Write a package-lock.json file to the parent directory to simulate
      // multiple lockfiles.
      '../package-lock.json': JSON.stringify({
        name: 'parent-workspace',
        version: '1.0.0',
        lockfileVersion: 3,
      }

   await retry(async () => {
      expect(next.cliOutput).toMatch(
        /We detected multiple lockfiles and selected the directory of .+ as the root directory\./
      )

      if (isTurbopack) {
        expect(next.cliOutput).toMatch(
          /To sil

),
    },
    // So that ../package-lock.json doesn't leave the isolated testDir
    subDir: 'test',
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should have multiple lockfiles warnings', async () => {


ely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".'

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

  if (skipped) {
    return
  }

  if (isNextDev) {
    describe('when a "use cache" fill hangs in the static stage', () => {
      it('should show an error after a timeout

description": "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".",
           "environmentLabel": "Server",
           "label": "Conso

', async () => {
        const outputIndex = next.cliOutput.length
        const browser = await next.browser('/static')

        await expect(browser).toDisplayCollapsedRedbox(`
         {
           "code": "E236",
           "


a realistic migration hazard. The documented "preload"
// pattern (see the "Preloading data" section of the
// caching-without-cache-components guide in the Next.js docs) wraps the loader
// in `React.cache`, so its store is tied to the ALS snapshot — `'use cache'`
// bodies run in a clean snapshot and miss, re-executing the loader in cache
// scope. That path is safe during migration.
//
// What's modeled here is the common-in-the-wild variant that uses a
// module-scoped `Map` instead of `React.cache`. Th

ed fetch in the outer scope returns a
//     hanging promise that intentionally never resolves.
//   - In dev, the outer fetch is parked on the Dynamic stage, which can't
//     advance until the cache fills. The cache awaits the outer fetch via the
//

e map lives above ALS, so a
// `'use cache'` body joining it reuses the outer-scope promise (request scope
// or prerender scope) — a promise that was never meant to resolve for the
// cache:
//   - During prerendering, an uncach


ort { cookies } from 'next/headers'
import { Suspense } from 'react'
import { getData, preload } from '../shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=runtime'

async function getCachedData(): Promise<string> {
  'use cache'

  // Joins the outer fetch via the module-scoped dedupe map (see note in
  // ../shared.ts).
  return getData(sharedUrl).then((res) => res.text())
}

async function Cached() {
  // The timeout error must also be shown in the Next.js DevTools w

n Runtime() {
  await cookies()

  // Simulate another part of the tree (e.g. a sibling component or a shared
  // data loader) kicking off the same fetch in outer scope before `Cached`
  // runs.
  preload(sharedUrl)

  return <Cached />
}

export default

hen the
  // invocation is wrapped in a try/catch.
  try {
    const data = await getCachedData()

    return
{data}

  } catch (error) {
    return
Error: {error.message}

  }
}

async functio


./shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=static'

async function getCachedData(): Promise<string> {
  'use cache'

  // Joins the outer fetch via the module-scoped dedupe map (see note in
  // ../shared.ts).
  return getData(sharedUrl).then((res) => res.text())
}

async function Cached() {
  // The timeout error must also be shown in the Next.js DevTools when the
  // invocation is wrapped in a try/catch.
  try {
    const data = await getCachedData()

    re

return
Error: {error.message}

  }
}

export default function Page() {
  // Simulate another part of the tree (e.g. a sibling component or a shared
  // data loader) kicking off the same fetch in outer scope before `Cached`
  // runs.


for https://github.com/vercel/next.js/issues/93142
//
// Any non-ASCII character (Hebrew, Arabic, CJK, emoji, …) in a cache tag —
// whether it's a path-derived implicit tag or a user-supplied tag from
// `cacheTag()`, `unstable_cache({tags})`, or `fetch({next:{tags}})` — gets
// written into the internal `x-next-cache-tags` HTTP header on ISR responses.
// Node's `validateHeaderValue` rejects any byte outside `\t\x20-\x7e`, so the
// response crashes with `ERR_INVALID_CHAR`.
//
// On Vercel deploy stale-if

SCII path / tag must actually be invalidated by
// `revalidatePath`, `revalidateTag`, or `updateTag` against each cache backend.
describe('non-ASCII cache tags', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  const S

-error masks the 500 from clients, but revalidation
// itself keeps failing and the cache stops refreshing for affected routes. The
// revalidate / updateTag cases here cover that round-trip: a cached entry keyed
// under a non-A


TADATA_BASE_WARN_STRING =
  'metadataBase property in metadata export is not set for resolving social open graph or twitter images,'

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

  if (skipped) {
    return
  }

  // If it's start mode, we get the whole logs since they're from build process.
  // If it's development mode, we get the logs after request
  function getCliOutput(logStartPo

gStartPosition = next.cliOutput.length
      await next.fetch('/absolute-url-og')
      const output = getCliOutput(logStartPosition)
      expect(output).not.toInclude(METADATA_BASE_WARN_STRING)
    })
  }

  it('should show warning in vercel deployment o

sition: number) {
    return isNextDev ? next.cliOutput.slice(logStartPosition) : next.cliOutput
  }

  if (isNextDev) {
    it('should not warn metadataBase is missing if there is only absolute url', async () => {
      const lo


import { Suspense } from 'react'

async function SearchAndRouteParams({
  searchParams,
  params,
}: {
  searchParams: Promise<{ id?: string }>
  params: Promise<{ id: string }>
}) {
  const { id: searchId } = await searchParams
  const { id: routeId } = await params

  return (

Search and Route Parameters

Route param id: {routeId}

Search param id: {searchId || 'not provided'}

  )
}

export default function Page

ams,
  params,
}: {
  searchParams: Promise<{ id?: string }>
  params: Promise<{ id: string }>
}) {
  return (
    <Suspense fallback={
Loading...
}>
      <SearchAndRouteParams searchParams={searchParams} params={params} />
    </Suspense>
  )
}


onst { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  if (isNextDev) {
    it('is skipped', () => {})
    return
  }

  it('navigates to prerendered route without waiting for dynamic render', async () => {
    const browser = await next.browser('/')

    // Track elapsed time across the client navigation.
    await browser.eval(`window.__navStart = performance.now()`)

    const link = await browser.elementByCss('a[href="/alpha"]')
    await link.click()

    const slug = await browser.el

.toBe('Hi alpha')

    const elapsed = await browser.eval(`performance.now() - window.__navStart`)

    // The page has an intentional 2s delay in the Server Component.
    // If this were a dynamic render, navigation would take ~2s+.
    expect(elapsed).t

ementById('slug')
    expect(await slug.innerText())


 { nextTestSetup } from 'e2e-utils'

describe('parallel-routes-and-interception', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    // TODO: remove after deployment handling is updated
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  // TODO: revisit the error for missing parallel routes slot
  it('should not render the @children slot when the @slot is not found', async () => {
    const browser = await next.browser('/')
    // we make sure the page is available

sSelector('#children-slot')).toBe(false)
    const $ = await next.render$('/')
    expect($('title').length).toBe(1)
    expect($('title').text()).toBe('layout title')
  })

  it('should render the title once for the non-existed route', async () => {
    c

 through navigating
    expect(await browser.elementByCss('body').text()).toMatch(
      /This page could not be found/
    )

    // we also check that the #children-slot id is not present
    expect(await browser.hasElementByCs


eSelectedLayoutSegment } from 'next/navigation'
import Link from 'next/link'

export default function RootLayout({
  children,
  auth,
  nav,
}: Readonly<{
  children: React.ReactNode
  auth: React.ReactNode
  nav: React.ReactNode
}>) {
  const authSegment = useSelectedLayoutSegment('auth')
  const navSegment = useSelectedLayoutSegment('nav')
  const routeSegment = useSelectedLayoutSegment()

  return (
    <html lang="en">
      <body>
        <section>
          <nav>
            <Link href="/">Main</Link

/Link>
            <Link href="/reset/withEmail">
              Reset with Email (/app/@auth/reset/withEmail)
            </Link>
            <Link href="/reset/withMobile">
              Reset with Mobile (/app/@auth/reset/withMobile)
            </Link>

>
            <Link href="/foo">Foo (regular page)</Link>
            <Link href="/login">
              Login (/app/@auth/login) and (/app/@nav/login)
            </Link>
            <Link href="/reset">Reset (/app/@auth/reset)<


t { Suspense, type ReactNode } from 'react'

// Validation level is 'manual-warning' (set in next.config.ts).
// No implicit validation should fire on bare pages, in dev or build —
// only segments that explicitly opt in via `unstable_instant` are validated.
//
// Children are wrapped in Suspense so that pages with uncached data
// accessed at the top of the page don't fail static-shell validation
// (the Suspense fallback renders into the static shell). Instant
// validation can still flag "Suspense too hi

navigation"
// as an instant-specific violation when it runs.

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

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


om 'react'

export default function Home() {
  const [workerDeploymentId, setWorkerDeploymentId] = useState('default')
  const mainDeploymentId = process.env.NEXT_DEPLOYMENT_ID

  return (
    <div>
      <button
        onClick={() => {
          const worker = new Worker(
            new URL('../deployment-id-worker', import.meta.url),
            {
              type: 'module',
            }
          )
          worker.addEventListener('message', (event) => {
            setWorkerDeploymentId(event.data

ment ID from worker
      </button>

        Main deployment ID:{' '}
        {mainDeploymentId}

        Worker deployment ID:{' '}
        {workerDeployment

)
          })
        }}
      >
        Get deploy


lient'
import { useState } from 'react'

export default function Home() {
  const [pngInfo, setPngInfo] = useState(null)

  return (

      <button
        onClick={() => {
          const worker = new Worker(new URL('../png-worker', import.meta.url), {
            type: 'module',
          })
          worker.addEventListener('message', (event) => {
            setPngInfo(event.data)
          })
        }}
      >
        Get PNG info from worker
      </button>

        PNG URL: <span i

default'}

        Fetched From:{' '}
        {pngInfo?.fetchedFrom ?? 'default'}

        Content-Type:{' '}
        {pngInfo?.contentType ?? 'default'}<

d="png-url">{pngInfo?.url ?? 'default'}

        PNG Width: {pngInfo?.width ?? 'default'}

        PNG Height: {pngInfo?.height ?? '


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

  it('should prefetch the page without errors', async () => {
    let hasNetworkError = false
    const browser = await next.browser('/', {
      beforePageLoad: (page) => {
        page.on('response', (response) => {
          // 304 Not Modified is a valid revalidation response (from no-cache),
          // not a network error
          if (!response.ok() && response.status() !== 304) {
            hasNetworkError = true
          }

e window to ensure we don't perform an MPA navigation when navigating to the prefetched link
    await browser.eval('window.beforeNav = 1')
    await browser.elementById('link-to-random').click()

    await browser.waitForElementByCss('#random-page')

        })
      },
    })

    // set a flag on th


  computeCacheBustingSearchParam,
  computeLegacyCacheBustingSearchParam,
} from 'next/dist/shared/lib/router/utils/cache-busting-search-param'

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

  if (skipped) {
    return
  }

  it('should error when passing invalid router state tree', async () => {
    const stateTree1 = JSON.stringify(['', ''])
    const stateTree2 = JSON.stringify(['', {}])

    const headers1 = {

L('/', 'http://localhost')

    // Add cache busting search param for both requests
    const cacheBustingParam1 = await computeCacheBustingSearchParam(
      undefined,
      undefined,
      stateTree1,
      undefined
    )
    const cacheBustingParam2

     rsc: '1',
      'next-router-state-tree': stateTree1,
    }

    const headers2 = {
      rsc: '1',
      'next-router-state-tree': stateTree2,
    }

    const url1 = new URL('/', 'http://localhost')
    const url2 = new UR


enerate-static-params', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should render the intercepted/non-intercepted modal', async () => {
    const browser = await next.browser('/en')
    expect(await browser.elementByCss('h1').text()).toBe('Home Page')
    await browser.elementByCss("[href='/en/interception/123']").click()
    await browser.waitForElementByCss('#intercepted-slot')

    expect(await browser.elementByCss('h1').text()).toBe('Home Page')
    expect(await browser.

terception Page'
    )

    await browser.back()

    await browser.waitForElementByCss('#home-page')

    await browser.elementByCss("[href='/en/no-interception/123']").click()

    await browser.waitForElementByCss('#non-intercepted-page')

    expect(aw

elementByCss('h2').text()).toBe(
      'Modal for In


} from 'node:timers/promises'

async function stable(action: () => Promise<void>, stableForMS: number = 1000) {
  // Wait for it to reach the initial state.
  await retry(async () => {
    await action()
  })

  // Wait for the stableForMS to ensure that it doesn't change.
  for (let i = 0; i < 10; i++) {
    await action()
    await setTimeout(stableForMS / 10)
  }
}

describe('parallel-route-navigations', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  // We're skipping

eters on the server', async () => {
    const $ = await next.render$('/vercel/sub/folder')

    expect({
      client: $(
        '[data-client-file="/[teamID]/@slot/[...catchAll]/page.tsx"][data-client-params]'
      ).data('client-params'),
      server:

 this in development because there's no prefetching
  // during development.
  if (isNextDev) {
    it.skip('skipping in dev', async () => {
      await Promise.resolve()
    })
    return
  }

  it('should render the right param


xt, isNextDev, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    skipStart: true,
  })
  if (skipped) return

  if (!isNextDev) {
    it('should skip in non-dev mode', () => {})
    return
  }

  beforeAll(async () => {
    await next.start()
  })

  describe('CLI output', () => {
    // Test string-error before error to ensure each error appears independently
    // in the CLI output (webpack only shows errors[0] per compilation)
    it('should show the loader pat

toContain('string-error.data')
          expect(output).toContain(
            'A string error thrown by string-error-loader'
          )
          expect(output).toMatch(/\(from .+loaders\/string-error-loader/)
        },
        // webpack compilation ou

h and error message when a loader throws a plain string', async () => {
      await next.fetch('/string-error')
      await retry(
        async () => {
          const output = stripAnsi(next.cliOutput)
          expect(output).


t').NextConfig}
 */
const nextConfig = {
  turbopack: {
    rules: {
      'error.data': {
        loaders: [require.resolve('./loaders/error-loader.js')],
        as: '*.js',
      },
      'string-error.data': {
        loaders: [require.resolve('./loaders/string-error-loader.js')],
        as: '*.js',
      },
      'promise-error.data': {
        loaders: [require.resolve('./loaders/promise-error-loader.js')],
        as: '*.js',
      },
      'timeout-error.data': {
        loaders: [require.resolve('

     loaders: [require.resolve('./loaders/fs-error-loader.js')],
        as: '*.js',
      },
      'crash.data': {
        loaders: [require.resolve('./loaders/crash-loader.js')],
        as: '*.js',
      },
    },
  },
  webpack(config) {
    config.mod

./loaders/timeout-error-loader.js')],
        as: '*.js',
      },
      'no-stack-error.data': {
        loaders: [require.resolve('./loaders/no-stack-error-loader.js')],
        as: '*.js',
      },
      'fs-error.data': {


is a rather contrived test. The trick is to construct a scenario where
 * Math.random is called during the create-component-tree phase but not during the render.
 * To do this we patch Symbol.for. When the `react.client.reference` symbol is checked
 * we know that this is happening in the create-component-tree function. Given the simple
 * setup of the test page this symbol is not checked during the actual prerender itself.
 *
 * This test may flake in the future. It should be replaced ideally with a test t

 will not express the regression for other reasons.
 */
function patchSymbolFor() {
  const isPatched = globalThis[Symbol.for('__SYMBOL_FOR_PATCHED__')]
  if (!isPatched) {
    console.log('patching')
    globalThis[Symbol.for('__SYMBOL_FOR_PATCHED__')] =

hat uses
 * our OTel implementation to ensure that the built-in trace in create-component-tree does not
 * early abort the prerender. The problem is that we are about to make these functions avoid the
 * workUnitStore scope so it


ce,
  retry,
} from 'next-test-utils'

describe('app-dir - error-on-next-codemod-comment', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    skipDeployment: true,
  })
  if (skipped) return

  if (isNextDev) {
    beforeAll(async () => {
      await next.start()
    })

    it('should error with swc if you have codemod comments left', async () => {
      const browser = await next.browser('/')

      await waitForRedbox(browser)

      if (process.

               After review, either remove the comment if you made the necessary changes or replace "@next-codemod-error" with "@next-codemod-ignore" to bypass the build error if no action at this line can be taken.
             1 | export default functio

env.IS_TURBOPACK_TEST) {
        expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
           "./app/page.tsx (2:2)
           You have an unresolved @next/codemod comment "remove jsx of next line" that needs review.


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

  it('should handle conflicting search and route params on page', async () => {
    const browser = await next.browser('/render/123?id=456')

    const routeParamText = await browser.elementByCss('#route-param').text()
    expect(routeParamText).toContain('Route param id: 123')

    const searchParamText = await browser.elementByCss('#search-param').text()
    expect(searchParamText).toContain('Search param id: 456')
  })

  it('should hand

, async () => {
    // Test with route param "789" and search param "abc"
    const response = await next.fetch('/api/789?id=abc')
    const data = await response.json()

    expect(data).toEqual({
      routeParam: '789',
      searchParam: 'abc',
    })

le conflicting search and route params on API route'


-build-mode', 'compile'] })
    })
    afterEach(async () => {
      await next.stop()
    })
  } else {
    beforeAll(async () => {
      await next.start()
    })
  }

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

  // The fixture has two parallel route trees:
  //
  // - `with-root-suspense/`: layout wraps {children

ant`, so
  //   violating bare pages produce no errors at all.
  //
  // - `without-root-suspense/`: layout renders {children} directly. A
  //   bare violating page has no Suspense between the layout and the
  //   runtime data, so static-shell validation

} in Suspense, so
  //   static-shell validation is satisfied for pages that access runtime
  //   data at the top. Under 'manual-warning', instant validation only
  //   runs on segments that explicitly opt in via `unstable_inst


up({
    files: __dirname,
    // The latest changes to support this behavior on deployed infra are available in the adapter,
    // and are not being backported to the CLI
    skipDeployment: !isAdapterTest,
  })

  if (isNextDev) {
    it.skip('skipping dev test', () => {})
    return
  }

  if (process.env.__NEXT_CACHE_COMPONENTS === 'true') {
    // Here we're validating that the correct fallback shell was used for
    // rendering.
    it('should use the correct fallback route', async () => {
      //

/second/third')

      // Get the sentinel value that was generated at build time or runtime.
      expect($('[data-layout="/"]').data('sentinel')).toBe('buildtime')
      expect($('[data-layout="/[first]"]').data('sentinel')).toBe('buildtime')
      expec

First try to load a page that'll use the base fallback route with the
      // `/[first]/[second]/[third]` fallback.
      let $ = await next.render$('/first/second/third')

      expect($('[data-slug]').data('slug')).toBe('first


retry } from 'next-test-utils'

describe('parallel-routes-and-interception-from-root', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  it('should interpolate [locale] in "/[locale]/example/(...)[locale]/intercepted"', async () => {
    const browser = await next.browser('/en/example')

    expect(await browser.elementByCss('h1').text()).toBe('Example Page')
    expect(await browser.elementByCss('p').text()).toBe('Locale: en')

    if (!isNextDeploy) {
      expect(next

xt.cliOutput.length

    await browser.elementByCss('a').click()

    await retry(async () => {
      expect(await browser.elementByCss('h2').text()).toBe(
        'Page intercepted from root'
      )
    })

    // Ensure that the locale is still correctl

.cliOutput).toInclude('RootLayout rendered, locale: en')
    }

    // Referenced by commented out assertion below, see TODO message
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const cliOutputLength = ne


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

const isoDateRegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
const randomRegExp = /^\d+\.\d+$/

describe('use-cache-with-server-function-props', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should be able to use inline server actions as props', async () => {
    const browser = await next.browser('/server-action')

    await browser.elementById('submit-button-hi').click()
    await retry(async ()

xt()).toMatch('Hello, World!')
    })
  })

  it('should be able to use nested cache functions as props', async () => {
    const browser = await next.browser('/nested-cache')

    await browser.elementById('submit-button-date').click()
    await retry(asy

=> {
      expect(await browser.elementById('hi').text()).toMatch('Hi, World!')
    })

    await browser.elementById('submit-button-hello').click()
    await retry(async () => {
      expect(await browser.elementById('hello').te


ort { isNextDev, nextTestSetup } from 'e2e-utils'
import { waitForNoRedbox } from 'next-test-utils'

describe('hello-world', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: !isNextDev,
    skipDeployment: true,
  })

  if (isNextDev) {
    it('should not indicate there is an error when incidental math.random calls occur during component tree generation during dev', async () => {
      const browser = await next.browser('/')
      await waitForNoRedbox(browser)

      // The r

se {
    it('should not indicate there is an error when incidental math.random calls occur during component tree generation during build', async () => {
      try {
        await next.build()
      } catch (e) {
        console.error('Expected build to Suc

edbox assertion is currently unreliable in this test and so this is an additional check to ensure the CLI didn't print anything with `Math.random()` in it.
      expect(next.cliOutput).not.toContain('Math.random()')
    })
  } el


  })

  it('hard nav to router page and soft nav around other router pages', async () => {
    const browser = await next.browser('/')
    await check(
      () => browser.elementById('navSegment').text(),
      'navSegment (parallel route):'
    )
    await check(
      () => browser.elementById('authSegment').text(),
      'authSegment (parallel route):'
    )
    await check(
      () => browser.elementById('routeSegment').text(),
      'routeSegment (app route):'
    )

    await browser.elementByCss('[

parallel route):'
    )
    await check(
      () => browser.elementById('routeSegment').text(),
      'routeSegment (app route): foo'
    )
  })

  it('hard nav to router page and soft nav to parallel routes', async () => {
    const browser = await next.

href="/foo"]').click()
    await check(
      () => browser.elementById('navSegment').text(),
      'navSegment (parallel route):'
    )
    await check(
      () => browser.elementById('authSegment').text(),
      'authSegment (


opack } = nextTestSetup({
    files: __dirname,
    env: {
      NEXT_DEPLOYMENT_ID: isNextStart ? 'test-deployment-id' : undefined,
    },
    disableAutoSkewProtection: true,
  })

  function beforePageLoad(page: Page) {
    // TODO fix deployment id for webpack
    if (isTurbopack && (isNextDeploy || isNextStart)) {
      page.on('request', (request) => {
        const url = request.url()
        if (url.includes('/_next/')) {
          let parsed = new URL(url, next.url)
          expect(parsed.searchPa

ct(await browser.elementByCss('#worker-state').text()).toBe('default')

    await browser.elementByCss('button').click()

    await retry(async () =>
      expect(await browser.elementByCss('#worker-state').text()).toBe(
        'worker.ts:worker-dep'

rams.get('dpl')).toBe(next.assetToken)
        }
      })
    }
  }

  it('should support web workers with dynamic imports', async () => {
    const browser = await next.browser('/classic', {
      beforePageLoad,
    })
    expe


ed'

// TODO(NAR-423): Migrate to Cache Components.
describe.skip('ppr-middleware-rewrite-force-dynamic-generate-static-params', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  const expectedParams = JSON.stringify(expected)

  it('should have correct dynamic params', async () => {
    // should be rewritten with /en
    const browser = await next.browser('/')
    expect(await browser.elementByCss('a').text()).toBe('Go to /1/2')

    // navigate to /1/2
    await browser.elementByCs

1/2 with correct params
    expect(await browser.elementByCss('p').text()).toBe(expectedParams)

    // reloading the page should have the same params
    await browser.refresh()
    expect(await browser.elementByCss('p').text()).toBe(expectedParams)
  })

s('a').click()

    // should be rewritten with /en/


nction Page() {
  return (
    <>
      <p>
        This test does some hacky stuff to cause a `Math.random()` call to
        happen during the component tree creation which happens before the RSC
        render. Before the fix that this test accompanied landed this kind of
        sync IO was reported as a problem in Dev and in runtime during start. In
        practice this would be because of an OTEL span generation during
        create-component-tree. With this fix in place the create-component-tree

o the final prerender.
      </p>
      <p>
        In the long run we ought to move create-component-tree into the render
        itself and ensure the tree structure comports with CacheComponent rules.
        This test can be deleted at that time.

function will not leak any sync IO results int


use client'
import { useState } from 'react'

export default function Home() {
  const [state, setState] = useState('default')
  return (
    <div>
      <button
        onClick={() => {
          const worker = new SharedWorker(
            new URL('../shared-worker', import.meta.url),
            {
              type: 'module',
            }
          )
          worker.port.addEventListener('message', (event) => {
            setState(event.data)
            // Run a second time, which will use the same

r('message', (event) => {
              setState(event.data)
            })
            worker.port.start()
          })
          worker.port.start()
        }}
      >
        Get web worker data
      </button>

Worker state:

      <p id="w

worker
            const worker = new SharedWorker(
              new URL('../shared-worker', import.meta.url),
              {
                type: 'module',
              }
            )
            worker.port.addEventListene


port { Suspense } from 'react'
import ComponentClient from './component.client'

type Props = {
  file: string
  params: Promise<Record<string, string | string[]>>
}

async function ComponentServer(props: Props) {
  const params = await props.params
  return (
    <code
      data-server-file={props.file}
      data-server-params={JSON.stringify(params)}
    >
      {JSON.stringify(params)}
    </code>
  )
}

export default function Component(props: Props) {
  return (
    <div data-file={props.file}>

back={
Loading Server...
}>
        <ComponentServer {...props} />
      </Suspense>
      <Suspense fallback={
Loading Client...
}>
        <ComponentClient file={props.file} />
      </Suspense>

  )

File: {props.file}

      <Suspense fall


tTestSetup } from 'e2e-utils'

describe('parallel-routes-and-interception-catchall', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should render intercepted route and preserve other slots', async () => {
    const browser = await next.browser('/')

    const homeContent = 'Home Page\n\nOpen cart\nOpen catch all'
    const slotContent = 'Slot Page'

    expect(await browser.elementById('children-slot').text()).toBe(homeContent)
    expect(await browser.elementById('slot-slot').

ntById('cart-modal-intercept').text()).toBe(
      'Cart Modal'
    )

    // Children slot should still be the same
    expect(await browser.elementById('children-slot').text()).toBe(homeContent)
    expect(await browser.elementById('slot-slot').text()).t

text()).toBe(slotContent)

    // Check if navigation to modal route works
    await browser
      .elementByCss('[href="/cart"]')
      .click()
      .waitForElementByCss('#cart-modal-intercept')

    expect(await browser.eleme


          <Link href={'/first'} id="first" prefetch={false}>
            First
          </Link>

          <Link href={'/first-client'} id="first-client" prefetch={false}>
            First client
          </Link>

          <Link href={'/second'} id="second" prefetch={false}>
            Second
          </Link>

          <Link href={'/second-client'} id="second-client" prefetch={false}>
            Second

ved-a" prefetch={false}>
            Interleaved A
          </Link>

          <Link href={'/interleaved/b'} id="interleaved-b" prefetch={false}>
            Interleaved B
          </Link>

          <L

client
          </Link>

          <Link href={'/third'} id="third" prefetch={false}>
            Third
          </Link>

          <Link href={'/interleaved/a'} id="interlea


from 'e2e-utils'

describe('mdx-font-preload', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    dependencies: {
      '@next/mdx': 'canary',
      '@mdx-js/loader': '^2.2.1',
      '@mdx-js/react': '^2.2.1',
    },
  })

  it('should render MDX page', async () => {
    const browser = await next.browser('/')
    expect(await browser.elementByCss('h1').text()).toBe('MDX Page')
    expect(await browser.elementByCss('p').text()).toBe(
      'This is an MDX page with font preloading.'
    )

).toMatch(/myFont/)
  })

  it('should preload font from layout on MDX page', async () => {
    const browser = await next.browser('/')

    // Check for font preload link in DOM
    const fontPreloadLinks = await browser.elementsByCss('link[as="font"]')

  })

  it('should apply font class from layout', async () => {
    const browser = await next.browser('/')
    const fontFamily = await browser.eval(
      'getComputedStyle(document.body).fontFamily'
    )
    expect(fontFamily


('missing-suspense-with-csr-bailout', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    // This test is skipped when deployed because it's not possible to rename files after deployment.
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isNextDev) {
    it.skip('skip test for development mode', () => {})
    return
  }

  beforeEach(async () => {
    await next.clean()
  })

  const isCacheComponentsEnabled =
    process.env.__N

nse boundary at page "/".`

    it('should fail build if useSearchParams is not wrapped in a suspense boundary', async () => {
      const { exitCode } = await next.build()
      expect(exitCode).toBe(1)
      expect(next.cliOutput).toContain(message)

EXT_CACHE_COMPONENTS === 'true'

  describe('useSearchParams', () => {
    const message = isCacheComponentsEnabled
      ? 'https://nextjs.org/docs/messages/blocking-route'
      : `useSearchParams() should be wrapped in a suspe


pped } = nextTestSetup({
    files: __dirname,
    // This test is skipped because it sends requests with manipulated host headers
    // which doesn't work in a deployed environment
    skipDeployment: true,
  })

  if (skipped) return

  it('should include x-forwarded-* headers', async () => {
    const res = await next.fetch('/')
    const headers = await res.json()
    const url = new URL(next.url)

    expect(headers['x-forwarded-host']).toBe(url.host)
    expect(headers['x-forwarded-port']).toBe(url.p

ers['middleware-x-forwarded-proto']).toBe(
      url.protocol.replace(':', '')
    )
  })

  describe('host header exists', () => {
    it('should include x-forwarded-* headers relative to host', async () => {
      const url = new URL(next.url)
      cons

ort)
    expect(headers['x-forwarded-proto']).toBe(url.protocol.replace(':', ''))
    expect(headers['middleware-x-forwarded-host']).toBe(url.host)
    expect(headers['middleware-x-forwarded-port']).toBe(url.port)
    expect(head


ental: {
        staleTimes: {
          static: 30, // Minimum enforced by clientSegmentCache is 30 seconds
          dynamic: 5,
        },
      },
    },
  })

  if (isNextDev) {
    it('should skip next dev for now', () => {})
    return
  }

  it('should not fetch again when a static page was prefetched when navigating to it twice', async () => {
    let act: ReturnType<typeof createRouterAct>
    const browser = await next.browser('/', {
      beforePageLoad(page) {
        act = createRouterAct(page

t reveal.click()
        return browser.elementByCss('#to-static-page')
      },
      { includes: 'Static Page [prefetch-sentinel]' }
    )

    // Navigate to static page - should use prefetched data with no additional requests
    await act(async () =>

)
      },
    })

    // Reveal the link to trigger prefetch and wait for it to complete
    const link = await act(
      async () => {
        const reveal = await browser.elementByCss('#accordion-to-static-page')
        awai


ext dev for now', () => {})
    return
  }

  it('NEXT_RSC_UNION_QUERY query name is _rsc', async () => {
    expect(NEXT_RSC_UNION_QUERY).toBe('_rsc')
  })

  it('should show layout eagerly when prefetched with loading one level down', async () => {
    let act: ReturnType<typeof createRouterAct>
    const timeController = createTimeController()
    const browser = await next.browser('/', {
      beforePageLoad(page) {
        act = createRouterAct(page)
      },
    })

    await timeController.install(br

eveal.click()
        await browser.waitForElementByCss('#to-dashboard')
        return await browser.elementByCss('#to-dashboard')
      },
      { includes: '[dashboard-prefetch-sentinel]' }
    )

    const before = Date.now()
    await dashboardLink.cl

owser)

    // Reveal the dashboard accordion and wait for prefetch to complete
    const dashboardLink = await act(
      async () => {
        const reveal = await browser.elementByCss('#accordion-to-dashboard')
        await r


r) => {
      return browser.eval(`
        (() => {
          const OriginalDate = Date;
          const originalSetTimeout = globalThis.setTimeout;
          const originalSetInterval = globalThis.setInterval;
          const originalClearTimeout = globalThis.clearTimeout;
          const originalClearInterval = globalThis.clearInterval;

          let fixedTime = ${fixedTime};
          let timerIdCounter = 1;
          const timers = new Map();

          // Override the Date constructor
          globa

    }

            static now() {
              return fixedTime;
            }
          };

          // Preserve static methods
          Object.setPrototypeOf(globalThis.Date, OriginalDate);

          // Override setTimeout
          globalThis.setTim

lThis.Date = class extends OriginalDate {
            constructor(...args) {
              if (args.length === 0) {
                super(fixedTime);
              } else {
                super(...args);
              }


mponents/link-accordion'

export default function HomePage() {
  return (
    <>

Home Page [prefetch-sentinel]

      <LinkAccordion href="/dashboard" id="to-dashboard">
        To Dashboard
      </LinkAccordion>
      <LinkAccordion href="/static-page" id="to-static-page">
        To Static Page
      </LinkAccordion>
      <LinkAccordion
        href="/static-page-no-prefetch"
        id="to-static-page-no-prefetch"
      >
        To Static Page No Prefetch
      </LinkAccordi

-dynamic-page-no-params">
        To Dynamic Page
      </LinkAccordion>
      <LinkAccordion href="/prefetch-auto/foobar" id="to-dynamic-page">
        To Dynamic Slug Page
      </LinkAccordion>
      <a href="/static-page" id="to-static-page-hard">

on>
      <LinkAccordion href="/dynamic-page" id="to


('app-dir - bun externals', () => {
  const { next, isNextDev, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should handle bun builtins as external modules', async () => {
    const $ = await next.render$('/')

    // When not running in Bun, these should throw "Cannot find module" errors
    expect($('#bun-ffi').text()).toBe('external (not found)')
    expect($('#bun-jsc').text()).toBe('external (not found)')
    expect

 found)')
  })

  it('should handle bun builtins in server actions', async () => {
    const browser = await next.browser('/server-action')

    await browser.elementByCss('#test-action').click()

    await browser.waitForElementByCss('#action-result')

($('#bun-sqlite').text()).toBe('external (not found)')
    expect($('#bun-test').text()).toBe('external (not found)')
    expect($('#bun-wrap').text()).toBe('external (not found)')
    expect($('#bun').text()).toBe('external (not


be handled as external modules
  // When not running in Bun, require() will throw "Cannot find module"
  let bunFfiStatus = 'not loaded'
  let bunJscStatus = 'not loaded'
  let bunSqliteStatus = 'not loaded'
  let bunTestStatus = 'not loaded'
  let bunWrapStatus = 'not loaded'
  let bunStatus = 'not loaded'

  try {
    require('bun:ffi')
    bunFfiStatus = 'loaded successfully'
  } catch (e: any) {
    // Expected error when not running in Bun
    bunFfiStatus = e.message.includes('Cannot find module')

rnal (not found)'
      : 'error: ' + e.message
  }

  try {
    require('bun:sqlite')
    bunSqliteStatus = 'loaded successfully'
  } catch (e: any) {
    bunSqliteStatus = e.message.includes('Cannot find module')
      ? 'external (not found)'
      : 'e

   ? 'external (not found)'
      : 'error: ' + e.message
  }

  try {
    require('bun:jsc')
    bunJscStatus = 'loaded successfully'
  } catch (e: any) {
    bunJscStatus = e.message.includes('Cannot find module')
      ? 'exte


import { NextResponse } from 'next/server'

export async function GET() {
  const results: Record<string, string> = {}

  const modules = [
    { name: 'bunFfi', module: 'bun:ffi' },
    { name: 'bunJsc', module: 'bun:jsc' },
    { name: 'bunSqlite', module: 'bun:sqlite' },
    { name: 'bunTest', module: 'bun:test' },
    { name: 'bunWrap', module: 'bun:wrap' },
    { name: 'bun', module: 'bun' },
  ]

  for (const { name, module } of modules) {
    try {
      require(module)
      results[name] = 'loaded'

nd module error when not in Bun runtime
      // This confirms the module was externalized (not bundled)
      results[name] = e.message.includes('Cannot find module')
        ? 'external'
        : 'error'
    }
  }

  return NextResponse.json(results)
}

    } catch (e: any) {
      // Expected: Cannot fi


r'

export async function testBunExternals() {
  const modules = [
    'bun:ffi',
    'bun:jsc',
    'bun:sqlite',
    'bun:test',
    'bun:wrap',
    'bun',
  ]
  const results: Record<string, string> = {}

  for (const mod of modules) {
    try {
      require(mod)
      results[mod] = 'loaded'
    } catch (e: any) {
      // Expected: Cannot find module error when not in Bun runtime
      results[mod] = e.message.includes('Cannot find module')
        ? 'external (not found)'
        : 'error'
    }
  }

les should be external (not found when not in Bun)
  const allExternal = Object.values(results).every(
    (r) => r === 'external (not found)' || r === 'loaded'
  )
  return allExternal
    ? 'All Bun modules are external'
    : 'Some modules were not prop


('pnpm-workspace-root', () => {
  const { next, skipped } = nextTestSetup({
    files: {
      'app/layout.tsx': `
        import { ReactNode } from 'react'
        export default function Root({ children }: { children: ReactNode }) {
          return (
            <html>
              <body>{children}</body>
            </html>
          )
        }
      `,
      'app/page.tsx': `
        import { message } from '../../shared/utils'
        export default function Page() {
          return <p>{message}</p

ngify({
        name: 'parent-workspace',
        version: '1.0.0',
        lockfileVersion: 3,
      }),
      // Write pnpm-workspace.yaml in the same parent directory.
      // This file should be prioritized over lockfiles when determining root.

>
        }
      `,
      // Write a package-lock.json (npm lockfile, ignored by pnpm) to the application directory
      // directory to create the scenario where multiple "lockfiles" exist.
      'package-lock.json': JSON.stri


tTestSetup } from 'e2e-utils'

describe('app-dir - esm js extension', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should be able to render nextjs api in app router', async () => {
    const $ = await next.render$('/app')

    async function validateDomNodes(selector: string) {
      expect(await $(`${selector} .img`).prop('tagName')).toBe('IMG')
      expect(await $(`${selector} .link`).prop('tagName')).toBe('A')
      expect(await $(`${selector} .typeof-getImageProps`).text

st.js"]').length).toBe(1)
  })

  it('should be able to use nextjs api in pages router', async () => {
    const $ = await next.render$('/pages')

    expect(await $('meta[name="head-value-1"]').attr('content')).toBe(
      'with-ext'
    )
    expect(awai

()).toContain(
        'function'
      )
    }

    await validateDomNodes('#with-ext')
    await validateDomNodes('#without-ext')

    expect($('head link[href="/test-ext.js"]').length).toBe(1)
    expect($('head link[href="/te


import fs from 'fs-extra'
import os from 'os'
import path from 'path'
import {
  findPort,
  initNextServerScript,
  killApp,
  fetchViaHTTP,
} from 'next-test-utils'

if (!(globalThis as any).isNextStart) {
  it('should skip for non-next start', () => {})
} else {
  describe('output: standalone with ESM app dir', () => {
    const { next, skipped } = nextTestSetup({
      files: {
        app: new FileRef(path.join(__dirname, 'app')),
        pages: new FileRef(path.join(__dirname, 'pages')),
        publi

      return
    }

    beforeAll(async () => {
      await next.start()
    })

    it('should work correctly with output standalone', async () => {
      const tmpFolder = path.join(os.tmpdir(), 'next-standalone-' + Date.now())
      await fs.mkdirp(tmp

c: new FileRef(path.join(__dirname, 'public')),
        'next.config.js': 'export default {"output": "standalone"}',
      },
      packageJson: {
        type: 'module',
      },
      skipStart: true,
    })

    if (skipped) {


tTestSetup } from 'e2e-utils'

function countSubstring(str: string, substr: string): number {
  return str.split(substr).length - 1
}

// TODO(NAR-423): Migrate to Cache Components.
describe.skip('ppr-metadata-blocking-ppr-fallback', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    // Need to skip deployment because the test uses the private env cannot be used in deployment
    skipDeployment: true,
    env: {
      __NEXT_EXPERIMENTAL_STATIC_SHELL_DEBUGGING: '1',
    },
  })

itle>')).toBe(0)
  })

  it('should include viewport metadata in partial shell when metadata is dynamic under suspense', async () => {
    const $ = await next.render$(
      '/dynamic-metadata/partial?__nextppronly=fallback'
    )
    expect(countSubstrin

 if (skipped) return

  it('should not include metadata in partial shell when page is fully dynamic', async () => {
    const $ = await next.render$('/fully-dynamic?__nextppronly=fallback')
    expect(countSubstring($.html(), '<t


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


rt { 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 (

suspended component

      <NestedSuspendedComponent />

  )
}

async function NestedSuspendedComponent() {
  await

meout(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',


 stripAnsi from 'strip-ansi'

const nextConfigWithUseCache: NextConfig = {
  experimental: { useCache: true },
}

describe('use-cache-without-experimental-flag', () => {
  const { next, isNextStart, isTurbopack, skipped, isRspack } = nextTestSetup({
    files: __dirname,
    skipStart: process.env.NEXT_TEST_MODE !== 'dev',
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isNextStart) {
    it('should fail the build with an error', async () => {
      const { cliOutput } = await next.bu

       )
        expect(buildOutput).toContain(
          'Read more: https://nextjs.org/docs/app/api-reference/directives/use-cache#usage'
        )
        expect(buildOutput).toContain('Ecmascript file had an error')
        expect(buildOutput).toContai

ild()
      const buildOutput = getBuildOutput(cliOutput)

      if (isTurbopack) {
        expect(buildOutput).toContain(
          'To use "use cache", please enable the feature flag `cacheComponents` in your Next.js config.'


port { nextTestSetup } from 'e2e-utils'

describe('app dir - metadata dynamic routes with async deps', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    dependencies: {
      '@vercel/og': 'latest',
    },
  })

  it('should render page with og:image meta tag when opengraph-image has async dependencies', async () => {
    const $ = await next.render$('/blog/hello-world')
    const ogImageUrl = $('meta[property="og:image"]').attr('content')
    expect(ogImageUrl).toContain('/blog/hello-wor

mage')
  })

  it('should serve the opengraph-image route as a valid image', async () => {
    const res = await next.fetch('/blog/hello-world/opengraph-image')
    expect(res.status).toBe(200)
    expect(res.headers.get('content-type')).toBe('image/png')


sNextDeploy, skipped } = nextTestSetup({
    files: __dirname,
    // `patchFile` and reading runtime logs are not supported in a deployed environment
    skipDeployment: true,
  })

  if (skipped) return
  const pathPrefix = '/' + runtimeValue

  let currentCliOutputIndex = 0

  const ignorePreviousLogs = () => {
    currentCliOutputIndex = next.cliOutput.length
  }
  const resetLogIsolation = () => {
    currentCliOutputIndex = 0
  }

  const getLogs = () => {
    if (next.cliOutput.length < currentCliOut

beforeEach(() => {
    ignorePreviousLogs()
  })

  it('runs in dynamic pages', async () => {
    const response = await next.fetch(pathPrefix + '/123/dynamic')
    expect(response.status).toBe(200)
    await retry(() => {
      expect(getLogs()).toContain

putIndex) {
      // cliOutput shrank since we started the test, so something (like a `sandbox`) reset the logs
      currentCliOutputIndex = 0
    }
    return Log.readCliLogs(next.cliOutput.slice(currentCliOutputIndex))
  }


kies } from 'next/headers'
import { NextResponse, after } from 'next/server'
import { cliLog } from './utils/log'

export async function middleware(
  /** @type {import ('next/server').NextRequest} */ request
) {
  const url = new URL(request.url)

  {
    const match = url.pathname.match(
      /^(?<prefix>\/[^/]+?)\/middleware\/redirect-source/
    )
    if (match) {
      const prefix = match.groups.prefix
      const requestId = url.searchParams.get('requestId')
      const cookieStore = await cookies()

urn NextResponse.redirect(
        new URL(prefix + '/middleware/redirect', request.url)
      )
    }
  }

  {
    const match = url.pathname.match(
      /^(?<prefix>\/[^/]+?)\/provided-request-context\/middleware/
    )
    if (match) {
      const pref

      after(async () => {
        cliLog({
          source: '[middleware] /middleware/redirect-source',
          requestId,
          cookies: { testCookie: cookieStore.get('testCookie')?.value },
        })
      })
      ret


t { after } from 'next/server'
import { cookies } from 'next/headers'

export default async function Index() {
  const cookieStore = await cookies()
  after(async () => {
    cookieStore.set('testCookie', 'after-render', { path: '/' })
  })

  const action = async () => {
    'use server'
    const cookieStore = await cookies()
    cookieStore.set('testCookie', 'action', { path: '/' })

    after(async () => {
      cookieStore.set('testCookie', 'after-action', { path: '/' })
    })
  }

  return (
    <div

ge with after() that tries to set cookies

        Cookie: {JSON.stringify(cookieStore.get('testCookie')?.value ?? null)}

      <form action={action}>
        <button type="submit">Submit</button>
      </form>


ort { after, connection } from 'next/server'
import { cache } from 'react'
import { cliLog } from '../../../utils/log'

const thing = cache(() => Symbol('cache me please'))

export default async function Index(props) {
  await connection()
  const valueFromRender = thing()

  after(async () => {
    const valueFromAfter = thing()

    cliLog({
      source: '[page] /nested-after (after #1)',
      assertions: {
        'cache() works in after()': valueFromRender === valueFromAfter,
      },
    })

    afte

    })

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

    after(() => {
      const valueFromAfter = thing()

      cliLog({
        source: '[page] /nested-after (after #3)',
        assertions: {
          'cache() works in after()': val

r(() => {
      const valueFromAfter = thing()

      cliLog({
        source: '[page] /nested-after (after #2)',
        assertions: {
          'cache() works in after()': valueFromRender === valueFromAfter,
        },
      })


ter } from 'next/server'
import { Suspense } from 'react'
import { cliLog } from '../../../../../utils/log'

export default async function Page() {
  await connection()
  after(() => {
    cliLog({
      source: '[page] /interrupted/incomplete-stream/hang',
    })
  })
  return (

Hanging forever

      <Suspense
        fallback={

            {
              // we're going to look for this string in the streamed response,
              // make s

 else
              'Loading' + (Math.random() > 1 ? 'impossible' : '') + '...'
            }
          </div>
        }
      >
        <HangForever />
      </Suspense>
    </main>
  )
}

async function HangForever() {
  await new Promise((_resolve) => {

ure it doesn't show up in the literal form someplace


('Script component with beforeInteractive strategy CSS class rendering', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should render script tag with correct class attribute instead of classname', async () => {
    const browser = await next.browser('/')

    // Wait for the page to fully load
    await browser.waitForElementByCss('script#example-script')

    // Get the HTML content to check the actual rendered attributes
    const html = await browser.eval(() => document.docu

he script element directly
    const scriptElement = await browser.elementByCss('script#example-script')
    const className = await scriptElement.getAttribute('class')

    expect(className).toBe('example-class')
  })

  it('should execute beforeInteracti

mentElement.innerHTML)

    // Check that the script tag has 'class' attribute, not 'classname'
    expect(html).toContain('class="example-class"')
    expect(html).not.toContain('classname="example-class"')

    // Also verify t


xport default function MultiplePage() {
  return (

Multiple beforeInteractive Scripts Test

nnerHTML={{
          __html: `
            console.log('Second beforeInteractive script executed');
            window.secondScriptExecuted = true;
          `,
        }}
      />
      <p>
        This page tests multiple beforeInteractive scripts with

   className="second-script"
        dangerouslySetI


'

const isAdapterTest = Boolean(process.env.NEXT_ENABLE_ADAPTER)

type NextInstance = ReturnType<typeof nextTestSetup>['next']

function createSplitHTMLFetcher(next: NextInstance) {
  return async function fetchSplitHTML(pathname: string) {
    let response: Awaited<ReturnType<typeof next.fetch>> | undefined
    const [staticPart, dynamicPart] = await splitResponseWithPPRSentinel(
      async () => {
        response = await next.fetch(pathname)
        expect(response.status).toBe(200)

        if (!respo

oad(staticPart),
    }
  }
}

describe('partial-fallback-shell-upgrade', () => {
  const { next, isNextDev } = nextTestSetup({
    files: path.join(__dirname, 'fixtures', 'default'),
    // The latest changes to support this behavior on deployed infra are

nse.body) {
          throw new Error(`Expected a streamed response body for ${pathname}`)
        }

        return response.body
      }
    )

    return {
      response: response!,
      dynamicPart,
      static$: cheerio.l


 await browser.loadPage(new URL(`/?n=1&unrelated`, next.url).toString())
    expect(await browser.waitForElementByCss('#x').text()).toBe('1')
    const random1b = await browser.waitForElementByCss('#y').text()

    // The two navigations to n=1 should use a cached value.
    expect(random1a).toBe(random1b)

    // The navigation to n=2 should be some other random value.
    expect(random1a).not.toBe(random2)

    // Client component should have rendered.
    expect(await browser.waitForElementByCss('#z').te

ync () => {
    const browser = await next.browser(`/custom-handler?n=1`)
    expect(await browser.waitForElementByCss('#x').text()).toBe('1')
    const random1a = await browser.waitForElementByCss('#y').text()

    await browser.loadPage(new URL(`/custom-

xt()).toBe('foo')

    // Client component child should have rendered but not invalidated the cache.
    expect(await browser.waitForElementByCss('#r').text()).toContain('rnd')
  })

  it('should cache results custom handler', as


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

describe('RSC binary serialization', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      'server-only': 'latest',
    },
  })
  if (skipped) return

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

  it('should correctly encode/decode binaries and hydrate', async function () {
    const browser = await next.browser('/')
    await check(async () => {

nt = await browser.elementByCss('body').text()

      return content.includes('utf8 binary: hello') &&
        content.includes('arbitrary binary: 255,0,1,2,3') &&
        content.includes('hydrated: true')
        ? 'success'
        : 'fail'
    }, 'succ


e client'

import { useActionState } from 'react'

export function Form({
  revalidateAction,
  initialValues,
}: {
  revalidateAction: (type: 'tag' | 'path') => Promise<[number, number, string]>
  initialValues: [number, number, string]
}) {
  const [
    [useCacheValue1, useCacheValue2, fetchedValue],
    revalidate,
    isPending,
  ] = useActionState(
    async (_state: [number, number, string], type: 'tag' | 'path') =>
      revalidateAction(type),
    initialValues
  )

  return (
    <form>
      <p>

alue}</p>
      <button
        id="revalidate-tag"
        formAction={() => revalidate('tag')}
        disabled={isPending}
      >
        Revalidate Tag
      </button>
      <button
        id="revalidate-path"
        formAction={() => revalidate('pa

before revalidate: {useCacheValue1}

        after revalidate: {useCacheValue2}

{fetchedV


 { revalidatePath, cacheTag, updateTag } from 'next/cache'
import { Form } from './form'
import { connection } from 'next/server'

async function fetchCachedValue() {
  return fetch('https://next-data-api-endpoint.vercel.app/api/random', {
    next: { tags: ['revalidate-and-use'], revalidate: false },
  }).then((res) => res.text())
}

async function getCachedValue() {
  'use cache'
  cacheTag('revalidate-and-use')
  return Math.random()
}

export default async function Page() {
  // Make the page dynamic, a

()

        if (type === 'tag') {
          updateTag('revalidate-and-use')
        } else {
          revalidatePath('/revalidate-and-use')
        }

        return Promise.all([
          initialCachedValue,
          getCachedValue(),
          fetchCa

s we don't want to deal with ISR in this scenario.
  await connection()

  return (
    <Form
      revalidateAction={async (type: 'tag' | 'path') => {
        'use server'

        const initialCachedValue = await getCachedValue


ort { connection } from 'next/server'
import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'

async function Inner() {
  await setTimeout(1000)

  return
{Math.random()}

}

// We're only using the id to force using a different cache key for different
// test scenarios.
async function InnerCached({ id }: { id: string; name: string }) {
  'use cache: remote'

  return (
    <Suspense fallback={
Loading...
}>
      <Inner />
    </Suspens

e: remote'

  return <InnerCached id={id} name="inner" />
}

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  await connection()
  const { id } = await params

  return (
    <>
      <OuterCached1 id={id} name="

e>
  )
}

async function OuterCached1({ id }: { id: string; name: string }) {
  'use cache: remote'

  return <InnerCached id={id} name="inner" />
}

async function OuterCached2({ id }: { id: string; name: string }) {
  'use cach


'next/navigation'

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

        This tests a regression case where an action called outside of a React
        transition scope (onClick, in this case) results in a server-side
        redirect. Before the fix, the external redirect would get swallowed by
        Next.js.

        Clicking the button should redirect to localhost:9292. (The redirect
        doesn't need to actually load; the associated e2e test will intercept

uest.)
      </p>
      <button
        id="external-redirect-from-action-on-click"
        onClick={async () => {
          'use server'
          redirect('http://localhost:9292')
        }}
      >
        External Redirect
      </button>
    </>
  )
}


t { createSandbox } from 'development-sandbox'

describe('use-cache-unknown-cache-kind', () => {
  const { next, isNextStart, isTurbopack, isRspack, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: process.env.NEXT_TEST_MODE !== 'dev',
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isNextStart) {
    beforeAll(async () => {
      await next.build()
    })

    it('should fail the build with an error', async () => {
      const buildOutput = getBuildOutput(next.cliOutp

andler for this kind in the \`cacheHandlers\` object in your Next.js config.
         > 1 | 'use cache: custom'
             | ^^^^^^^^^^^^^^^^^^^
           2 |
           3 | export default async function Page() {
           4 |   return <p>hello world</

ut)

      if (isTurbopack) {
        expect(buildOutput).toMatchInlineSnapshot(`
         "Error: Turbopack build failed with 1 errors:
         ./app/page.tsx:1:1
         Unknown cache kind "custom". Please configure a cache h


import { nextTestSetup } from 'e2e-utils'

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

  // Recommended for tests that check HTML. Cheerio is a HTML parser that has a jQuery like API.
  it('should work using cheerio', async () => {
    const $ = await next.render$('/')
    expect($('p').text()).toBe('hello world')
  })

  // Recommended for tests that need a full browser
  it('should work using browser', async () => {
    const browser = await next.browser('/'

r('/')
    expect(html).toContain('hello world')
  })

  // In case you need to test the response object
  it('should work with fetch', async () => {
    const res = await next.fetch('/')
    const html = await res.text()
    expect(html).toContain('hello

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

  // In case you need the full HTML. Can also use $.html() with cheerio.
  it('should work with html', async () => {
    const html = await next.rende

test/e2e/app-dir/test-template/{{ toFileName name }}/{{ toFileName name }}.test.ts


: string
  status: '200' | '404'
}

type RouteSection = {
  title: string
  columns: string[]
  routes: RouteInfo[]
}

const ROUTE_SECTIONS: RouteSection[] = [
  {
    title: 'Base Routes',
    columns: ['Route', 'Expected Status'],
    routes: [
      { href: '/en', status: '200' },
      { href: '/fr', status: '200' },
      { href: '/es', status: '404' },
    ],
  },
  {
    title: 'Without generateStaticParams',
    columns: ['Route', 'Expected Status'],
    routes: [
      { href: '/en/no-gsp/stories/d

['Route', 'Expected Status'],
    routes: [
      {
        href: '/en/gsp/stories/static-123',
        status: '200',
      },
      {
        href: '/fr/gsp/stories/static-123',
        status: '200',
      },
      {
        href: '/es/gsp/stories/stati

ynamic-123', status: '200' },
      { href: '/fr/no-gsp/stories/dynamic-123', status: '200' },
      { href: '/es/no-gsp/stories/dynamic-123', status: '200' },
    ],
  },
  {
    title: 'With generateStaticParams',
    columns:


tTestSetup } from 'e2e-utils'
import type { Response } from 'node-fetch'

import cookies, { nextConfigHeaders } from './cookies.mjs'

function getSetCookieHeaders(res: Response): ReadonlyArray<string> {
  return (
    (res.headers as any).getSetCookie?.() ??
    (res.headers as any).raw()['set-cookie']
  )
}

describe('set-cookies', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    // TODO: re-enable once this behavior is corrected on deploy
    skipDeployment: true,
  })

  if (

imes)('for %s runtime', (runtime) => {
      it('should set two set-cookie headers', async () => {
        let res = await next.fetch(`/api/${dir}/${runtime}`)

        let headers = getSetCookieHeaders(res)

        expect(headers).toHaveLength(2)

skipped) {
    return
  }

  describe.each([
    { dir: 'pages', runtimes: ['edge', 'experimental-edge', 'node'] },
    { dir: 'app', runtimes: ['edge', 'node'] },
  ])('for /$dir', ({ dir, runtimes }) => {
    describe.each(runt


uteRedirect,
  multiRelativeRedirect,
  relativeRedirect,
} from '../actions'

export default function Page() {
  return (
    <>

hello subdir page

      <button
        onClick={async () => {
          startTransition(async () => {
            await relativeRedirect()
          })
        }}
        id="relative-subdir-redirect"
      >
        relative redirect
      </button>
      <button
        onClick={async () => {
          startTransition(async () => {
            await multiRelativeR

relative-subdir-redirect"
      >
        multi-level relative redirect
      </button>
      <button
        onClick={async () => {
          startTransition(async () => {
            await absoluteRedirect()
          })
        }}
        id="absolute-s

edirect()
          })
        }}
        id="multi-


dge SSR', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should handle edge only routes', async () => {
    const appHtml = await next.render('/edge/basic')
    expect(appHtml).toContain('
Edge!
')

    const pageHtml = await next.render('/pages-edge')
    expect(pageHtml).toContain('
pages-edge-ssr
')
  })

  it('should retrieve cookies in a server component in the edge runtime', async () => {
    co

apis/process')
    expect(await $('#process').text()).toContain('object')
  })

  it('should handle /index routes correctly', async () => {
    const appHtml = await next.render('/index')
    expect(appHtml).toContain('the /index route')
  })

  if ((globa

nst res = await next.fetch('/edge-apis/cookies')
    expect(await res.text()).toInclude('Hello')
  })

  it('should treat process as object without polyfill in edge runtime', async () => {
    const $ = await next.render$('/edge-


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

describe('app dir - unauthorized with default unauthorized boundary', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  // TODO: error unauthorized usage in root layout
  it.skip('should error on client unauthorized from root layout in browser', async () => {
    const browser = await next.browser('/')

    await browser.e

  )
    }
  })

  // TODO: error unauthorized usage in root layout
  it.skip('should error on server unauthorized from root layout on server-side', async () => {
    const browser = await next.browser('/?root-unauthorized=1')

    if (isNextDev) {
      aw

lementByCss('#trigger-unauthorized').click()

    if (isNextDev) {
      await waitForRedbox(browser)
      expect(await getRedboxDescription(browser)).toMatch(
        /unauthorized\(\) is not allowed to use in root layout/


nextTestSetup({
    files: __dirname,
    dependencies: {
      sass: 'latest',
    },
  })

  it('should successfully hard navigate from pages -> app', async () => {
    const browser = await next.browser('/base/pages-path')
    await browser.elementByCss('#to-another').click()
    // Hard nav from pages -> app triggers on-demand compilation in dev; allow extra time
    await browser.waitForElementByCss('#page-2', 30000)
  })

  it('should support `basePath`', async () => {
    const html = await next.rend

yCss('a[href="/base/another"]')
        .click()
        .waitForElementByCss('#page-2')
        .text()
    ).toBe(`Page 2`)
  })

  it('should prefix segment metadata og image with basePath and pathname', async () => {
    const $ = await next.render$('/

er('/base')
    expect(html).toContain('
Test Page
')
  })

  it('should support Link with basePath prefixed', async () => {
    const browser = await next.browser('/base')
    expect(
      await browser
        .elementB


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

describe('dynamic-interception-route-revalidate', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should refresh the dynamic intercepted route when the interception route is revalidated', async () => {
    const browser = await next.browser('/en')

    // trigger the interception
    await browser.elementByCss("[href='/en/photos/1/view']").click()

    // verify we're on the intercepted page
    await browser

e fired
    await browser.waitForElementByCss('#loading')

    // verify that we get a result from the server action
    await retry(async () => {
      const result = await browser.elementByCss('#result').text()
      expect(result).toMatch(/^Result: 0(\.

.waitForElementByCss('#intercepted-page')

    expect(await browser.elementByCss('h2').text()).toBe('Photo Id: 1')

    // trigger the revalidate
    await browser.elementByCss('button').click()

    // make sure the loading stat


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

describe('actions-revalidate-remount', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should not remount the page + loading component when revalidating', async () => {
    const browser = await next.browser('/test')
    const initialTime = await browser.elementById('time').text()

    expect(initialTime).toMatch(/Time: \d+/)

    await browser.elementByCss('button').click()

    await retry(async () => {
      const time = await b

.toMatch(/Time: \d+/)

      // The time should be updated
      expect(initialTime).not.toBe(time)

      const logs = (await browser.log()).filter(
        (log) => log.message === 'Loading Mounted'
      )

      // There should not be any loading logs

rowser.elementById('time').text()
      expect(time)


from 'e2e-utils'

// TODO(NAR-423): Migrate to Cache Components.
describe.skip('static-shell-debugging', () => {
  const ppr = Boolean(process.env.__NEXT_CACHE_COMPONENTS)
  const context = {
    ppr,
    debugging: ppr,
  }

  const { next, skipped, isNextDev } = nextTestSetup({
    files: __dirname,
    // This test skips deployment because env vars that are doubled underscore prefixed
    // are not supported. This is also intended to be used in development.
    skipDeployment: true,
    env: {
      __N

xt.ppr) {
    it('should only render the static shell', async () => {
      const res = await next.fetch('/?__nextppronly=1')
      expect(res.status).toBe(200)

      const html = await res.text()
      expect(html).toContain('Fallback')
      expect(html

EXT_EXPERIMENTAL_STATIC_SHELL_DEBUGGING: context.debugging
        ? '1'
        : undefined,
    },
    nextConfig: {
      experimental: { ppr: context.ppr },
    },
  })

  if (skipped) return

  if (context.debugging && conte


-test-utils'

const runtimes = ['nodejs', 'edge']

const WAIT_BEFORE_REVALIDATING = 1000

// If we want to verify that `after()` ran its callback,
// we need it to perform some kind of side effect (because it can't affect the response).
// In other tests, we often use logs for this, but we don't have access to those in deploy tests.
// So instead this test relies on calling `revalidatePath` inside `after`
// to revalidate an ISR page '/timestamp/key/[key]', and then checking if the timestamp changed --
// i

ntimes)('after() in %s runtime', (runtimeValue) => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    env: { WAIT_BEFORE_REVALIDATING: WAIT_BEFORE_REVALIDATING + '' },
  })
  const retryDuration = WAIT_BEFORE_REVALIDATING * 2

  if (sk

f it did, we successfully ran the callback (and performed a side effect).

// This test relies on revalidating a static page, so it can't work in dev mode.
const _describe = isNextDev ? describe.skip : describe

_describe.each(ru


nst revalidate = 3600 // arbitrarily long, just so that it doesn't happen during a test run
export const dynamicParams = true

export async function generateStaticParams() {
  return ['nodejs', 'edge'].flatMap((runtime) =>
    ['dynamic-page', 'middleware', 'route', 'server-action'].map((page) => ({
      key: `/${runtime}/${page}`,
    }))
  )
}

export default async function Page({ params }) {
  const { key } = await params
  const data = {
    key,
    timestamp: Date.now(),
  }
  console.log('/timestamp

 {
    const decoded = decodeURIComponent(key)
    new URL(decoded, 'http://__n')
    path = decoded
  } catch (err) {}

  return (
    <>
      {path !== null && (
        <Link prefetch={false} href={path}>
          Go to {path}
        </Link>
      )}

/key/[key] rendered', data)

  let path = null
  try


tion Home() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black font-[family-name:var(--font-geist-sans)]">
      <main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={100}
          height={20}
          priority
        />
        <div cla

 the page.tsx file.
          </h1>
          <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
            Looking for a starting point or more instructions? Head over to{' '}
            <a
              href="https://vercel.com

ssName="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
          <h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
            To get started, edit


mport { nextTestSetup } from 'e2e-utils'

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

  it('regression: Server Action triggered from onClick redirects to external URL', async () => {
    let page
    const browser = await next.browser('/', {
      async beforePageLoad(p) {
        page = p
        await page.route('**/*', (route) => {
          const req = route.request()
          // Intercept the request to the external page and mock the response.

 return route.continue()
        })
      },
    })
    const button = await browser.elementById(
      'external-redirect-from-action-on-click'
    )
    await button.click()
    await page.waitForNavigation('http://localhost:9292')
    expect(await page.

if (req.url().includes('localhost:9292')) {
            return route.fulfill({
              status: 200,
              body: '<!DOCTYPE html>External page',
            })
          }


on } from 'next/server'
import { ReactNode, Suspense } from 'react'

// A cache fill in the Dynamic stage (after `connection()`) should not be
// subject to the dev fill timeout. This mirrors prerender, where caches
// past `connection()` aren't executed at all and therefore not subject to
// the timeout handling either. The sleep is intentionally longer than the
// configured `experimental.useCacheTimeout` (10s) so the test fails if the
// timer isn't cleared.
async function Cached(): Promise<ReactNode> {

imeout(resolve, 12_000))

  return
cached

}

async function Dynamic(): Promise<ReactNode> {
  await connection()

  return <Cached />
}

export default function Page() {
  return (
    <Suspense fallback="Loading...">
      <Dynamic />

  'use cache'

  await new Promise((resolve) => setT


  return result
}

const PAGES: Record<
  string,
  {
    group: string
    url: string
    selector: string
    color: string
    background?: string
    conflict?: boolean
    conflictTurbo?: boolean
    brokenLoading?: boolean
    brokenLoadingDev?: boolean
    requests?: number
    requestsLoose?: number
    requestsGraph?: number
  }
> = {
  first: {
    group: 'basic',
    url: '/first',
    selector: '#hello1',
    color: 'rgb(0, 0, 255)',
    requests: 1,
  },
  second: {
    group: 'basic',
    u

lient': {
    group: 'basic',
    url: '/first-client',
    selector: '#hello1c',
    color: 'rgb(255, 0, 255)',
    requests: 1,
  },
  'second-client': {
    group: 'basic',
    url: '/second-client',
    selector: '#hello2c',
    color: 'rgb(255, 128, 0

rl: '/second',
    selector: '#hello2',
    color: 'rgb(0, 128, 0)',
    requests: 1,
  },
  third: {
    group: 'basic',
    url: '/third',
    selector: '#hello3',
    color: 'rgb(0, 128, 128)',
    requests: 1,
  },
  'first-c


 { nextTestSetup } from 'e2e-utils'

describe('app-dir - params hooks compat', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should only access search params with useSearchParams', async () => {
    const browserApp = await next.browser('/app/foobar?q=app')
    const appSearchparamsJSON = JSON.parse(
      await browserApp.elementByCss('#use-search-params').text()
    )
    const browserPages = await next.browser('/pages/foobar?q=pages')
    const pagesSearchparamsJSON = JSON.

params with useParams', async () => {
    const browserApp = await next.browser('/app/foobar?a=app')
    const appParamsJSON = JSON.parse(
      await browserApp.elementByCss('#use-params').text()
    )
    const browserPages = await next.browser('/pages/f

parse(
      await browserPages.elementByCss('#use-search-params').text()
    )

    expect(appSearchparamsJSON).toEqual({ q: 'app' })
    expect(pagesSearchparamsJSON).toEqual({ q: 'pages' })
  })

  it('should only access path


test-utils'

describe('parallel-routes-catchall-groups', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should work without throwing any errors about conflicting paths', async () => {
    const browser = await next.browser('/')

    await check(() => browser.elementByCss('body').text(), /Home/)
    await browser.elementByCss('[href="/foo"]').click()
    // Foo has a page route defined, so we'd expect to see the page content
    await check(() => browser.elementByCss('body').tex

ar doesn't have an explicit page path. (group-a) defines a catch-all slot with a separate root layout
    // that only renders a slot (ie no children). So we'd expect to see the fallback slot content
    await browser.elementByCss('[href="/bar"]').click()

t(), /Foo Page/)
    await browser.back()

    // /b


tSetup } from 'e2e-utils'
import path from 'path'

describe('app-dir edge SSR invalid reexport', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: {
      'app/export': new FileRef(path.join(__dirname, 'app', 'export')),
      'app/export/inherit/page.tsx':
        "export { default, runtime, preferredRegion } from '../basic/page'",
    },
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should warn or error about the re-export of a pages ru

// Turbopack is stricter and disallows reexports completely
      // webpack merely warns in the CLI and still serves the page wuthout a redbox
      if (process.env.IS_TURBOPACK_TEST) {
        await expect(browser).toDisplayRedbox(`
         {

ntime/preferredRegion config', async () => {
    try {
      await next.start()
    } catch (_) {
      // We expect the build to fail
    }

    if (isNextDev) {
      const browser = await next.browser('/export/inherit')


tTestSetup } from 'e2e-utils'

describe('app dir - unauthorized - basic', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should match dynamic route unauthorized boundary correctly', async () => {
    // `/dynamic` display works
    const browserDynamic = await next.browser('/dynamic')
    expect(await browserDynamic.elementByCss('main').text()).toBe('dynamic')

    // `/dynamic/401` calling unauthorized() will match the same level unauthorized boundary
    const browserError =

t browserDynamicId.elementByCss('#page').text()).toBe(
      'dynamic [id]'
    )
  })

  it('should escalate unauthorized to parent layout if no unauthorized boundary present in current layer', async () => {
    const browserDynamic = await next.browser(

await next.browser('/dynamic/401')
    expect(await browserError.elementByCss('#unauthorized').text()).toBe(
      'dynamic/[id] unauthorized'
    )

    const browserDynamicId = await next.browser('/dynamic/123')
    expect(awai


/action'

export default function Page() {
  return (
    <div>
      <form>
        <button
          id="redirect-relative"
          formAction={() => redirectAction('/another')}
        >
          redirect internal with relative path
        </button>
      </form>
      <form>
        <button
          id="redirect-absolute-internal"
          formAction={() => redirectAction(location.origin + '/base/another')}
        >
          redirect internal with domain (including basePath)
        </button>

d="redirect-absolute-external"
          formAction={() =>
            redirectAction(location.origin + '/outsideBasePath')
          }
        >
          redirect external with domain (excluding basePath)
        </button>
      </form>
    </div>
  )
}

    </form>
      <form>
        <button
          i


export function middleware(
  /** @type {import ('next/server').NextRequest} */ request
) {
  const url = new URL(request.url)

  const match = url.pathname.match(/^(?<prefix>\/[^/]+?)\/middleware/)
  if (match) {
    const pathPrefix = match.groups.prefix
    after(async () => {
      // we can't call revalidatePath from middleware, so we need to do it via an endpoint instead
      const pathToRevalidate = pathPrefix + `/middleware`

      const postUrl = new URL('/timestamp/trigger-revalidate', url.href

validate)

      const response = await fetch(postUrl, { method: 'POST' })
      if (!response.ok) {
        throw new Error(
          `Failed to revalidate path '${pathToRevalidate}' (status: ${response.status})`
        )
      }
    })
  }
}

export co

)
      postUrl.searchParams.append('path', pathToRe


retry } from 'next-test-utils'

describe('server-actions-relative-redirect', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should work with relative redirect', async () => {
    const browser = await next.browser('/')
    await browser.waitForElementByCss('#relative-redirect').click()

    await retry(async () => {
      expect(await browser.waitForElementByCss('#page-loaded').text()).toBe(
        'hello nested page'
      )
    })
  })

  it('should work with absolute redire

()).toBe(
        'hello nested page'
      )
    })
  })

  it('should work with relative redirect from subdir', async () => {
    const browser = await next.browser('/subdir')
    await browser.waitForElementByCss('#relative-subdir-redirect').click()

ct', async () => {
    const browser = await next.browser('/')
    await browser.waitForElementByCss('#absolute-redirect').click()

    await retry(async () => {
      expect(await browser.waitForElementByCss('#page-loaded').text


tTestSetup({
    skipDeployment: true,
    files: __dirname,
  })

  if (skipped) return

  if (isNextStart) {
    it('should not log server actions in production mode', async () => {
      const browser = await next.browser('/')
      const outputIndex = next.cliOutput.length

      await browser.elementByCss('#success-action').click()
      await browser.waitForElementByCss('#result')

      // Wait a bit and verify no action logs appear
      await retry(() => {
        const logs = stripAnsi(next.cliOut

    const browser = await next.browser('/')
    const outputIndex = next.cliOutput.length

    await browser.elementByCss('#success-action').click()
    await browser.waitForElementByCss('#result')

    await retry(() => {
      const logs = stripAnsi(nex

put.slice(outputIndex))
        // Should not contain the server action log format
        expect(logs).not.toContain('└─ ƒ successAction')
      })
    })
    return
  }

  it('should log successful server action', async () => {


ort default function ActionButtons({
  successAction,
  multiArgAction,
  redirectAction,
  notFoundAction,
  errorAction,
  objectArgAction,
  arrayArgAction,
  inlineAction,
  promiseArgAction,
}) {
  const [result, setResult] = useState(null)
  const [error, setError] = useState(null)

  const handleSuccess = async () => {
    setError(null)
    const res = await successAction(5)
    setResult(res)
  }

  const handleMultiArg = async () => {
    setError(null)
    const res = await multiArgAction(1, 2, 3

)
    } catch (e) {
      setError('notFound triggered')
    }
  }

  const handleError = async () => {
    setError(null)
    try {
      await errorAction()
    } catch (e) {
      setError(e.message)
    }
  }

  const handleObjectArg = async () => {

)
    setResult(res)
  }

  const handleRedirect = async () => {
    setError(null)
    await redirectAction('/redirect-target')
  }

  const handleNotFound = async () => {
    setError(null)
    try {
      await notFoundAction(


erver'

import { redirect } from 'next/navigation'
import { notFound } from 'next/navigation'

// Simple successful action
export async function successAction(value) {
  return { result: value + 1 }
}

// Action with multiple arguments
export async function multiArgAction(a, b, c) {
  return { sum: a + b + c }
}

// Action that redirects (should show 303 status)
export async function redirectAction(path) {
  redirect(path)
}

// Action that throws notFound (should show 404 status)
export async function notF

on objectArgAction(data) {
  return { received: data }
}

// Action with array argument
export async function arrayArgAction(items) {
  return { count: items.length }
}

// Inline action export for testing inline action display
export const inlineAction =

oundAction() {
  notFound()
}

// Action that throws an error (should show 500 status)
export async function errorAction() {
  throw new Error('Intentional error for testing')
}

// Action with object argument
export async functi


tTestSetup } from 'e2e-utils'

describe('metadata-invalid-image-file', () => {
  const { next, isTurbopack, isNextDev, skipped, isRspack } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    skipStart: true,
  })

  if (skipped) return

  it('should error on invalid metadata image file', async () => {
    // In dev, it needs to render the page first
    if (isNextDev) {
      await next.start()
      await next.fetch('/')
    } else {
      await next.build()
    }

    if (isTurbopack) {

).toContain('unable to decode image data')
    } else {
      expect(next.cliOutput).toContain(
        'Error: Process image "/favicon.ico" failed:'
      )
    }

    if (!isNextDev) {
      // `next build` should fail
      if (isTurbopack) {
        ex

      // In turbopack the image decoding error is displayed in multiple lines
      expect(next.cliOutput).toContain('app/favicon.ico')
      expect(next.cliOutput).toContain('Processing image failed')
      expect(next.cliOutput


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

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

  it('should contain rsc query in rsc request when redirect the page', async () => {
    const browser = await next.browser('/redirect')

    const rscRequestUrls: string[] = []
    browser.on('request', (req) => {
      if (req.url().includes('?_rsc=')) {
        rscRequestUrls.push(req.url())
      }
    })

    // Click redirect link
    const link

 redirect source and dest urls should both contain the rsc query
    expect(rscRequestUrls[0]).toContain('/redirect/source')
    expect(rscRequestUrls[1]).toContain('/redirect/dest')
  })

  it('should contain rsc query in rsc request when rewrite the page

 = await browser.elementByCss('a')
    await link.click()

    // Wait for the page load to be completed
    await retry(async () => {
      expect(await browser.elementByCss('h1').text()).toBe('Redirect Dest')
    })

    // The