he from 'next/dist/server/lib/incremental-cache/file-system-cache'
import { nodeFs } from 'next/dist/server/lib/node-fs-methods'
import {
  CachedRouteKind,
  IncrementalCacheKind,
} from 'next/dist/server/response-cache'

const cacheDir = fileURLToPath(new URL('./cache', import.meta.url))

describe('FileSystemCache', () => {
  it('set image route', async () => {
    const fsCache = new FileSystemCache({
      _requestHeaders: {},
      flushToDisk: true,
      fs: nodeFs,
      serverDistDir: cacheDir,

s: {
          'Content-Type': 'image/png',
        },
        status: 200,
        kind: CachedRouteKind.APP_ROUTE,
      },
      {}
    )

    expect(
      (
        await fsCache.get('icon.png', {
          kind: IncrementalCacheKind.APP_ROUTE,

   revalidatedTags: [],
    })

    const binary = await fs.readFile(
      fileURLToPath(new URL('./images/icon.png', import.meta.url))
    )

    await fsCache.set(
      'icon.png',
      {
        body: binary,
        header


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

describe.each(['edge', 'nodejs'])(
  'dynamic-css-client-navigation react lazy %s',
  (runtime) => {
    const { next } = nextTestSetup({
      files: __dirname,
    })

    it(`should not remove style when navigating from static imported component to react lazy at runtime ${runtime}`, async () => {
      const browser = await next.browser(`/${runtime}`)

      await browser.elementByCss(`a[href="/${runtime}/react-lazy"]`).click()

      awai

aitForElementByCss('#red-button').text()).toBe(
          'Red Button'
        )

        const buttonBgColor = await browser
          .elementByCss('button')
          .getComputedCss('background-color')

        expect(buttonBgColor).toBe('rgb(255, 0, 0

t retry(async () => {
        expect(await browser.w


port { nextTestSetup } from 'e2e-utils'

describe.each(['edge', 'nodejs'])(
  'dynamic-css-client-navigation dynamic import %s',
  (runtime) => {
    const { next } = nextTestSetup({
      files: __dirname,
    })

    it(`should not remove style when navigating from static imported component to dynamic import at runtime ${runtime}`, async () => {
      const browser = await next.browser(`/${runtime}`)
      expect(
        await browser
          .elementByCss(`a[href="/${runtime}/dynamic-import"]`)

tton')
          .text()
      ).toBe('Red Button')

      const buttonBgColor = await browser.eval(
        `window.getComputedStyle(document.querySelector('button')).backgroundColor`
      )

      expect(buttonBgColor).toBe('rgb(255, 0, 0)')
    })
  }

    .click()
          .waitForElementByCss('#red-bu


 { writeAppTypeDeclarations } from 'next/dist/lib/typescript/writeAppTypeDeclarations'

const fixtureDir = join(__dirname, 'fixtures/app-declarations')
const declarationFile = join(fixtureDir, 'next-env.d.ts')
const imageImportsEnabled = false

describe('find config', () => {
  beforeEach(async () => {
    await fs.ensureDir(fixtureDir)
  })
  afterEach(() => fs.remove(declarationFile))

  it('should preserve CRLF EOL', async () => {
    const eol = '\r\n'
    const content =
      '/// <reference types="ne

ms.d.ts";` +
      eol +
      eol +
      '// NOTE: This file should not be edited' +
      eol +
      '// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.' +
      eol

    await fs.writeFile(declarationFile, conte

xt" />' +
      eol +
      (imageImportsEnabled
        ? '/// <reference types="next/image-types/global" />' + eol
        : '') +
      `import "./.next/types/routes.d.ts";` +
      eol +
      `import "./.next/types/root-para


'path'
import glob from 'glob'
import index from '@next/eslint-plugin-next'

const getRuleNameFromRulePath = (path) => basename(path, '.js')
const rulePaths = glob.sync('packages/eslint-plugin-next/dist/rules/*js', {
  absolute: true,
})

describe('@next/eslint-plugin-next index', () => {
  it('should include all defined rules and no extra / undefined rules', () => {
    const rules = rulePaths.map((rulePath) => getRuleNameFromRulePath(rulePath))

    expect(index.rules).toContainAllKeys(rules)
  })

  it('

fig = index.configs.recommended
    expect(config.name).toBe('next/recommended')
    expect(config.rules).toBeDefined()
  })

  it('should have proper flat config structure for core-web-vitals', () => {
    const config = index.configs['core-web-vitals']

should have meta information', () => {
    expect(index.meta).toBeDefined()
    expect(index.meta.name).toBe('@next/eslint-plugin-next')
  })

  it('should have proper flat config structure for recommended', () => {
    const con


Rule = rules['no-duplicate-head']

const message =
  'Do not include multiple instances of `<Head/>`. See: https://nextjs.org/docs/messages/no-duplicate-head'

const tests = {
  valid: [
    {
      code: `import Document, { Html, Head, Main, NextScript } from 'next/document'

      class MyDocument extends Document {
        static async getInitialProps(ctx) {
          //...
        }

        render() {
          return (
            <Html>
              <Head/>
            </Html>
          )
        }

     render() {
          return (
            <Html>
              <Head>
                <meta charSet="utf-8" />
                <link
                  href="https://fonts.googleapis.com/css2?family=Sarabun:ital,wght@0,400;0,700;1,400;1,700&display=swa

      }

      export default MyDocument
    `,
      filename: 'pages/_document.js',
    },
    {
      code: `import Document, { Html, Head, Main, NextScript } from 'next/document'

      class MyDocument extends Document {


ment
 */

import { userAgentFromString, userAgent, NextRequest } from 'next/server'

const UA_STRING =
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'

it('parse an user agent', () => {
  const parser = userAgentFromString(UA_STRING)
  expect(parser.ua).toBe(
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
  )
  expect(parser.browser).toStrictEqual({
    n

})
  expect(parser.cpu).toStrictEqual({ architecture: undefined })
  expect(parser.isBot).toBe(false)
})

it('parse empty user agent', () => {
  expect.assertions(3)
  for (const input of [undefined, null, '']) {
    expect(userAgentFromString(input)).toSt

ame: 'Chrome',
    version: '89.0.4389.90',
    major: '89',
  })
  expect(parser.engine).toStrictEqual({
    name: 'Blink',
    version: '89.0.4389.90',
  })
  expect(parser.os).toStrictEqual({ name: 'Mac OS', version: '11.2.3'


om 'postcss'

import mod from 'next/dist/compiled/cssnano-simple'
import css from '../noop-template'

describe('accepts plugin configuration', () => {
  test('should not remove all comments', async () => {
    const input = css`
      p {
        /*! heading */
        color: yellow;
      }
    `

    const res = await postcss([mod({ discardComments: {} })]).process(input, {
      from: 'input.css',
      to: 'output.css',
    })

    expect(res.css).not.toBe('p{color:#ff0}')
  })

  test('should remove al

    p {
        /*! heading */
        color: yellow;
      }
    `

    const res = await postcss([
      mod({ discardComments: { removeAll: true } }),
    ]).process(input, {
      from: 'input.css',
      to: 'output.css',
    })

    expect(res.css).t


v jest */
import { extractEtag, getImageEtag } from 'next/dist/server/image-optimizer'
import { readFile } from 'fs-extra'
import { join } from 'path'

describe('extractEtag', () => {
  it('should return base64url encoded etag if etag is provided', () => {
    const etag = 'sample-etag'
    const result = extractEtag(etag, Buffer.from(''))
    expect(result).toEqual('c2FtcGxlLWV0YWc')
  })

  it('should not return weak etag identifier if etag is provided', () => {
    const etag = 'W/"sample-etag"'
    cons

dirname, './images/test.jpg'))
    const res = extractEtag(null, buffer)
    expect(res).toBe(getImageEtag(buffer))
  })

  it('should call getImageEtag and return its result if etag is undefined', async () => {
    const buffer = await readFile(join(__dir

t result = extractEtag(etag, Buffer.from(''))
    expect(result).toEqual('Vy8ic2FtcGxlLWV0YWci')
  })

  it('should call getImageEtag and return its result if etag is null', async () => {
    const buffer = await readFile(join(__


 from 'next/dist/shared/lib/find-closest-quality'

describe('findClosestQuality', () => {
  it.each<{ input: Parameters<typeof findClosestQuality>; output: number }>([
    {
      input: [undefined, undefined],
      output: 75,
    },
    {
      input: [50, undefined],
      output: 50,
    },
    {
      input: [50, { qualities: undefined }],
      output: 50,
    },
    {
      input: [35, { qualities: [10, 30, 50] }],
      output: 30,
    },
    {
      input: [30, { qualities: [10, 30, 50] }],

,
      output: 30,
    },
    {
      input: [40, { qualities: [10, 30, 50] }],
      output: 30, // favor the lower number when halfway
    },
    {
      input: [41, { qualities: [10, 30, 50] }],
      output: 50,
    },
    {
      input: [75, { qualit


ver/image-optimizer'
import {
  CachedRouteKind,
  IncrementalCacheEntry,
} from 'next/dist/server/response-cache/types'
import { readFile } from 'fs-extra'
import { join } from 'path'

const getImageUpstream = async (filepath, contentType = 'image/jpeg') => {
  const buffer = await readFile(join(__dirname, filepath))
  const result: Parameters<typeof getPreviouslyCachedImageOrNull>[0] = {
    buffer,
    contentType,
    cacheControl: 'max-age=31536000',
    etag: getImageEtag(buffer),
  }
  return result

 = async (
  filepath,
  extension = 'jpeg',
  optimizedEtag = true
) => {
  const buffer = await readFile(join(__dirname, filepath))
  const upstreamEtag = getImageEtag(buffer)
  const result: IncrementalCacheEntry = {
    ...baseCacheEntry,
    value: {

}
const baseCacheEntry = {
  revalidateAfter: Date.now() + 1000,
  curRevalidate: Date.now() + 500,
  revalidate: Date.now() + 1000,

  isStale: false,
  isMiss: false,
  isFallback: false,
} as const

const getPreviousCacheEntry


tTestSetup } from 'e2e-utils'

describe.each(['edge', 'nodejs'])(
  'dynamic-css-client-navigation next/dynamic %s',
  (runtime) => {
    const { next } = nextTestSetup({
      files: __dirname,
    })

    it(`should not remove style when navigating from static imported component to next/dynamic at runtime ${runtime}`, async () => {
      const browser = await next.browser(`/${runtime}`)
      expect(
        await browser
          .elementByCss(`a[href="/${runtime}/next-dynamic"]`)
          .click()

      expect(buttonBgColor).toBe('rgb(255, 0, 0)')
    })

    it(`should not remove style when navigating from static imported component to next/dynamic with ssr: false at runtime ${runtime}`, async () => {
      const browser = await next.browser(`/${ru

       .waitForElementByCss('#red-button')
          .text()
      ).toBe('Red Button')

      const buttonBgColor = await browser.eval(
        `window.getComputedStyle(document.querySelector('button')).backgroundColor`
      )


Rule = rules['no-typos']

const tests = {
  valid: [
    `
      export default function Page() {
        return

;
      }
      export const getStaticPaths = async () => {};
      export const getStaticProps = async () => {};
    `,
    `
      export default function Page() {
        return

;
      }
      export const getServerSideProps = async () => {};
    `,
    `
      export default function Page() {
        return

;
      }
      export async function getStaticPaths

hat is one operation away from the correct one
    `
      export default function Page() {
        return

;
      }
      export async function getServerSidePropsss() {};
    `,
    `
      export default function Page() {
        return
<

() {};
      export async function getStaticProps() {};
    `,
    `
      export default function Page() {
        return

;
      }
      export async function getServerSideProps() {};
    `,
    // detect only typo t


} from '@next/eslint-plugin-next'

const NextESLintRule = rules['no-css-tags']

const message =
  'Do not include stylesheets manually. See: https://nextjs.org/docs/messages/no-css-tags'

const tests = {
  valid: [
    `import {Head} from 'next/document';

      export class Blah extends Head {
        render() {
          return (

Hello title

          );
        }
    }`,

    `import {Head} from 'next/document';
      export class Blah extends H

);
        }
    }`,

    `import {Head} from 'next/document';
      export class Blah extends Head {
        render(props) {
          return (

Hello title

              <link {...props} />
            </div

ead {
        render() {
          return (

Hello title

              <link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet" />


', 'http://127.0.0.1:3000')
  expect(parsed.basePath).toEqual('')
  expect(parsed.hostname).toEqual('localhost')
  expect(parsed.host).toEqual('localhost:3000')
  expect(parsed.href).toEqual('http://localhost:3000/about?param1=value1')

  parsed.pathname = '/hihi'
  expect(parsed.href).toEqual('http://localhost:3000/hihi?param1=value1')
})

it('allows to change the host', () => {
  const parsed = new NextURL('/about?param1=value1', 'http://127.0.0.1')
  expect(parsed.hostname).toEqual('localhost')
  expect(

(parsed.href).toEqual('http://foo.com/about?param1=value1')
  expect(parsed.toString()).toEqual('http://foo.com/about?param1=value1')
})

it('does noop changing to an invalid hostname', () => {
  const url = new NextURL('https://foo.com/example')
  url.hos

parsed.host).toEqual('localhost')
  expect(parsed.href).toEqual('http://localhost/about?param1=value1')

  parsed.hostname = 'foo.com'
  expect(parsed.hostname).toEqual('foo.com')
  expect(parsed.host).toEqual('foo.com')
  expect


hould reject a literal private IP hostname with a generic error message', async () => {
      const fetchMock = jest.fn()
      global.fetch = fetchMock

      const error = await fetchExternalImage(
        'http://192.168.0.1/private.jpg',
        false,
        50_000_000
      ).catch((e) => e)

      expect(error).toBeInstanceOf(ImageError)
      expect((error as ImageError).statusCode).toBe(400)
      expect((error as ImageError).message).toBe(
        '"url" parameter is not allowed'
      )
      ex

0,
        body: new ReadableStream({
          start(controller) {
            controller.enqueue(new Uint8Array([1, 2, 3]))
            controller.close()
          },
        }),
        headers: {
          get: jest.fn((header: string) => {

pect(fetchMock).not.toHaveBeenCalled()
    })

    it('should allow a literal private IP when dangerouslyAllowLocalIP is true', async () => {
      global.fetch = jest.fn().mockResolvedValue({
        ok: true,
        status: 20


xample.com:81/path/to/file'))).toBe(true)
    expect(m(p, new URL('https://example.com:81/path/to/file?q=1'))).toBe(true)
    expect(m(p, new URL('http://example.com:81/path/to/file'))).toBe(true)
  })

  it('should match literal protocol and hostname', () => {
    const p = { protocol: 'https', hostname: 'example.com' } as const
    expect(m(p, new URL('https://example.com'))).toBe(true)
    expect(m(p, new URL('https://example.com.uk'))).toBe(false)
    expect(m(p, new URL('https://sub.example.com'))).toB

ttps://example.com/path/to/file'))).toBe(true)
    expect(m(p, new URL('https://example.com:81/path/to/file'))).toBe(true)
    expect(m(p, new URL('https://example.com:81/path/to/file?q=1'))).toBe(true)
    expect(m(p, new URL('http://example.com:81/path/t

e(false)
    expect(m(p, new URL('https://com'))).toBe(false)
    expect(m(p, new URL('https://example.com/path/to'))).toBe(true)
    expect(m(p, new URL('https://example.com/path/to/file'))).toBe(true)
    expect(m(p, new URL('h


 join(__dirname, '_resolvedata', 'with-function')

// force require usage instead of dynamic import in jest
// x-ref: https://github.com/nodejs/node/issues/35889
process.env.__NEXT_TEST_MODE = 'jest'

describe('config', () => {
  let loadConfig: typeof import('next/dist/server/config').default

  beforeEach(async () => {
    // Reset the module cache to ensure each test gets a fresh config load
    // This is important because config.ts now has a module-level configCache
    jest.resetModules()

    // Dyna

t config = await loadConfig(PHASE_DEVELOPMENT_SERVER, pathToConfig)
    expect((config as any).customConfig).toBe(true)
  })

  it('Should pass the phase correctly', async () => {
    const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, pathToConfigFn

mically import the module after reset to get a fresh instance
    const configModule = await import('next/dist/server/config')
    loadConfig = configModule.default
  })
  it('Should get the configuration', async () => {
    cons


} from '../utils'

describe('eslint-config-next/typescript', () => {
  it('should match expected resolved configuration', () => {
    const eslintConfigAfterSetupJSON = execSync(
      // Pass explicit absolute path to not get affected by the root eslint config.
      `pnpm eslint --config ${join(__dirname, 'eslint.config.mjs')} --print-config ${join(__dirname, 'test.tsx')}`,
      {
        cwd: __dirname,
        encoding: 'utf8',
        stdio: ['pipe', 'pipe', 'inherit'],
      }
    )

    const { lang

    expect(getEslintConfigSnapshot(eslintConfigAfterSetup))
      .toMatchInlineSnapshot(`
     {
       "language": "@/js",
       "linterOptions": {
         "reportUnusedDisableDirectives": 1,
       },
       "plugins": [
         "@",
         "@typ

uageOptions, ...eslintConfigAfterSetup } = JSON.parse(
      eslintConfigAfterSetupJSON
    )

    expect({
      parser: languageOptions.parser,
    }).toEqual({
      parser: expect.stringContaining('typescript-eslint'),
    })


  expect(m(p, new URL('https://example.com:81/path/to/file'))).toBe(false)
    expect(m(p, new URL('https://example.com:81/path/to/file?q=1'))).toBe(false)
    expect(m(p, new URL('http://example.com:81/path/to/file'))).toBe(false)
  })

  it('should match literal protocol, hostname, port 42', () => {
    const p = new URL('https://example.com:42/**')
    expect(m(p, new URL('https://example.com:42'))).toBe(true)
    expect(m(p, new URL('https://example.com.uk:42'))).toBe(false)
    expect(m(p, new URL('htt

ile?q=1'))).toBe(false)
    expect(m(p, new URL('http://example.com:42/path/to/file'))).toBe(false)
    expect(m(p, new URL('ftp://example.com:42/path/to/file'))).toBe(false)
    expect(m(p, new URL('https://example.com'))).toBe(false)
    expect(m(p, new

ps://sub.example.com:42'))).toBe(false)
    expect(m(p, new URL('https://com:42'))).toBe(false)
    expect(m(p, new URL('https://example.com:42/path/to/file'))).toBe(true)
    expect(m(p, new URL('https://example.com:42/path/to/f


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

describe('ssg-single-pass', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
  })

  if (skipped) {
    return
  }

  it('should only render the page once during build', async () => {
    expect(next.cliOutput).toIncludeRepeated('home page rendered', 1)
  })

  it('should only render the page once during an ISR revalidation', async () => {
    let logOccurrences = next.cliOutput.split('home page rendered').length

mber).toMatch(/\d+/)
    // grab the index of the last log message so we can start
    // parsing future logs from there
    const outputIndex = next.cliOutput.length

    // trigger a revalidation
    const response = await (await next.fetch('/revalidate'

 - 1
    expect(logOccurrences).toBe(1)
    logOccurrences = 0
    const browser = await next.browser('/')
    const initialRandomNumber = await browser
      .elementById('random-number')
      .text()
    expect(initialRandomNu


tion } from 'next/server'

async function Random({ cached }: { cached?: boolean }) {
  const data = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random'
  ).then((res) => res.text())

  return (
    <>

now: {Date.now()}

        {cached ? 'cached ' : ''}random: {data}

    </>
  )
}

async function CachedRandom() {
  'use cache'

  return <Random cached />
}

export default async function Page() {
  await connection()

  return (
    <>

index page<

<Random />
      </Suspense>
      <Suspense fallback={
Loading...
}>
        <CachedRandom />
      </Suspense>
      <form action={revalidate}>
        <button id="revalidate-tag" type="submit">
          Revalidate tag
        </button>

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


  const { next, skipped } = nextTestSetup({
    files: __dirname,
  })
  if (skipped) return

  it('should only include imported components 3rd party package in browser bundle with direct imports', async () => {
    const $ = await next.render$('/third-party-dep')
    const chunkContents = await Promise.all(
      $('script[src]')
        .toArray()
        .map(async (el) => {
          const href = $(el).attr('src')

          const file = await next.fetch(href).then((res) => res.text())

          return

oBe(false)
    expect(
      chunkContents.every((content) =>
        content.includes('client-dep-default:esm')
      )
    ).toBe(false)
  })

  it('should only include the imported identifier of CJS module in browser bundle', async () => {
    const $ =

 file
        })
    )

    expect(
      chunkContents.some((content) => content.includes('client-dep-bar:esm'))
    ).toBe(true)
    expect(
      chunkContents.every((content) => content.includes('client-dep-foo:esm'))
    ).t


() => {
  // TODO(NAR-423): Migrate to Cache Components.
  describe.skip('with mixed static and dynamic pages and app router routes', () => {
    const { next } = nextTestSetup({
      files: path.join(__dirname, 'fixtures/mixed'),
      skipStart: true,
      env: {
        __NEXT_PRIVATE_DETERMINISTIC_BUILD_OUTPUT: '1',
      },
    })

    beforeAll(() => next.build())

    it('should show info about prerendered and dynamic routes in a tree view', async () => {
      expect(getTreeView(next.cliOutput)).t

che-life-hours           1h      1d
       ├ ƒ /dynamic
       ├   /ppr/[slug]                 1w     30d
       │ ├ ◐ /ppr/[slug]               1w     30d
       │ ├ ◐ /ppr/days                 1d      1w
       │ └ ◐ /ppr/weeks                1w     30d

oMatchInlineSnapshot(`
       "Route (app)             Revalidate  Expire
       ┌ ○ /_not-found
       ├ ƒ /api
       ├ ○ /api/force-static
       ├ ○ /app-static
       ├ ○ /cache-life-custom         ≈7m     ≈2h
       ├ ○ /ca


import {
  nextTestSetupActionTreeShaking,
  getActionsRoutesStateByRuntime,
} from '../_testing/utils'

// TODO: revisit when we have a better side-effect free transform approach for server action
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'actions-tree-shaking - basic',
  () => {
    const { next } = nextTestSetupActionTreeShaking({
      files: __dirname,
    })

    it('should not have the unused action in the manifest', async () => {
      const actionsRoutesState = await getActions

pp/inline/page.js#$$RSC_SERVER_ACTION_0",
         ],
         "app/server/page": [
           "app/actions.js#clientComponentAction",
           "app/actions.js#serverComponentAction",
           "app/actions.js#unusedExportedAction",
         ],
       }

RoutesStateByRuntime(next)
      expect(actionsRoutesState).toMatchInlineSnapshot(`
       {
         "app/client/page": [
           "app/actions.js#clientComponentAction",
         ],
         "app/inline/page": [
           "a


 {
  nextTestSetupActionTreeShaking,
  getActionsRoutesStateByRuntime,
} from '../_testing/utils'

// TODO: revisit when we have a better side-effect free transform approach for server action
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'actions-tree-shaking - mixed-module-actions',
  () => {
    const { next } = nextTestSetupActionTreeShaking({
      files: __dirname,
    })

    it('should not do tree shake for cjs module when import server actions', async () => {
      const actionsRout

       "app/mixed-module/cjs/actions.js#esmModuleTypeAction",
           "app/mixed-module/cjs/actions.js#unusedModuleTypeAction1",
         ],
         "app/mixed-module/esm/page": [
           "app/mixed-module/esm/actions.js#cjsModuleTypeAction",

esState = await getActionsRoutesStateByRuntime(next)
      expect(actionsRoutesState).toMatchInlineSnapshot(`
       {
         "app/mixed-module/cjs/page": [
           "app/mixed-module/cjs/actions.js#cjsModuleTypeAction",


est, retry } from 'next-test-utils'
import { parseRelativeUrl } from 'next/dist/shared/lib/router/utils/parse-relative-url'

function getServerReferenceIdsFromBundle(source: string): string[] {
  // Reference IDs are strings with [0-9a-f] that are at least 32 characters long.
  // We use RegExp to find them in the bundle.
  const referenceIds = source.matchAll(/"([0-9a-f]{32,})"/g) || []
  return [...referenceIds].map(([, id]) => id)
}

describe('app-dir - client-actions-tree-shaking', () => {
  const { nex

 })

  afterEach(async () => {
    logs.length = 0
  })

  /**
   * Parses the client reference manifest for a given route and returns the client chunks
   */
  function getClientChunks(route: string): Array<string> {
    const clientManifest = getClientRe

t } = nextTestSetup({
    files: __dirname,
  })

  const logs: string[] = []

  beforeAll(() => {
    const onLog = (log: string) => {
      logs.push(log.trim())
    }
    next.on('stdout', onLog)
    next.on('stderr', onLog)


extTestSetupActionTreeShaking,
  getActionsRoutesStateByRuntime,
} from '../_testing/utils'

// TODO: revisit when we have a better side-effect free transform approach for server action
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'actions-tree-shaking - shared-module-actions',
  () => {
    const { next } = nextTestSetupActionTreeShaking({
      files: __dirname,
    })

    it('should not have the unused action in the manifest', async () => {
      const actionsRoutesState = await getAct

age": [
           "app/client/actions.js#sharedClientLayerAction",
         ],
         "app/server/one/page": [
           "app/server/actions.js#sharedServerLayerAction",
           "app/server/actions.js#unusedServerLayerAction1",
           "app/serve

ionsRoutesStateByRuntime(next)
      expect(actionsRoutesState).toMatchInlineSnapshot(`
       {
         "app/client/one/page": [
           "app/client/actions.js#sharedClientLayerAction",
         ],
         "app/client/two/p


ionsRoutesStateByRuntime,
} from '../_testing/utils'
import { retry } from 'next-test-utils'

// TODO: revisit when we have a better side-effect free transform approach for server action
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'actions-tree-shaking - reexport',
  () => {
    const { next } = nextTestSetupActionTreeShaking({
      files: __dirname,
    })

    it('should not tree-shake namespace exports the manifest', async () => {
      const actionsRoutesState = await getActionsRoute

 "app/named-reexport/server/page": [
           "app/named-reexport/server/actions.js#sharedServerLayerAction",
           "app/named-reexport/server/actions.js#unusedServerLayerAction1",
           "app/named-reexport/server/actions.js#unusedServerLayerAc

sStateByRuntime(next)

      expect(actionsRoutesState).toMatchInlineSnapshot(`
       {
         "app/named-reexport/client/page": [
           "app/named-reexport/client/actions.js#sharedClientLayerAction",
         ],


2e-utils'

// This is from 'next/dist/build/webpack/plugins/flight-client-entry-plugin', but unfortunately
// Typescript breaks when importing it directly.
type Actions = {
  [actionId: string]: {
    exportedName?: string
    filename?: string
    workers: {
      [name: string]: {
        moduleId: string | number
        async: boolean
      }
    }
  }
}

async function getActionsMappingByRuntime(
  next: NextInstance,
  runtime: 'node' | 'edge'
): Promise<Actions> {
  const manifest = JSON.parse(
    a

TEST_EDGE,
  })

  if (process.env.TEST_EDGE) {
    beforeAll(async () => {
      const layoutContent = await result.next.readFile('app/layout.js')
      await result.next.patchFile(
        'app/layout.js',
        layoutContent + `\nexport const runtime

wait next.readFile('.next/server/server-reference-manifest.json')
  )

  return manifest[runtime]
}

export function nextTestSetupActionTreeShaking(opts) {
  let result = nextTestSetup({
    ...opts,
    skipStart: !!process.env.


t { nextTestSetup } from 'e2e-utils'

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

  it('should contain the routes in prerender manifest', async () => {
    const manifestContent = await next.readFile('.next/prerender-manifest.json')
    const prerenderManifest = JSON.parse(manifestContent)

    expect(
      prerenderManifest.routes['/revalidate/og/opengraph-image']
        .initialRevalidateSeconds
    ).toBe(5)
    expect(
      prerende

es['/manifest.webmanifest'].initialRevalidateSeconds
    ).toBe(5)
    expect(
      prerenderManifest.routes['/robots.txt'].initialRevalidateSeconds
    ).toBe(5)
    expect(
      prerenderManifest.routes['/sitemap.xml'].initialRevalidateSeconds
    ).to


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

describe('metadata spread types', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should allow spreading resolved parent metadata into child metadata', async () => {
    const browser = await next.browser('/')
    const matchMultiDom = createMultiDomMatcher(browser)

    // Verify page title from child's generateMetadata
    expect(await browser.eval(`document.title`)).toBe('Page title')

    // Verify ope

name': 'Example Site',
    })

    // Verify twitter metadata is correctly merged
    await matchMultiDom('meta', 'name', 'content', {
      'twitter:title': 'Page Twitter title',
      'twitter:description': 'Page Twitter description',
      'twitter:site

nGraph metadata is correctly merged
    await matchMultiDom('meta', 'property', 'content', {
      'og:title': 'Page OG title',
      'og:description': 'Page OG description',
      'og:url': 'https://example.com',
      'og:site_


('generate-static-params-errors', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  let cliOutputLength: number

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

  const buildRoute = async (routePath: string) => {
    cliOutputLength = next.cliOutput.length
    await next.build({ args: ['--debug-build-paths', routePath] })
  }

  const getCliOutput = () => next.cliOutput.slice(cliOutputLength)

  it('should error when cookies() is called inside generateStatic

ted because `generateStaticParams` runs at build time without an HTTP request. Read more: https://nextjs.org/docs/messages/next-dynamic-api-wrong-context'
    )
  })

  it('should error when headers() is called inside generateStaticParams', async () => {

Params', async () => {
    await buildRoute('app/[lang]/cookies/[slug]/page.tsx')
    expect(getCliOutput()).toContain(
      'Error: Route /[lang]/cookies/[slug] used `cookies()` inside `generateStaticParams`. This is not suppor


ls'
import { existsSync } from 'fs'
import path from 'path'

describe('post-build', () => {
  if (!isNextStart) {
    it('skipped for non-start mode', () => {})
    return
  }

  const { next, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    env: {
      NEXT_USE_POST_BUILD: '1',
    },
  })

  if (skipped) return

  it('should run post-build compaction on the turbopack cache', async () => {
    if (!isTurbopack) {
      console.log('Skipping: turbopack-only test')

t', 'cache', 'turbopack')
    expect(existsSync(cachePath)).toBe(true)

    // Run `next internal post-build` to compact the database
    const result = await next.runCommand(['internal', 'post-build'])

    if (result.exitCode !== 0) {
      console.log('

   return
    }

    // Build with NEXT_USE_POST_BUILD=1 which skips compaction during build
    const buildResult = await next.build()
    expect(buildResult.exitCode).toBe(0)

    const cachePath = path.join(next.testDir, '.nex


t { ImageResponse } from 'next/og'

// Image metadata
export const alt = 'About Acme'
export const size = {
  width: 1200,
  height: 630,
}

export const contentType = 'image/png'

// Image generation
export default async function Image() {
  return new ImageResponse(
    (
      // ImageResponse JSX element
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',

nt: 'center',
        }}
      >
        About Acme
      </div>
    ),
    // ImageResponse options
    {
      // For convenience, we can re-use the exported opengraph-image
      // size config to also set the ImageResponse's width and height.
      ...


import fs from 'fs'

describe('upload-trace', () => {
  if (!isNextStart) {
    it('skipped for non-start mode', () => {})
    return
  }

  const { next, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    skipDeployment: true,
    buildCommand: 'pnpm next build --experimental-cpu-prof --internal-trace',
  })

  if (skipped) return

  it('should upload profiles and trace to the mock endpoint after build', async () => {
    const buildResult = await next.build()
    expe

les.filter((f: string) =>
      f.endsWith('.cpuprofile')
    )
    expect(cpuProfiles.length).toBeGreaterThan(0)

    if (isTurbopack) {
      expect(allFiles).toContain('trace-turbopack')
    }

    const uploadableFiles = allFiles.filter(
      (f: stri

ct(buildResult.exitCode).toBe(0)

    const profilesDir = path.join(next.testDir, '.next-profiles')
    expect(fs.existsSync(profilesDir)).toBe(true)

    const allFiles = fs.readdirSync(profilesDir)
    const cpuProfiles = allFi


ort { nextTestSetup } from 'e2e-utils'
import path from 'path'
;(process.env.IS_TURBOPACK_TEST ? describe.skip : describe)(
  'app dir - typed-routes-with-webpack-worker',
  () => {
    describe('good-routes', () => {
      const { next } = nextTestSetup({
        files: path.join(__dirname, 'good-routes'),
        skipStart: true,
      })

      it('builds successfully without errors', async () => {
        const { exitCode, cliOutput } = await next.build()

        expect(cliOutput).toContain('webpackBui

oin(__dirname, 'bad-routes'),
        skipStart: true,
      })

      it('builds with valid errors', async () => {
        const { exitCode, cliOutput } = await next.build()

        expect(cliOutput).toContain('webpackBuildWorker')
        expect(exitCod

ldWorker')
        expect(exitCode).toBe(0)
        expect(cliOutput).not.toContain(`"/" is not an existing route.`)
      })
    })

    describe('bad-routes', () => {
      const { next } = nextTestSetup({
        files: path.j


from 'e2e-utils'

describe('use-cache-non-deterministic-args', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should warn about non-deterministic cache args during on-demand prerender', async () => {
    const outputIndex = next.cliOutput.length
    await next.browser('/unknown')

    const output = next.cliOutput.slice(outputIndex)
    expect(output).not.toContain('Connection closed')
    expect(output).toContain(
      'Unexpected cache miss after cache warming phase during p

tchDone = new Promise<void>((resolve) => {
      onRuntimePrefetchDone = resolve
    })

    // Visit /with-runtime-prefetch to render the Link. The link being visible
    // in the viewport triggers a runtime prefetch for the /known route.
    await next.

rerendering'
    )
  })

  it('should warn about non-deterministic cache args during runtime prefetch', async () => {
    const outputIndex = next.cliOutput.length

    let onRuntimePrefetchDone: () => void
    const runtimePrefe


heSignal } from 'react'
import { setTimeout } from 'timers/promises'
import { cookies } from 'next/headers'

export const unstable_prefetch = 'force-runtime'

const callCounts = new Map<string, number>()

function getNonDeterministicItems(slug: string) {
  // For testing purposes, this creates an artificial deterministic scenario
  // that in real-world apps would usually be triggered by a race condition
  // only sometimes (e.g. concurrent async operations pushing results into a
  // shared array in non-de

ces a different cache
  // key that misses the RDC.
  const count = (callCounts.get(slug) ?? 0) + 1
  callCounts.set(slug, count)
  return count === 1 ? ['b', 'a'] : ['a', 'b']
}

async function getCachedValue(items: string[]) {
  'use cache'
  await setTi

terministic order, and that array then being passed
  // as an argument to a cached function).
  //
  // Call 1 (prospective prerender) warms the RDC entry for ['b','a'].
  // Call 2+ (final prerender) uses ['a','b'], which produ


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

  it('should emit partial fallback metadata when infra can upgrade the shell', async () => {
    const { outputs }: Parameters<NextAdapter['onBuildComplete']>[0] =
      await next.readJSON('build-complete.json')

    const withGspPrerender = outputs.prerenders.find(
      (output) => output.pathname === '/with-gsp/[slug]'
    )
    const withGspRouteTreePrerender = outputs.prerenders.find(
      (output) =>
        out

   output.pathname !== '/with-gsp/[slug].segments/_tree.segment.rsc'
    )
    const withoutGspPrerender = outputs.prerenders.find(
      (output) => output.pathname === '/without-gsp/[slug]'
    )
    const withoutGspRouteTreePrerender = outputs.prerender

put.pathname === '/with-gsp/[slug].segments/_tree.segment.rsc'
    )
    const withGspOtherSegmentPrerenders = outputs.prerenders.filter(
      (output) =>
        output.pathname.startsWith('/with-gsp/[slug].segments/') &&


serted-html', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
  })

  if (isNextStart) {
    it('should mark the route as ppr rendered', async () => {
      const prerenderManifest = JSON.parse(
        await next.readFile('.next/prerender-manifest.json')
      )
      expect(prerenderManifest.routes['/partial-resume'].renderingMode).toBe(
        'PARTIALLY_STATIC'
      )
    })
  }

  it('should not log insertion in build', async () => {
    const output = next.cliOutput

:')
  })

  it('should insert the html insertion into html body', async () => {
    const $ = await next.render$('/partial-resume')
    const output = next.cliOutput
    expect(output).toContain('testing-log-insertion:dynamic-data')

    expect($('head [da


('build-output-ssg-bailout', () => {
  if (process.env.__NEXT_CACHE_COMPONENTS === 'true') {
    it.skip('PPR is enabled, will throw instead of bailing out', () => {})
    return
  }

  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    env: {
      __NEXT_PRIVATE_DETERMINISTIC_BUILD_OUTPUT: '1',
      NEXT_DEBUG_BUILD: '1',
    },
  })

  beforeAll(() => next.build())

  // This is available when NEXT_DEBUG_BUILD is set (or --debug flag is used).
  it('should show error message

t(next.cliOutput).toContain(
      'Error: Static generation failed due to dynamic usage on /ssg-bailout/2, reason: `await searchParams`, `searchParams.then`, or similar'
    )
    expect(next.cliOutput).toContain(
      'Error: Static generation failed du

s for SSG bailout', async () => {
    expect(next.cliOutput).toContain(
      'Error: Static generation failed due to dynamic usage on /ssg-bailout/1, reason: `await searchParams`, `searchParams.then`, or similar'
    )
    expec


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

describe('empty-generate-static-params', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  if (skipped) return

  // If we're not using cache components, there shouldn't be any build errors!
  if (process.env.__NEXT_CACHE_COMPONENTS !== 'true') {
    beforeAll(async () => {
      await next.start()
    })

    it('should mark the page with empty generateStaticParams as SSG in build output', async (

by a HIT after being generated', async () => {
      const firstResponse = await next.fetch('/foo')
      expect(firstResponse.status).toBe(200)

      // With PPR enabled, the initial request doesn't send back a cache header
      const isPPREnabled = pro

) => {
      const isPPREnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'
      expect(next.cliOutput).toContain(`${isPPREnabled ? '◐' : '●'} /[slug]`)
    })

    it('should be a cache miss on the initial render followed


from 'e2e-utils'

describe('app edge middleware', () => {
  describe('without node.js modules', () => {
    const { next } = nextTestSetup({
      files: {
        'app/page.js': `
          export default function Page() {
            return
hello world

          }
      `,
        'app/layout.js': `
          export default function Root({ children }) {
            return (

                  {children}

,
    })
    it('should not have any errors about using Node.js modules if not present in middleware', async () => {
      expect(next.cliOutput).not.toContain('node-module-in-edge-runtime')
    })
  })

  describe('with node.js modules', () => {
    const

      )
          }
        `,
        'middleware.js': `
          import { NextResponse } from 'next/server';
          export async function middleware() {
            return NextResponse.next()
          }
      `,
      }


import type { ClientReferenceManifest } from 'next/dist/build/webpack/plugins/flight-manifest-plugin'

describe('route-handler-manifest-size', () => {
  const { next, isNextStart, skipped } = nextTestSetup({
    files: __dirname,
  })

  if (skipped) return

  /**
   * Gets the module paths from clientModules
   */
  function getClientModulePaths(manifest: ClientReferenceManifest): string[] {
    return Object.keys(manifest.clientModules)
  }

  if (isNextStart) {
    it('should not include page client com

tain client components from the page
      const hasButton = modulePaths.some((p) => p.includes('Button'))
      const hasModal = modulePaths.some((p) => p.includes('Modal'))
      const hasDropdown = modulePaths.some((p) => p.includes('Dropdown'))

ponents in pure route handler manifest', () => {
      const manifest = getClientReferenceManifest(next, '/api/hello/route')
      const modulePaths = getClientModulePaths(manifest)

      // The pure route handler should NOT con


export function Dropdown({
  items,
}: {
  items: { label: string; value: string }[]
}) {
  const [isOpen, setIsOpen] = useState(false)
  const [selected, setSelected] = useState<string | null>(null)

  return (

      <button onClick={() => setIsOpen(!isOpen)}>
        {selected || 'Select an option'}
      </button>
      {isOpen && (

          {items.map((item) => (
            <li
              key={item

   setSelected(item.label)
                setIsOpen(false)
              }}
              className="px-4 py-2 hover:bg-gray-100 cursor-pointer"
            >
              {item.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  )


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

describe('app-fetch-deduping', () => {
  const { next, isTurbopack } = nextTestSetup({ files: __dirname })

  const itSkipTurbopack = isTurbopack ? it.skip : it

  // This solution consistently works in Webpack but is flaky in Turbopack.
  // This will require a follow-up
  itSkipTurbopack(
    'should still properly cache fetches when the user has a custom fetch implementation',
    async () => {
      const browser = awa

await browser.refresh()
        currentValue = await browser.elementById('random').text()
        expect(currentValue).toBe(initialRandom)
      })

      // wait for the revalidation period
      await waitFor(3000)

      await retry(async () => {

it next.browser('/')

      let currentValue: string | undefined
      await retry(async () => {
        const initialRandom = await browser.elementById('random').text()
        expect(initialRandom).toMatch(/^0\.\d+$/)


tTestSetup } from 'e2e-utils'

describe('Re-export all exports from page is disallowed', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    dependencies: {
      'find-up': '4.1.0',
    },
  })

  it('shows error when a page re-export all exports', async () => {
    const { cliOutput, exitCode } = await next.build()
    expect(exitCode).toBe(1)
    expect(cliOutput).toContain('pages/contact.js')
    expect(cliOutput).toContain('3:1')
    expect(cliOutput).toContain(

})

  it('builds without error when no `export * from "..."` is used in pages', async () => {
    const origContent = await next.readFile('pages/contact.js')
    const newContent = origContent.replace(/^export \*/gm, '// export *')
    await next.patchFile

    "Using `export * from '...'` in a page is disallowed. Please use `export { default } from '...'` instead."
    )
    expect(cliOutput).toContain(
      'Read more: https://nextjs.org/docs/messages/export-all-in-page'
    )


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

describe('TypeScript basic', () => {
  const { next } = nextTestSetup({
    files: new FileRef(path.join(__dirname, 'app')),
    dependencies: {
      '@next/bundle-analyzer': 'canary',
      typescript: 'latest',
      '@types/node': 'latest',
      '@types/react': 'latest',
      '@types/react-dom': 'latest',
    },
  })

  it('should not have eslint setup started', async () => {
    expec

llo world')
  })

  // Turbopack doesn't support Babel built-in.
  ;(process.env.IS_TURBOPACK_TEST ? it.skip : it)(
    'should work with babel',
    async () => {
      await next.stop()
      await next.patchFile(
        '.babelrc',
        JSON.stringi

t(next.cliOutput).not.toContain(
      'How would you like to configure ESLint'
    )
  })

  it('have built and started correctly', async () => {
    const html = await renderViaHTTP(next.url, '/')
    expect(html).toContain('he


 as childProcess from 'child_process'
import path from 'path'
import { FileRef, nextTestSetup } from 'e2e-utils'

describe('typechecking', () => {
  const { next } = nextTestSetup({
    files: new FileRef(path.join(__dirname, 'typechecking')),
    skipStart: true,
  })

  it('should typecheck', async () => {
    const { status, stdout } = childProcess.spawnSync(
      'pnpm',
      ['tsc', '--project', 'tsconfig.json'],
      {
        cwd: next.testDir,
        encoding: 'utf-8',
      }
    )

    if (sta

 the format barely useable.
      // Printing it as a last resort in case it's not reproducible locally.
      // Best to NEXT_TEST_SKIP_CLEANUP=1 this test and run the command in the app localy.
      throw new Error('Typecheck failed: \n' + stdout)
    }

tus !== 0) {
      // Piped output is incomplete and


rt { useRouter } from 'next/router'
import Link from 'next/link'
import { type PageConfig } from 'next'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'

export const config: PageConfig = {}

export default function Page() {
  // eslint-disable-next-line  @typescript-eslint/no-unused-vars
  const router = useRouter()
  return (
    <>

hello world

      <Link
        href="/another"
        onClick={(e) => {
          c

   to /another
      </Link>
      <Link
        href="/another"
        onClick={(e) => {
          /** @ts-expect-error - foo does not exist on React.MouseEvent */
          console.log(e.foo)
        }}
      >
        to /another
      </Link>
    </>

onsole.log(e.currentTarget)
        }}
      >


te } from 'next'
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
;() => {
  ;({
    background_color: undefined,
    categories: undefined,
    description: undefined,
    dir: undefined,
    display: undefined,
    display_override: undefined,
    file_handlers: undefined,
    icons: undefined,
    id: undefined,
    lang: undefined,
    launch_handler: undefined,
    name: undefined,
    orientation: undefined,
    prefer_related_applications: undefined,
    protocol_handlers: undefin

}) satisfies MetadataRoute.Manifest
  ;({
    icons: [
      {
        src: '',
        type: undefined,
        sizes: undefined,
        purpose: undefined,
      },
    ],
    related_applications: [
      {
        platform: '',
        url: '',

ed,
    related_applications: undefined,
    scope: undefined,
    screenshots: undefined,
    share_target: undefined,
    short_name: undefined,
    shortcuts: undefined,
    start_url: undefined,
    theme_color: undefined,


incremental-cache/file-system-cache')

/**
 * A FileSystemCache variant that lets a test simulate the passage of time by
 * shifting the `lastModified` timestamp of a cached entry into the past. The
 * offset is controlled per-request via the `x-test-cache-age-offset-ms` header
 * so tests can skip over `expire` windows (e.g. 5 minutes) without actually
 * waiting.
 */
module.exports = class IncrementalCacheHandler extends FileSystemCache {
  constructor(ctx) {
    super(ctx)
    this.requestHeaders = ctx._

t result = await super.get(key, ctx)

    const offsetHeader = this.requestHeaders?.['x-test-cache-age-offset-ms']
    const offsetMs = typeof offsetHeader === 'string' ? Number(offsetHeader) : 0

    if (result && offsetMs > 0 && !ctx.fetchCache) {

requestHeaders
  }

  async get(key, ctx) {
    cons


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

async function getValue() {
  'use cache'
  cacheLife({ revalidate: 60, expire: 300 })
  return new Date().toISOString()
}

export function generateStaticParams() {
  return [{ id: 'known' }]
}

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  return (
    <>

{await getValue()}

      <Suspense fallback={
loading
}>
        <DynamicPar

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

async function DynamicPart({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  await connection()
  return (
    <p id="dynamic">
      {id} - {new Date().toISOString()}
    </p>
  )
}


 { nextTestSetup, type NextInstance } from 'e2e-utils'

async function getServerActionManifestNodeKeys(next: NextInstance) {
  const manifest = await next.readJSON(
    '.next/server/server-reference-manifest.json'
  )
  return Object.keys(manifest.node)
}

describe('app-dir - server-action-period-hash', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  it('should have same manifest between continuous two builds', async () => {
    await next.build()
    const fir

e different manifest between two builds with period hash', async () => {
    await next.build()
    const firstActionIds = await getServerActionManifestNodeKeys(next)

    await next.remove('.next') // dismiss cache
    await next.build()

    const second

stActionIds = await getServerActionManifestNodeKeys(next)

    await next.build()
    const secondActionIds = await getServerActionManifestNodeKeys(next)

    expect(firstActionIds).toEqual(secondActionIds)
  })

  it('should hav


 nextTestSetup } from 'e2e-utils'
import { generatePNG } from '../generate-image'

describe('app-dir - metadata-img-too-large twitter-image', () => {
  const { next, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  const pngFile = generatePNG(5)

  it('should throw when twitter-image file size exceeds 5MB', async () => {
    await next.patchFile('app/twitter-image.png', pngFile as any)
    await next.build()

    const regex = isTurbopack
      ? // in Turbopack, the path i

ier, so the prefix is slightly different.
        /File size for Twitter image "\[project\]\/app\/twitter-image\.png" exceeds 5MB/
      : /Error: File size for Twitter image ".*\/app\/twitter-image\.png" exceeds 5MB/

    expect(next.cliOutput).toMatch(re

s simplified as [project]/.... It's also thrown earl


t path from 'path'
import { nextTestSetup } from 'e2e-utils'

function collectJsFiles(dir: string): string[] {
  if (!fs.existsSync(dir)) return []
  return fs
    .readdirSync(dir, { recursive: true, encoding: 'utf8' })
    .flatMap((entry) => {
      const full = path.join(dir, entry)
      return entry.endsWith('.js') && fs.statSync(full).isFile() ? [full] : []
    })
}

describe('turbopack-chunk-loading-global', () => {
  const { next, isNextDeploy, isTurbopack } = nextTestSetup({
    files: __dirname,

next.testDir, '.next/static')
        const jsFiles = collectJsFiles(staticDir)
        expect(jsFiles.length).toBeGreaterThan(0)

        const allContent = jsFiles
          .map((f) => fs.readFileSync(f, 'utf8'))
          .join('\n')

        // Custom

  })

  ;(isNextDeploy || !isTurbopack ? describe.skip : describe)(
    'chunk output',
    () => {
      it('uses the custom global name and drops the default TURBOPACK global', async () => {
        const staticDir = path.join(


join } from 'path'

describe('NEXT_HASH_SALT', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  /** Build with the given salt and return { chunks, images, css } filename lists. */
  async function buildWithSalt(salt: string) {
    await next.clean()
    await next.build({ env: { NEXT_HASH_SALT: salt } })
    const files = await listClientChunks(join(next.testDir, next.distDir))
    const chunks = files.filter(
      (f) => f.includes('/chunks/') && f.endsWith('.j

salt-b.
  let saltAFirst: Awaited<ReturnType<typeof buildWithSalt>>
  let saltASecond: Awaited<ReturnType<typeof buildWithSalt>>
  let saltB: Awaited<ReturnType<typeof buildWithSalt>>

  beforeAll(
    async () => {
      saltAFirst = await buildWithSalt('

s')
    )
    const images = files.filter((f) => f.endsWith('.png'))
    const css = files.filter((f) => f.endsWith('.css'))
    return { chunks, images, css }
  }

  // Three builds: salt-a (twice for reproducibility check) and


ypto from 'crypto'

function generateMD5(text: string) {
  const hash = crypto.createHash('md5')
  hash.update(text)
  return hash.digest('hex')
}

describe('app dir - metadata static routes cache', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  it('should generate different content after replace the static metadata file', async () => {
    await next.start()

    const $ = await next.render$('/')
    const faviconUrl = $('link[rel="icon"]').attr('href')
    co

 await next.fetch(opengraphImageUrl)
    ).text()
    const opengraphImageMd5 = generateMD5(opengraphImageBody)

    await next.stop()

    // Update favicon and opengraph image
    const newFaviconContent = await next.readFileBuffer('app/favicon.new.ico')

nst faviconBody = await (await next.fetch(faviconUrl)).text()
    const faviconMd5 = generateMD5(faviconBody)

    const opengraphImageUrl = $('meta[property="og:image"]').attr('href')
    const opengraphImageBody = await (


oDateRegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/

describe.each(['NEXT_DEPLOYMENT_ID', 'BUILD_ID', 'default'])(
  'use-cache-cross-deployment with %s',
  (envKey) => {
    const { next, skipped } = nextTestSetup({
      files: __dirname,
      disableAutoSkewProtection: true,
      skipStart: true,
    })

    if (skipped) return

    // In the future, this assertion can be relaxed to only prevent sharing if the implementation
    // changed.
    it('should not have the same cache key across de

 keyRoot: string, keyPrerender: string

          {
            let match = next.cliOutput.match(
              /CustomCacheHandler::get \["([A-Za-z0-9_-]+)","([0-9a-f]{2})+",\[\{"id":"dynamic-cache"\}\]\] \[\["_N_T_\/layout","_N_T_\/prerender\/layout","_N

ployments', async () => {
      async function execute(id: string) {
        await next.stop()
        if (envKey !== 'default') {
          next.env[envKey] = id
        }
        try {
          await next.start()
          let


 "▲ Next.js x.y.z (Turbopack)
             - Cache Components enabled
             - Experiments (use with caution):
               ✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
               ✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
            `)
          } else if (isRspack) {
            expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
             "▲ Next.js x.y.z (Rspack)
             - Cache Components enabled

S\`)"
            `)
          } else {
            expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
             "▲ Next.js x.y.z (webpack)
             - Cache Components enabled
             - Experiments (use with caution):


e of graceful-degrade-error-bot.test.ts since we need to
// restart with a new browser context for UA setting. Otherwise the browser context
// will not be closed and reset properly. TODO: investigate why browser.close didn't help.

import { nextTestSetup } from 'e2e-utils'
import { deleteBrowserDynamicChunks } from './delete-dynamic-chunk'

describe('graceful-degrade - non bot', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  // Delete client chunks to simulate chunk loading failur

r('/chunk-loading-failed')

    const logs = await browser.log()
    const errors = logs
      .filter((x) => x.source === 'error')
      .map((x) => x.message)
      .join('\n')

    expect(errors).toMatch(/Failed to load resource./)
    // Should not sho

e
  beforeAll(async () => {
    await deleteBrowserDynamicChunks(next)
  })

  it('should not degrade to graceful error when chunk loading fails in ssr for non-bot user agents', async () => {
    const browser = await next.browse


from 'e2e-utils'
import { deleteBrowserDynamicChunks } from './delete-dynamic-chunk'

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

  // Delete client chunks to simulate chunk loading failure
  beforeAll(async () => {
    await deleteBrowserDynamicChunks(next)
  })

  it('should degrade to graceful error when chunk loading fails in ssr for bot', async () => {
    const browser = await next.browser('/chunk-loading-failed', {
      userAgent: 'Googlebot',

show the original content
    const originHtml = await browser.elementByCss('html')
    const originBody = await browser.elementByCss('body')
    expect(await originHtml.getAttribute('class')).toBe('layout-cls')
    expect(await originBody.getAttribute('cl

    })

    const logs = await browser.log()
    const errors = logs
      .filter((x) => x.source === 'error')
      .map((x) => x.message)
      .join('\n')

    expect(errors).toMatch(/Failed to load resource./)
    // Should


      })

      function fetchWithPolicy(policy: string | null, reportOnly?: boolean) {
        const cspKey = reportOnly
          ? 'Content-Security-Policy-Report-Only'
          : 'Content-Security-Policy'
        return next.fetch(`/${runtime}`, {
          headers: policy
            ? {
                [cspKey]: policy,
              }
            : {},
        })
      }

      async function renderWithPolicy(
        policy: string | null,
        reportOnly?: boolean
      ) {
        const res =

      const policies = [
          `script-src 'nonce-'`, // invalid nonce
          'style-src "nonce-cmFuZG9tCg=="', // no script or default src
          `script-src 'nonce-" onerror="alert(1)'`, // malformed nonce
          '', // empty string

 await fetchWithPolicy(policy, reportOnly)

        expect(res.ok).toBe(true)

        const html = await res.text()

        return cheerio.load(html)
      }

      it('does not include nonce when not enabled', async () => {


from 'e2e-utils'

// TODO: the incremental option has been removed, update to use cacheComponents
describe.skip('app-dir - metadata-streaming-config', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should have the default streaming metadata config output in routes-manifest.json', async () => {
    const requiredServerFiles = JSON.parse(
      await next.readFile('.next/required-server-files.json')
    )

    expect(requiredServerFiles.config.htmlLimitedBots).toMatchInlineSnapsh

log|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight"`
    )

    const prerenderManifest = JSON.parse(
      await next.readFile('.next/prerender-manifest.json')
    )
    const { routes } = prerenderManifest

    co

ot(
      `"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcata


from 'e2e-utils'

describe('worker-restart', () => {
  describe('timeout', () => {
    const { next } = nextTestSetup({
      files: __dirname + '/fixtures/timeout',
      skipStart: true,
    })

    it('should properly exhaust all restart attempts and not fail with any worker errors', async () => {
      const { cliOutput } = await next.build()
      expect(cliOutput).toContain(
        'Failed to build /bad-page/page: /bad-page (attempt 1 of 3) because it took more than 10 seconds. Retrying again shortly

Failed to build /bad-page/page: /bad-page after 3 attempts'
      )
      expect(cliOutput).not.toContain(
        'Error: Farm is ended, no more calls can be done to it'
      )
    })
  })

  describe('retries', () => {
    const { next } = nextTestSetup

.'
      )
      expect(cliOutput).toContain(
        'Failed to build /bad-page/page: /bad-page (attempt 2 of 3) because it took more than 10 seconds. Retrying again shortly.'
      )
      expect(cliOutput).toContain(
        '


etup } from 'e2e-utils'
import {
  fetchViaHTTP,
  findPort,
  initNextServerScript,
  killApp,
  retry,
} from 'next-test-utils'

describe('global-default-cache-handler', () => {
  let appPort: number
  let server: any
  let output = ''

  const { next } = nextTestSetup({
    files: new FileRef(__dirname),
    skipStart: true,
  })

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

    const standaloneServer = '.next/standalone/server.js'
    await next.patchFile(
      standaloneServer,
      `
      glob

     console.log('symbol set', cacheKey)
          },

          refreshTags() {
            console.log('symbol refreshTags')
          },

          getExpiration(...tags) {
            console.log('symbol getExpiration', tags)
          },

alThis[Symbol.for('@next/cache-handlers')] = {
        DefaultCache: {
          get(cacheKey, softTags) {
            console.log('symbol get', cacheKey, softTags)
          },

          set(cacheKey, entry) {


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

describe('empty-shell-route-cache', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

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

  async function getRouteArtifactMtimes(route: string) {
    const routeMeta = JSON.parse(
      await next.readFile(`.next/server/app/${route}.meta`)
    )
    const files = [
      `${route}.html`,
      `${route}.meta`,
      ...routeMeta.segmentPaths.map(
        (segmentPath: st

estDir, '.next/server/app', file)
          )

          return [file, stat.mtimeMs]
        })
      )
    )
  }

  it('should emit both routes as partially static build artifacts', async () => {
    const prerenderManifest = JSON.parse(
      await next.

ring) => `${route}.segments${segmentPath}.segment.rsc`
      ),
    ]

    return Object.fromEntries(
      await Promise.all(
        files.map(async (file) => {
          const stat = await fs.stat(
            path.join(next.t


ype NextInstance } from 'e2e-utils'

async function getServerActionManifestNodeKeys(next: NextInstance) {
  const manifest = await next.readJSON(
    '.next/server/server-reference-manifest.json'
  )
  return Object.keys(manifest.node)
}

describe('app-dir - server-action-period-hash-custom-key', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  it('should have a different manifest if the encryption key from process env is changed', async () => {
    process.env.N

RVER_ACTIONS_ENCRYPTION_KEY = 'my-secret-key2'
    await next.build()
    delete process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
    const secondActionIds = await getServerActionManifestNodeKeys(next)

    expect(firstActionIds).not.toEqual(secondActionIds)

EXT_SERVER_ACTIONS_ENCRYPTION_KEY = 'my-secret-key1'
    await next.build()
    delete process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
    const firstActionIds = await getServerActionManifestNodeKeys(next)

    process.env.NEXT_SE


er.from([137, 80, 78, 71, 13, 10, 26, 10])

function createChunk(type, data) {
  const length = Buffer.alloc(4)
  length.writeUInt32BE(data.length, 0)
  const crc = Buffer.alloc(4)
  const crcValue = calculateCRC(Buffer.concat([Buffer.from(type), data])) >>> 0 // Ensure unsigned 32-bit integer
  crc.writeUInt32BE(crcValue, 0)
  return Buffer.concat([length, Buffer.from(type), data, crc])
}

function calculateCRC(data) {
  let crc = 0xffffffff
  for (const b of data) {
    crc ^= b
    for (let i = 0; i < 8;

= 1024
  let pngFile: Buffer

  do {
    const ihdrData = Buffer.alloc(13)
    ihdrData.writeUInt32BE(width, 0)
    ihdrData.writeUInt32BE(height, 4)
    ihdrData.writeUInt8(8, 8) // bitDepth
    ihdrData.writeUInt8(6, 9) // colorType
    ihdrData.writeUIn

 i++) {
      crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0)
    }
  }
  return crc ^ 0xffffffff
}

export function generatePNG(targetSizeMB) {
  const targetSizeBytes = targetSizeMB * 1024 * 1024

  let width = 2048,
    height


rt { nextTestSetup } from 'e2e-utils'

describe('reference-tree-shaking', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('Should apply removeUnusedImports tree shaking to client and server references', async () => {
    const res = await next.fetch('/')
    expect(await res.text()).toContain('This is Server')

    // Only Turbopack applies inner graph tree shaking here
    if (process.env.IS_TURBOPACK_TEST) {
      const serverReferences = await next.readFile(
        '.next/ser

pect(serverReferences).not.toContain('library/action.js')

      const clientReferences = await next.readFile(
        '.next/server/app/page_client-reference-manifest.js'
      )
      expect(clientReferences).not.toContain('library/client.js')
    }
  })

ver/server-reference-manifest.json'
      )
      ex


 strictRouteTypes =
  process.env.__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES === 'true'

describe('types-gen-nounuselocal', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
  })

  if (skipped) {
    return
  }

  it('should build successfully', async () => {
    const $ = await next.render$('/')
    expect($('p').text()).toBe('hello world')
  })

  it('should generate route types successfully', async () => {
    const routeTypes = await fsp.readFile(
      path.join(next.testDir, '.nex

f (strictRouteTypes) {
      const cacheLifeTypes = await fsp.readFile(
        path.join(next.testDir, '.next', 'types', 'cache-life.d.ts'),
        'utf-8'
      )
      expect(cacheLifeTypes).toContain("declare module 'next/cache'")

      expect(routeT

t', 'types', 'routes.d.ts'),
      'utf-8'
    )

    expect(routeTypes).not.toContain('AppRouteHandlerRoutes')
    expect(routeTypes).not.toContain('PagesPageConfig')
    expect(routeTypes).not.toContain('ApiRouteConfig')

    i


import path from 'path'
import execa from 'execa'

const appDir = path.join(__dirname, 'app')

describe('next/jest image qualities config', () => {
  const { next } = nextTestSetup({
    skipStart: true,
    files: {
      'next.config.js': `
module.exports = {
  images: {
    qualities: [90, 100],
  },
}
        `,
      app: new FileRef(path.join(appDir, 'app')),
      'jest.config.js': `
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {

age quality config', () => {
  it('respects custom qualities from next.config.js', () => {
    render(
      <Image
        src="/test.jpg"
        alt="test"
        width={500}
        height={500}
        quality={100}
      />
    )

    const img = sc

  testEnvironment: 'jsdom',
}

module.exports = createJestConfig(customJestConfig)
        `,
      [`tests/image.test.tsx`]: `
import Image from 'next/image'
import { render, screen } from '@testing-library/react'

describe('Im


import { nextTestSetup } from 'e2e-utils'

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

  it('should static generate parallel routes', async () => {
    const rscExtension = '.rsc'
    expect(await next.hasFile('.next/server/app/nested/foo.html')).toBe(true)
    expect(await next.hasFile('.next/server/app/nested/foo.meta')).toBe(true)

    // we do not output '.rsc' statically with cache components
    if (!process.env.__NEXT_CACHE_COMPONE

.next/server/app/nested/bar.meta')).toBe(true)

    // we do not output '.rsc' statically with cache components
    if (!process.env.__NEXT_CACHE_COMPONENTS) {
      expect(
        await next.hasFile(`.next/server/app/nested/bar${rscExtension}`)
      ).t

NTS) {
      expect(
        await next.hasFile(`.next/server/app/nested/foo${rscExtension}`)
      ).toBe(true)
    }

    expect(await next.hasFile('.next/server/app/nested/bar.html')).toBe(true)
    expect(await next.hasFile('


outdent'

describe('production - app dir - build output', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  let output = ''
  beforeAll(() => {
    output = stripAnsi(next.cliOutput)
  })

  it('should only log app routes', async () => {
    expect(output).toContain('Route (app)')
    expect(output).not.toContain('Route (pages)')
    expect(output).not.toContain('/favicon.ico')
  })

  it('should always log version first then the rest jobs', async () => {
    const indexOfVersion = ou

han(indexOfLinting)
    expect(indexOfStartCompiling).toBeLessThan(indexOfLinting)
  })

  it('should match the expected output format', async () => {
    // output type
    expect(output).toContain('○  (Static)  prerendered as static content')
  })

  it(

tput.indexOf('▲ Next.js')
    const indexOfStartCompiling = output.indexOf(
      'Creating an optimized production build'
    )
    const indexOfLinting = output.indexOf('Running TypeScript')
    expect(indexOfVersion).toBeLessT


 under `test/production` so it only runs in `next start` mode. In dev
// mode the default in-memory `'use cache'` handler would return the cached
// function value on re-render (we don't fake time for the function-level
// cache), so the observable wouldn't change even when the ISR layer does a
// blocking revalidation. Deploy mode is also out of scope: on Vercel the ISR
// cache decision is made at the Proxy layer before the lambda runs, and
// Proxy-side behavior is covered by Proxy's own tests. The compa

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

  async function expectBlockingRevalidation(path: string) {
    const $first = await next.render$(path)
    const v0 = $first('#value').text()
    expect(v0).toBeTruthy()

    co

nion suite
// `test/e2e/app-dir/expire-time` covers the same `IncrementalCache` /
// response-cache change via classic ISR (no `use cache`, no custom handler,
// short `expireTime`) and runs in deploy mode.
describe('use-cache-ex


 { nextTestSetup } from 'e2e-utils'

// TODO: the incremental option has been removed, update to use cacheComponents
describe.skip('app-dir - metadata-streaming-config-customized', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    overrideFiles: {
      'next.config.js': `
        module.exports = {
          htmlLimitedBots: /MyBot/i,
            experimental: {
            ppr: 'incremental',
          }
        }
      `,
    },
  })

  it('should have the customized streaming metadata

 Object.keys(routes)
      .map((route) => [route, routes[route].experimentalBypassFor?.[2]])
      .filter(([, bypassConfig]) => Boolean(bypassConfig))
      .reduce((acc, [route, bypassConfig]) => {
        acc[route] = bypassConfig
        return acc

 config output in routes-manifest.json', async () => {
    const prerenderManifest = JSON.parse(
      await next.readFile('.next/prerender-manifest.json')
    )
    const { routes } = prerenderManifest

    const bypassConfigs =


s'
import { listClientChunks } from 'next-test-utils'
import path from 'path'

export async function deleteBrowserDynamicChunks(next) {
  const distDir = path.join(next.testDir, '.next')
  const clientChunkFiles = (await listClientChunks(distDir))
    .map((file) => path.join(distDir, file))
    // filter out the js file that contains the text "large test content"
    .filter((filePath) => {
      const isJsFile = filePath.endsWith('.js')
      const fileContent = isJsFile
        ? fs.readFileSync(filePath

        isJsFile && fileContent && fileContent.includes('large test content')
      )
    })

  // Intended to log to help debugging tests
  console.log('Deleting client chunk files:', clientChunkFiles)
  // delete all chunk files
  clientChunkFiles.map((

, { encoding: 'utf8' })
        : ''

      return (


from 'e2e-utils'

describe('no-mangling', () => {
  describe('with the default `next build`', () => {
    const { next } = nextTestSetup({
      files: __dirname,
      skipStart: true,
    })

    it('should show mangled function names in stack traces', async () => {
      try {
        await next.build()
      } catch {
        // we expect the build to fail
      }

      expect(next.cliOutput).toInclude(`
Error: Kaputt!
    at `) // mangled function name omitted because it's not deterministic

      //

build --no-mangling`', () => {
    const { next } = nextTestSetup({
      files: __dirname,
      buildArgs: ['--no-mangling'],
      skipStart: true,
    })

    it('should show original function names in stack traces', async () => {
      try {
        a

`Page` is the original function name that is mangled because `next
      // build` was called without `--no-mangling`.
      expect(next.cliOutput).not.toInclude(`
Error: Kaputt!
    at Page`)
    })
  })

  describe('with `next


Setup } from 'e2e-utils'
import { generatePNG } from '../generate-image'

describe('app-dir - metadata-img-too-large opengraph-image', () => {
  const { next, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  const pngFile = generatePNG(8)

  it('should throw when opengraph-image file size exceeds 8MB', async () => {
    await next.patchFile('app/opengraph-image.png', pngFile as any)
    await next.build()

    const regex = isTurbopack
      ? // in Turbopack, the path is s

, so the prefix is slightly different.
        /File size for Open Graph image "\[project\]\/app\/opengraph-image\.png" exceeds 8MB/
      : /Error: File size for Open Graph image ".*\/app\/opengraph-image\.png" exceeds 8MB/

    expect(next.cliOutput).toM

implified as [project]/.... It's also thrown earlier


lib/cache-handlers/default.external').default

/**
 * @type {import('next/dist/server/lib/cache-handlers/types').CacheHandler}
 */
const cacheHandler = {
  async get(cacheKey, softTags) {
    console.log('CustomCacheHandler::get', cacheKey, JSON.stringify([softTags]))
    return defaultCacheHandler.get(cacheKey, softTags)
  },

  async set(cacheKey, pendingEntry) {
    console.log('CustomCacheHandler::set', cacheKey)
    return defaultCacheHandler.set(cacheKey, pendingEntry)
  },

  async refreshTags() {

sync getExpiration(tags) {
    // Expecting soft tags in `get` to be used by the cache handler for checking
    // the expiration of a cache entry, instead of letting Next.js handle it.
    return Infinity
  },

  async updateTags(tags) {
    return defaul

  return defaultCacheHandler.refreshTags()
  },

  a


mises'
import { listClientChunks } from 'next-test-utils'

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

  let sources: string[] = []
  let jsContents: string[] = []
  beforeAll(async () => {
    const chunksDir = join(next.testDir, '.next')

    const chunks = await listClientChunks(chunksDir)

    const sourcemaps = await Promise.all(
      chunks
        .filter((filename) => filename.endsWith('.js.map'))
        .map((f) => readFile(join(chunksDir, f),

(join(chunksDir, f), 'utf8'))
    )
  })

  it('must not bundle any server modules into browser chunks', () => {
    const serverSources = sources.filter(
      (source) =>
        /webpack:\/\/_N_E\/(\.\.\/)*src\/server\//.test(source) ||
        source.i

 'utf8'))
    )
    sources = sourcemaps.flatMap((sourcemap) => JSON.parse(sourcemap).sources)

    jsContents = await Promise.all(
      chunks
        .filter((filename) => filename.endsWith('.js'))
        .map((f) => readFile


onfiguration', () => {
  let nextTgzFilename: string

  beforeAll(() => {
    nextTgzFilename = resolveNextTgzFilename()
  })

  it('should match biome.json snapshot', async () => {
    await useTempDir(async (cwd) => {
      const projectName = 'test-biome-snapshot'
      const { exitCode } = await run(
        [
          projectName,
          '--ts',
          '--biome',
          '--no-tailwind',
          '--no-src-dir',
          '--app',
          '--no-import-alias',
          '--no-react-compiler'

t readFile(join(projectDir, 'biome.json'), 'utf8')

      expect(biomeConfig).toMatchSnapshot()
    })
  })

  it('should run biome check successfully on generated TypeScript project', async () => {
    await useTempDir(async (cwd) => {
      const project

,
          '--no-agents-md',
          '--skip-install',
        ],
        nextTgzFilename,
        { cwd }
      )

      expect(exitCode).toBe(0)

      const projectDir = join(cwd, projectName)
      const biomeConfig = awai


=> {
  let nextTgzFilename: string

  beforeAll(() => {
    nextTgzFilename = resolveNextTgzFilename()
  })

  it('should create JavaScript project with --js flag', async () => {
    await useTempDir(async (cwd) => {
      const projectName = 'pages-js'
      const { exitCode } = await run(
        [
          projectName,
          '--js',
          '--no-app',
          '--eslint',
          '--no-src-dir',
          '--no-tailwind',
          '--no-import-alias',
          '--no-react-compiler',

 cwd,
        projectName,
        template: 'default',
        mode: 'js',
      })
      await tryNextDev({
        cwd,
        projectName,
        isApp: false,
      })
    })
  })

  it('should create TypeScript project with --ts flag', async () =>

  '--no-agents-md',
          ...(process.env.NEXT_RSPACK ? ['--rspack'] : []),
        ],
        nextTgzFilename,
        {
          cwd,
        }
      )
      expect(exitCode).toBe(0)
      shouldBeTemplateProject({


ort path from 'path'
import { FileRef, nextTestSetup } from 'e2e-utils'
import { renderViaHTTP } from 'next-test-utils'

const files = {
  'app/layout.jsx': `
    export default function AppLayout({ children }) {
      return (
        <html>
          <head>
            <title>WASM Import</title>
          </head>
          <body>
            {children}
          </body>
        </html>
      )
    }
  `,
  'app/page.jsx': `
    import wasm from '../wasm/add.wasm?module'
    const instance$ = WebAssembly.i

1 is: $\{two}\`
    }

    export const runtime = "edge"
  `,
  'wasm/add.wasm': new FileRef(path.join(__dirname, 'add.wasm')),
}

describe('app-dir edge runtime with wasm', () => {
  const { next } = nextTestSetup({
    files,
  })

  it('should have buil

nstantiate(wasm);

    async function addOne(a) {
      const { exports } = await instance$;
      return exports.add_one(a);
    }

    export default async function Page() {
      const two = await addOne(1)
      return \`1 +


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

describe('Image Component No IntersectionObserver test', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  describe('SSR Lazy Loading Tests', () => {
    it('should automatically load images if observer does not exist', async () => {
      const browser = await next.browser('/no-observer')

      await retry(async () => {
        const result = await browser.eval('IntersectionObserver')
        expect(result).toBeNull()
      }

entById('lazy-no-observer').getAttribute('srcset')
      ).toBe(
        'https://example.com/myaccount/foox.jpg?auto=format&fit=max&w=1024 1x, https://example.com/myaccount/foox.jpg?auto=format&fit=max&w=2000 2x'
      )
    })
  })

  describe('Client-si

)

      expect(
        await browser.elementById('lazy-no-observer').getAttribute('src')
      ).toBe(
        'https://example.com/myaccount/foox.jpg?auto=format&fit=max&w=2000'
      )
      expect(
        await browser.elem


s = await browser.elementsByCss('link[rel=preload][as=image]')
    for (const link of links) {
      const imagesrcset = await link.getAttribute('imagesrcset')
      const href = await link.getAttribute('href')
      if (imagesrcset?.includes(url) || (!imagesrcset && href === url)) {
        return true
      }
    }
    return false
  }

  async function hasImagePreloadBeforeCSSPreload(browser) {
    const links = await browser.elementsByCss('link')
    let foundImage = false
    for (const link of links)

tyle' && foundImage) {
          return true
        }
      }
    }
    return false
  }

  function runTests(browser: () => Browser) {
    it('should render an image tag', async () => {
      expect(await browser().hasElementByCssSelector('img')).toBeTru

{
      const rel = await link.getAttribute('rel')
      if (rel === 'preload') {
        const linkAs = await link.getAttribute('as')
        if (linkAs === 'image') {
          foundImage = true
        } else if (linkAs === 's


egacy/image'
import Link from 'next/link'

const ClientSide = () => {
  return (

This is a client side page

      <Image
        id="basic-image"
        src="foo.jpg"
        loading="eager"
        width={300}
        height={400}
        quality={60}
      ></Image>
      <Image
        id="attribute-test"
        data-demo="demo-value"
        src="bar.jpg"
        loading="eager"
        width={300}
        height={400}
      />
      <Image
        id="secondary-im

 src="https://arbitraryurl.com/foo.jpg"
        loading="eager"
        width={300}
        height={400}
      />
      <Image
        id="priority-image-client"
        priority
        src="withpriorityclient.png"
        width={300}
        height={400}

age"
        data-demo="demo-value"
        host="secondary"
        src="foo2.jpg"
        loading="eager"
        width={300}
        height={400}
      />
      <Image
        id="unoptimized-image"
        unoptimized


egacy/image'
import Link from 'next/link'
import Head from 'next/head'

const Page = () => {
  return (

Hello World

      <Image
        id="image-with-sizes"
        src="/test-sizes.jpg"
        width={2000}
        height={100}
        sizes="100vw"
      />
      <Image
        id="basic-image"
        src="foo.jpg"
        loading="eager"
        width={300}
        height={400}
        quality={60}
      ></Image>
      <Image
        id="attribute-test"
        data-demo="demo-

    loading="eager"
        width={300}
        height={400}
      />
      <Image
        id="unoptimized-image"
        unoptimized
        src="https://arbitraryurl.com/foo.jpg"
        loading="eager"
        width={300}
        height={400}
      />

value"
        src="bar.jpg"
        loading="eager"
        width={1024}
        height={400}
      />
      <Image
        id="secondary-image"
        data-demo="demo-value"
        host="secondary"
        src="foo2.jpg"


from 'react'
import Image from 'next/legacy/image'

const Lazy = () => {
  return (

This is a page with lazy-loaded images

      <Image
        id="lazy-top"
        src="lazy1.jpg"
        height={400}
        width={1024}
        loading="lazy"
      ></Image>

      <Image
        id="lazy-mid"
        src="lazy2.jpg"
        loading="lazy"
        height={400}
        width={300}
        className="exampleclass"
      ><

></Image>

      <Image
        id="lazy-without-attribute"
        src="lazy4.jpg"
        height={400}
        width={800}
      ></Image>

      <Image

/Image>

      <Image
        id="lazy-bottom"
        src="https://www.otherhost.com/lazy3.jpg"
        height={400}
        width={300}
        unoptimized
        loading="lazy"


import { join } from 'path'
import { nextTestSetup } from 'e2e-utils'
import { waitFor } from 'next-test-utils'

describe('react-virtualized wrapping next/legacy/image', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    skipDeployment: true,
    dependencies: {
      'react-virtualized': 'latest',
      'http-proxy': '1.18.1',
    },
  })
  if (skipped) return

  let proxyChild: ChildProcess
  let proxyPort: number

  async function getCancelCount() {
    co

next.start()

    proxyChild = spawn(
      process.execPath,
      [join(next.testDir, 'server.js'), next.url, '0', '3000'],
      { stdio: ['ignore', 'pipe', 'inherit'] }
    )

    proxyPort = await new Promise<number>((resolve, reject) => {
      let b

nst res = await fetch(`http://localhost:${proxyPort}/_test/cancel-count`)
    const data = (await res.json()) as { cancelCount: number }
    return data.cancelCount
  }

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


xy server used by react-virtualized.test.ts to introduce
// per-request stalls and count cancelled image requests. The script is run
// inside the isolated test directory so `http-proxy` can be installed via the
// `dependencies` option of `nextTestSetup`. The test process communicates
// with this server via the `_test/cancel-count` endpoint.
const http = require('http')
const httpProxy = require('http-proxy')

const target = process.argv[2]
const port = Number(process.argv[3] || 0)
const stallMs = Number(

eServer(async (req, res) => {
  if (req.url === '/_test/cancel-count') {
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({ cancelCount }))
    return
  }

  if (req.url.startsWith('/_next/image')) {
    let isComplete = fal

process.argv[4] || 3000)

if (!target) {
  console.error('usage: node server.js <target> [port] [stallMs]')
  process.exit(1)
}

let cancelCount = 0
const proxy = httpProxy.createProxyServer({ target })

const server = http.creat


jpg'
import { WindowScroller, List as VirtualizedList } from 'react-virtualized'

export default function Home() {
  return (
    <div>
      <WindowScroller serverHeight={800}>
        {({ height, isScrolling, onChildScroll, scrollTop }) => (
          <VirtualizedList
            autoHeight
            height={height}
            isScrolling={isScrolling}
            onScroll={onChildScroll}
            scrollTop={scrollTop}
            width={810}
            rowCount={5}
            estimatedRowSize={10

r={() => {
              return (
                <div>
                  <Image src={img} placeholder="blur" className="thumbnail" />
                  <Image src={img} className="large" />
                </div>
              )
            }}

}
            rowHeight={400}
            rowRendere


t { nextTestSetup } from 'e2e-utils'
import cheerio from 'cheerio'

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

  describe('Noscript page source tests', () => {
    it('should use local API for noscript img#basic-image src attribute', async () => {
      const html = await next.render('/')
      const $ = cheerio.load(html)

      expect($('noscript > img#basic-image').attr('src')).toMatch(
        /^\/_next\/image/
      )
    })

    it('should use loa

script img#image-with-loader src attribute', async () => {
      const html = await next.render('/')
      const $ = cheerio.load(html)

      expect($('noscript > img#image-with-loader').attr('src')).toMatch(
        /^https:\/\/customresolver.com/


tTestSetup, type Playwright } from 'e2e-utils'

describe('Custom Resolver Tests', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })
  type Browser = Playwright

  function runTests(browser: () => Browser) {
    it('Should use a custom resolver for image URL', async () => {
      expect(
        await browser().elementById('basic-image').getAttribute('src')
      ).toBe('https://customresolver.com/foo.jpg?w~~1024,q~~60')
    })
    it('should add a srcset based on the custom resolver', as

     )
    })
    it('should support the unoptimized attribute', async () => {
      expect(
        await browser().elementById('unoptimized-image').getAttribute('src')
      ).toBe('https://arbitraryurl.com/foo.jpg')
    })
  }

  describe('SSR Custom Lo

ync () => {
      expect(
        await browser().elementById('basic-image').getAttribute('srcset')
      ).toBe(
        'https://customresolver.com/foo.jpg?w~~480,q~~60 1x, https://customresolver.com/foo.jpg?w~~1024,q~~60 2x'


t'
import Image from 'next/legacy/image'
import Link from 'next/link'

const myLoader = ({ src, width, quality }) => {
  return `https://customresolver.com/${src}?w~~${width},q~~${quality}`
}

const MyImage = (props) => {
  return <Image loader={myLoader} {...props}></Image>
}

const Page = () => {
  return (

Image SSR Test

      <MyImage
        id="basic-image"
        src="foo.jpg"
        loading="eager"
        width={300}
        height={400}
        quality={60}

   unoptimized
        src="https://arbitraryurl.com/foo.jpg"
        loading="eager"
        width={300}
        height={400}
      />
      <Link href="/client-side" id="clientlink">
        Client Side
      </Link>
    </div>
  )
}

export default Page

/>
      <Image
        id="unoptimized-image"


p } from 'e2e-utils'
import {
  findPort,
  initNextServerScript,
  killApp,
  renderViaHTTP,
} from 'next-test-utils'

describe('pnpm support', () => {
  describe('build with dependencies installed via pnpm', () => {
    const { next } = nextTestSetup({
      files: {
        pages: new FileRef(path.join(__dirname, 'app/pages')),
        'next.config.js': new FileRef(
          path.join(__dirname, 'app/next.config.js')
        ),
      },
      packageJson: {
        scripts: {
          build: 'next buil

BeTruthy()

      expect(next.cliOutput).toMatch(/Compiled successfully/)

      const html = await renderViaHTTP(next.url, '/')
      expect(html).toContain('Hello World')

      const manifest = JSON.parse(
        await next.readFile('.next/next-server.

d',
          start: 'next start',
        },
      },
      buildCommand: 'pnpm run build',
    })

    it('should build with dependencies installed via pnpm', async () => {
      expect(await next.readFile('pnpm-lock.yaml')).to


('next.config.js validation', () => {
  describe('production mode', () => {
    const { next, skipped } = nextTestSetup({
      files: __dirname,
      skipStart: true,
      skipDeployment: true,
    })
    if (skipped) return

    it.each([
      {
        name: 'invalid config types',
        configContent: `
        module.exports = {
          rewrites: true,
          images: {
            loader: 'something'
          }
        }
      `,
        outputs: [
          `received 'something' at "images.

     experimental: {
            anotherNonExistent: true
          }
        }
      `,
        outputs: [
          `Unrecognized key(s) in object: 'nonExistent'`,
          `Unrecognized key(s) in object: 'anotherNonExistent' at "experimental"`,

loader"`,
          'Expected function, received boolean at "rewrites"',
        ],
      },
      {
        name: 'unexpected config fields',
        configContent: `
        module.exports = {
          nonExistent: true,


hing code,
// so we only enable it in production

describe('Link with legacyBehavior - handles buggy userspace ref merging', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('does not crash when Link unmounts', async () => {
    const browser = await next.browser('/')
    expect(await browser.elementByCss('h1').text()).toEqual('Home')
    expect(await browser.hasElementByCssSelector('#test-link')).toBe(true)

    // hide the link, unmounting it
    await browser.elementByCss('butt

sSelector('#test-link')).toBe(false)

    // shouldn't cause a crash
    expect(await browser.elementByCss('h1').text()).toEqual('Home')
    expect(await browser.elementByCss('body').text()).not.toContain(
      'Application error: a client-side exception

on').click()
    expect(await browser.hasElementByCs


from 'next/link'
import {
  useCallback,
  useState,
  type ComponentPropsWithRef,
  type ReactNode,
  type Ref,
  type RefCallback,
} from 'react'

export default function Page() {
  return (
    <>

Home

      <ToggleVisibility>
        <Link href="/link-target" legacyBehavior>
          <AnchorThatDoesRefMerging id="test-link">
            Go to /link-target
          </AnchorThatDoesRefMerging>
        </Link>
      </ToggleVisibility>
    </>
  )
}

function ToggleVisibility({ children }:

ow content'}
        </button>
      </div>
      {isVisible ? children : null}
    </>
  )
}

function AnchorThatDoesRefMerging({
  ref,
  children,
  ...anchorProps
}: ComponentPropsWithRef<'a'>) {
  const customRef: RefCallback<HTMLAnchorElement> = useC

 { children: ReactNode }) {
  const [isVisible, setIsVisible] = useState(true)
  return (
    <>
      <div>
        <button type="button" onClick={() => setIsVisible((prev) => !prev)}>
          {isVisible ? 'Hide content' : 'Sh


mport { recursiveReadDir } from 'next/dist/lib/recursive-readdir'
import { nextTestSetup } from 'e2e-utils'

function extractSourceMappingURL(jsContent: string): string | null {
  // Matches both //# and //@ sourceMappingURL=...
  const match = jsContent.match(/\/\/[#@] sourceMappingURL=([^\s]+)/)
  return match ? match[1] : null
}

async function validateSourceMapForChunk(
  dir: string,
  file: string,
  {
    polyfillFiles,
    productionBrowserSourceMaps,
  }: { polyfillFiles: string[]; productionBrowse

  /^__turbopack_load_page_chunks__\(["']/.test(jsContent) ||
      (!process.env.IS_TURBOPACK_TEST && jsContent.length < 300)
    ) {
      // There is no sourcemap in these loader chunks, ignore
      return
    }
    if (await fs.pathExists(jsFilePath +

rSourceMaps: boolean }
) {
  const jsFilePath = path.join(dir, file)
  const jsContent = await fs.readFile(jsFilePath, 'utf8')
  const sourceMappingURL = extractSourceMappingURL(jsContent)
  if (!sourceMappingURL) {
    if (


> {
    it('should not revalidate during prefetching', async () => {
      const cliOutputStart = next.cliOutput.length

      for (let i = 0; i < 3; i++) {
        for (const path of ['/blog/first', '/blog/second']) {
          const res = await fetchViaHTTP(
            next.url,
            `/_next/data/${next.buildId}${path}.json`,
            undefined,
            {
              headers: {
                purpose: 'prefetch',
              },
            }
          )
          expect(res.status).toB

revalidating /blog'
      )
    })

    it('should trigger revalidation after navigation', async () => {
      const getData = () =>
        fetchViaHTTP(
          next.url,
          `/_next/data/${next.buildId}/blog/first.json`,
          undefined,

e(200)
        }
        // do requests three times with 1 second between
        // to go over revalidate period
        await waitFor(1000)
      }
      expect(next.cliOutput.substring(cliOutputStart)).not.toContain(
        '


from 'e2e-utils'
import { retry } from 'next-test-utils'
import { NextInstanceOpts } from '../../lib/next-modes/base'

describe('React Profiling Mode', () => {
  describe('default is disabled', () => {
    const { next } = nextTestSetup({
      files: __dirname,
    })

    it('should not have used the react-dom profiling bundle', async () => {
      const browser = await next.browser('/')
      const results = await browser.eval('window.profileResults')

      expect(results).toBeFalsy()
    })
  })

  des

'--profile'] } satisfies Partial<NextInstanceOpts>,
    },
  ])('enabled with $name', ({ opts }) => {
    const { next } = nextTestSetup({
      files: __dirname,
      ...opts,
    })

    it('should have used the react-dom profiling bundle for pages', as

cribe.each([
    {
      name: 'config setting',
      opts: {
        env: { TEST_REACT_PRODUCTION_PROFILING: 'true' },
      } satisfies Partial<NextInstanceOpts>,
    },
    {
      name: 'CLI flag',
      opts: { buildArgs: [


oryStatsWithFiles
  serverUnbundled: CategoryStatsWithFiles
  clientJs: CategoryStatsWithFiles
  clientMaps: CategoryStatsWithFiles
  clientCss: CategoryStatsWithFiles
}

interface ToolOutput {
  routes: RouteInfo[]
  totals: Totals
}

const ALL_CATEGORIES = [
  'serverBundled',
  'serverMaps',
  'serverUnbundled',
  'clientJs',
  'clientMaps',
  'clientCss',
] as const

describe('next internal static-routes-info', () => {
  if (!isNextStart) {
    it('skipped for non-start mode', () => {})
    return
  }

= 0) {
      throw new Error(
        `next build failed with exit code ${buildResult.exitCode}`
      )
    }
  })

  async function runTool(
    args: string[]
  ): Promise<{ stdout: string; stderr: string; code: number | null }> {
    const result = awa

  const { next, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  if (skipped) return

  beforeAll(async () => {
    const buildResult = await next.build()
    if (buildResult.exitCode !=


 { readFileSync, statSync } from 'fs'
import { join } from 'path'
import { nextTestSetup } from 'e2e-utils'

describe('Fallback Modules', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    dependencies: {
      seedrandom: 'latest',
    },
  })

  it('should not include crypto', async () => {
    if (process.env.NEXT_PRIVATE_SKIP_SIZE_TESTS) {
      return
    }

    await next.build()

    const buildManifestPath = join(next.testDir, '.next', 'build-manifest.json')

th = join(next.testDir, '.next', chunkPath)
      try {
        const stats = statSync(fullChunkPath)
        totalSize += stats.size
      } catch {
        // chunk may not exist
      }
    }

    const totalSizeKB = totalSize / 1024

    expect(totalSi

  const buildManifest = JSON.parse(readFileSync(buildManifestPath, 'utf8'))

    const indexPageChunks = buildManifest.pages['/'] || []

    let totalSize = 0
    for (const chunkPath of indexPageChunks) {
      const fullChunkPa