Skip to content

Commit 12f72f2

Browse files
feat: support Next.js 16 inside component testing (#32955)
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
1 parent bf5c60d commit 12f72f2

File tree

23 files changed

+1060
-134
lines changed

23 files changed

+1060
-134
lines changed

.circleci/src/pipeline/@pipeline.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ commands:
121121
name: Set environment variable to determine whether or not to persist artifacts
122122
command: |
123123
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
124-
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/remove_unused_anchors" ]]; then
124+
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feat/support_next_16" ]]; then
125125
export SHOULD_PERSIST_ARTIFACTS=true
126126
fi' >> "$BASH_ENV"
127127
# You must run `setup_should_persist_artifacts` command and be using bash before running this command

.circleci/src/pipeline/workflows/@main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ linux-x64:
44
- equal: [ develop, << pipeline.git.branch >> ]
55
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
66
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
7-
- equal: [ 'chore/remove_unused_anchors', << pipeline.git.branch >> ]
7+
- equal: [ 'feat/support_next_16', << pipeline.git.branch >> ]
88
- matches:
99
pattern: /^release\/\d+\.\d+\.\d+$/
1010
value: << pipeline.git.branch >>

cli/CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
2-
## 15.6.1
2+
## 15.7.0
33

44
_Released 11/18/2025 (PENDING)_
55

66
**Performance:**
77

88
- Limits the number of matched elements that are tested for visibility when added to a command log entry. Fixes a crash scenario related to rapid successive DOM additions in conjunction with a large number of elements returned from a query. Addressed in [#32937](https://github.com/cypress-io/cypress/pull/32937).
99

10+
**Features:**
11+
12+
- `Next.js` version 16 is now supported within component testing. Currently, `webpack` is used to bundle Next.js components. Turbopack, the [new default](https://nextjs.org/docs/app/guides/upgrading/version-16#turbopack-by-default) inside Next.js 16, is not yet supported within Cypress. Addresses [#32857](https://github.com/cypress-io/cypress/issues/32857).
13+
1014
**Bugfixes:**
1115

1216
- Fixed an issue where [`cy.wrap()`](https://docs.cypress.io/api/commands/wrap) would cause infinite recursion and freeze the Cypress App when called with objects containing circular references. Fixes [#24715](https://github.com/cypress-io/cypress/issues/24715). Addressed in [#32917](https://github.com/cypress-io/cypress/pull/32917).
@@ -24,7 +28,7 @@ _Released 11/4/2025_
2428

2529
**Features:**
2630

27-
- Added a 'Self-healed' badge to the Command Log when [`cy.prompt()`](https://docs.cypress.io/api/commands/prompt) steps automatically recover after the element they need is not found in the cache. Addressed in [#32802](https://github.com/cypress-io/cypress/pull/32802).
31+
- Added a 'Self-healed' badge to the Command Log when [`cy.prompt()`](https://docs.cypress.io/api/commands/prompt) steps automatically recover after the element they need is not found in the cache. Addressed in [#32802](https://github.com/cypress-io/cypress/pull/32802).
2832
- [`cy.prompt()`](https://docs.cypress.io/api/commands/prompt) will now show a warning in the `Get code` modal when there are unsaved changes in `Studio` that will be lost if the user saves the generated code. Addressed in [#32741](https://github.com/cypress-io/cypress/pull/32741).
2933

3034
**Bugfixes:**

npm/webpack-dev-server/cypress/e2e/next.cy.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ for (const project of NEXT_PROJECTS) {
106106
})
107107
}
108108

109-
const NEXT_PROJECTS_TSCONFIG_TAILWIND: ProjectFixtureDir[] = ['next-14-tsconfig-tailwind', 'next-15-tsconfig-tailwind']
109+
const NEXT_PROJECTS_TSCONFIG_TAILWIND: ProjectFixtureDir[] = ['next-14-tsconfig-tailwind', 'next-15-tsconfig-tailwind', 'next-16-tsconfig-tailwind']
110110

111111
for (const project of NEXT_PROJECTS_TSCONFIG_TAILWIND) {
112112
// Since next-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
@@ -143,9 +143,11 @@ for (const project of NEXT_PROJECTS_TSCONFIG_TAILWIND) {
143143
})
144144

145145
cy.waitForSpecToFinish({ failCount: 1 })
146-
if (project !== 'next-15-tsconfig-tailwind') {
147-
// code frames not fully working with next 15
146+
if (project !== 'next-15-tsconfig-tailwind' && project !== 'next-16-tsconfig-tailwind') {
147+
// code frames not fully working with next 15/16
148148
cy.get('.test-err-code-frame').should('be.visible')
149+
} else {
150+
cy.get('.runnable-err-message').should('be.visible')
149151
}
150152

151153
cy.withCtx(async (ctx) => {

npm/webpack-dev-server/src/helpers/nextHandler.ts

Lines changed: 31 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Module from 'module'
33
import type { Configuration } from 'webpack'
44
import * as fs from 'fs'
55
import * as path from 'path'
6+
import semver from 'semver'
67
import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
78
import { cypressWebpackPath, getMajorVersion, ModuleClass, SourcedDependency, SourcedWebpack, sourceFramework, sourceHtmlWebpackPlugin, sourceWebpackDevServer } from './sourceRelativeWebpackModules'
89

@@ -74,71 +75,12 @@ function getNextJsPackages (devServerConfig: WebpackDevServerConfig) {
7475

7576
/**
7677
* Types for `getNextJsBaseWebpackConfig` based on version:
77-
* - v11.1.4
78+
* - v14.0.0, v15.0.0, and v16.0.0
7879
[
7980
dir: string,
80-
options: {
81-
buildId: string
82-
config: NextConfigComplete
83-
dev?: boolean
84-
isServer?: boolean
85-
pagesDir: string
86-
target?: string
87-
reactProductionProfiling?: boolean
88-
entrypoints: WebpackEntrypoints
89-
rewrites: CustomRoutes['rewrites']
90-
isDevFallback?: boolean
91-
runWebpackSpan: Span
92-
}
93-
]
94-
95-
* - v12.0.0 = Same as v11.1.4
96-
97-
* - v12.1.6
98-
[
99-
dir: string,
100-
options: {
101-
buildId: string
102-
config: NextConfigComplete
103-
compilerType: 'client' | 'server' | 'edge-server'
104-
dev?: boolean
105-
entrypoints: webpack5.EntryObject
106-
hasReactRoot: boolean
107-
isDevFallback?: boolean
108-
pagesDir: string
109-
reactProductionProfiling?: boolean
110-
rewrites: CustomRoutes['rewrites']
111-
runWebpackSpan: Span
112-
target?: string
113-
}
114-
]
115-
116-
* - v13.0.0
117-
[
118-
dir: string,
119-
options: {
120-
buildId: string
121-
config: NextConfigComplete
122-
compilerType: CompilerNameValues
123-
dev?: boolean
124-
entrypoints: webpack.EntryObject
125-
hasReactRoot: boolean
126-
isDevFallback?: boolean
127-
pagesDir?: string
128-
reactProductionProfiling?: boolean
129-
rewrites: CustomRoutes['rewrites']
130-
runWebpackSpan: Span
131-
target?: string
132-
appDir?: string
133-
middlewareMatchers?: MiddlewareMatcher[]
134-
}
135-
]
136-
137-
* - v13.0.1
138-
[
139-
dir: string,
140-
options: {
81+
options: {
14182
buildId: string
83+
encryptionKey: string
14284
config: NextConfigComplete
14385
compilerType: CompilerNameValues
14486
dev?: boolean
@@ -147,41 +89,27 @@ function getNextJsPackages (devServerConfig: WebpackDevServerConfig) {
14789
pagesDir?: string
14890
reactProductionProfiling?: boolean
14991
rewrites: CustomRoutes['rewrites']
92+
originalRewrites?: CustomRoutes['rewrites']
93+
originalRedirects?: CustomRoutes['redirects']
15094
runWebpackSpan: Span
151-
target?: string
15295
appDir?: string
153-
middlewareMatchers?: MiddlewareMatcher[]
154-
}
155-
]
156-
157-
* - v13.2.1
158-
[
159-
dir: string,
160-
options: {
161-
buildId: string
162-
config: NextConfigComplete
163-
compilerType: CompilerNameValues
164-
dev?: boolean
165-
entrypoints: webpack.EntryObject
166-
isDevFallback?: boolean
167-
pagesDir?: string
168-
reactProductionProfiling?: boolean
169-
rewrites: CustomRoutes['rewrites']
170-
runWebpackSpan: Span
171-
target?: string
172-
appDir?: string
173-
middlewareMatchers?: MiddlewareMatcher[]
174-
noMangling?: boolean
175-
jsConfig: any
176-
resolvedBaseUrl: string | undefined
177-
supportedBrowsers: string[] | undefined
178-
clientRouterFilters?: {
179-
staticFilter: ReturnType<
180-
import('../shared/lib/bloom-filter').BloomFilter['export']
181-
>
182-
dynamicFilter: ReturnType<
183-
import('../shared/lib/bloom-filter').BloomFilter['export']
184-
>
96+
middlewareMatchers?: ProxyMatcher[]
97+
noMangling?: boolean
98+
jsConfig: any
99+
jsConfigPath?: string
100+
resolvedBaseUrl: ResolvedBaseUrl
101+
supportedBrowsers: string[] | undefined
102+
clientRouterFilters?: {
103+
staticFilter: ReturnType<
104+
import('../shared/lib/bloom-filter').BloomFilter['export']
105+
>
106+
dynamicFilter: ReturnType<
107+
import('../shared/lib/bloom-filter').BloomFilter['export']
108+
>
109+
}
110+
fetchCacheKeyPrefix?: string
111+
isCompileMode?: boolean
112+
previewProps: NonNullable<(typeof NextBuildContext)['previewProps']>;
185113
}
186114
}
187115
]
@@ -210,7 +138,7 @@ async function loadWebpackConfig (devServerConfig: WebpackDevServerConfig): Prom
210138
// Client webpack config for Next.js > 12.1.5
211139
compilerType: 'client',
212140
// Required for Next.js > 13
213-
hasReactRoot: reactVersion === 18,
141+
hasReactRoot: Boolean(reactVersion && reactVersion >= 18),
214142
// Required for Next.js > 13.2.0 to respect TS/JS config
215143
jsConfig: jsConfigResult.jsConfig,
216144
// Required for Next.js > 13.2.0 to respect tsconfig.compilerOptions.baseUrl
@@ -338,34 +266,23 @@ function sourceNextWebpack (devServerConfig: WebpackDevServerConfig, framework:
338266
throw e
339267
}
340268

341-
// Next 11 allows the choice of webpack@4 or webpack@5, depending on the "webpack5" property in their next.config.js
342-
// The webpackModule.init" for Next 11 returns a webpack@4 or webpack@4 compiler instance based on this boolean
343-
let webpack5 = true
344269
const importPath = path.join(path.dirname(webpackJsonPath), 'webpack.js')
345270
const webpackModule = require(importPath)
346271

347-
try {
348-
const nextConfig = require(path.resolve(devServerConfig.cypressConfig.projectRoot, 'next.config.js'))
349-
350-
debug('NextWebpack: next.config.js found - %o', nextConfig)
351-
352-
if (nextConfig.webpack5 === false) {
353-
webpack5 = false
354-
}
355-
} catch (e) {
356-
// No next.config.js, assume webpack 5
272+
// If Next.js is 16 and greater, webpack.js runs the required code previously inside init() on require().
273+
// init() no longer exists and has the same affect on import, so we don't/can't invoke it
274+
if (semver.lt(framework.packageJson.version, '16.0.0')) {
275+
// for next 15 and down, we need to initialize the webpack module
276+
webpackModule.init(true)
357277
}
358278

359-
debug('NextWebpack: webpack5 - %s', webpack5)
360-
webpackModule.init(webpack5)
361-
362279
const packageJson = require(webpackJsonPath)
363280

364281
webpack.importPath = importPath
365282
// The package.json of "next/dist/compiled/webpack/package.json" has no version so we supply the version for later use
366-
webpack.packageJson = { ...packageJson, version: webpack5 ? '5' : '4' }
283+
webpack.packageJson = { ...packageJson, version: '5' }
367284
webpack.module = webpackModule.webpack
368-
webpack.majorVersion = getMajorVersion(webpack.packageJson, [4, 5])
285+
webpack.majorVersion = getMajorVersion(webpack.packageJson, [5])
369286

370287
debug('NextWebpack: Successfully loaded NextWebpack - %o', webpack)
371288

packages/scaffold-config/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ We will also attempt to scaffold a configuration file for projects using React a
88

99
### Supported Frameworks and Libraries
1010

11-
| Name | Version| Dev Server | Version | Library | Component Adaptor | Example Project |
12-
| ---------------- | -------| ---------- | ---------- | ------------------ | -------------------------- | ------------------------------------------------------------------- |
13-
| React | - | Vite | 5, 6, 7 | React 18, 19 | `@cypress/react@latest` | [Link](../../system-tests/projects/react-vite-ts-configured) |
14-
| React | - | Webpack | 5 | React 18, 19 | `@cypress/vue@latest` | [Link](../../system-tests/projects/react18) |
15-
| Vue | - | Vite | 5, 6, 7 | Vue 3 | `@cypress/react@latest` | [Link](../../system-tests/projects/vue3-vite-ts-configured) |
16-
| Vue | - | Webpack | 5 | Vue 3 | `@cypress/vue@latest` | [Link](../../system-tests/projects/vue3-webpack-ts-configured) |
17-
| Angular | - | Webpack | 5 | Angular 17, 18, 19 | `@cypress/angular@latest` | [Link](../../system-tests/projects/angular-cli-configured) |
18-
| Svelte | - | Vite | 5, 6, 7 | Svelte 5 | `@cypress/svelte@latest` | [Link](../../system-tests/projects/svelte-vite-configured) |
19-
| Svelte | - | Webpack | 5 | Svelte 5 | `@cypress/svelte@latest` | [Link](../../system-tests/projects/svelte-webpack-configured) |
20-
| Next.js | 14, 15 | Webpack | 5 | React 18, 19 | `@cypress/react@latest` | [Link](../../system-tests/projects/nextjs-configured) |
11+
| Name | Version | Dev Server | Version | Library | Component Adaptor | Example Project |
12+
| ---------------- | -----------| ---------- | ---------- | ------------------ | -------------------------- | ------------------------------------------------------------------- |
13+
| React | - | Vite | 5, 6, 7 | React 18, 19 | `@cypress/react@latest` | [Link](../../system-tests/projects/react-vite-ts-configured) |
14+
| React | - | Webpack | 5 | React 18, 19 | `@cypress/vue@latest` | [Link](../../system-tests/projects/react18) |
15+
| Vue | - | Vite | 5, 6, 7 | Vue 3 | `@cypress/react@latest` | [Link](../../system-tests/projects/vue3-vite-ts-configured) |
16+
| Vue | - | Webpack | 5 | Vue 3 | `@cypress/vue@latest` | [Link](../../system-tests/projects/vue3-webpack-ts-configured) |
17+
| Angular | - | Webpack | 5 | Angular 17, 18, 19 | `@cypress/angular@latest` | [Link](../../system-tests/projects/angular-cli-configured) |
18+
| Svelte | - | Vite | 5, 6, 7 | Svelte 5 | `@cypress/svelte@latest` | [Link](../../system-tests/projects/svelte-vite-configured) |
19+
| Svelte | - | Webpack | 5 | Svelte 5 | `@cypress/svelte@latest` | [Link](../../system-tests/projects/svelte-webpack-configured) |
20+
| Next.js | 14, 15, 16 | Webpack | 5 | React 18, 19 | `@cypress/react@latest` | [Link](../../system-tests/projects/nextjs-configured) |
2121

2222
### Adding More Projects
2323

packages/scaffold-config/src/dependencies.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const WIZARD_DEPENDENCY_NEXT = {
6161
// next 15.0.0 -> 15.0.3 use the React 19 RC as a dependency
6262
// Since we do not support the React 19 RC and only the official React 19 release,
6363
// we will only be supporting Next.js 15.0.4 officially (the others previously mentioned should still work)
64-
minVersion: '^14.0.0 || ^15.0.4',
64+
minVersion: '^14.0.0 || ^15.0.4 || ^16.0.0',
6565
} as const
6666

6767
export const WIZARD_DEPENDENCY_ANGULAR_CLI = {

packages/scaffold-config/test/detect.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,12 @@ describe('detectFramework', () => {
6969
expect(actual.bundler).toEqual('vite')
7070
})
7171

72-
;['14.0.0', '15.0.4'].forEach((v) => {
72+
;['15.0.4', '16.0.0'].forEach((v) => {
7373
it(`Next.js v${v}`, async () => {
7474
const projectPath = await scaffoldMigrationProject('nextjs-unconfigured')
7575

7676
fakeDepsInNodeModules(projectPath, [
77-
{ dependency: 'react', version: v === '15.0.0' ? '19.0.0' : '18.0.0' },
77+
{ dependency: 'react', version: '19.0.0' },
7878
{ dependency: 'next', version: v },
7979
])
8080

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
32+
# env files (can opt-in for committing if needed)
33+
.env*
34+
35+
# vercel
36+
.vercel
37+
38+
# typescript
39+
*.tsbuildinfo
40+
next-env.d.ts
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20+
21+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22+
23+
## Learn More
24+
25+
To learn more about Next.js, take a look at the following resources:
26+
27+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29+
30+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31+
32+
## Deploy on Vercel
33+
34+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35+
36+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

0 commit comments

Comments
 (0)