Skip to content

Commit f4aec3e

Browse files
authored
feat: support next 15 [run ci] (#30654)
1 parent 15c5761 commit f4aec3e

File tree

21 files changed

+1502
-80
lines changed

21 files changed

+1502
-80
lines changed

cli/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ in this [GitHub issue](https://github.com/cypress-io/cypress/issues/30447). Addr
4040
**Features:**
4141

4242
- Cypress Component Testing now supports `React` version 19. Cypress will allow detected use of the React 19 Release Candidate until React 19 is officially released. Addresses [#29470](https://github.com/cypress-io/cypress/issues/29470).
43+
- Cypress Component Testing now supports `Next.js` version 15. Addresses [#30445](https://github.com/cypress-io/cypress/issues/30445).
4344

4445
**Bugfixes:**
4546

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

+81-75
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/// <reference path="../support/e2e.ts" />
22
import type { ProjectFixtureDir } from '@tooling/system-tests/lib/fixtureDirs'
33

4-
// keeping this structure for future versions of next.js. Next.js 15 has released but we have not yet added support
54
const NEXT_PROJECTS: ProjectFixtureDir[] = ['next-14']
65
// Add to this list to focus on a particular permutation
76
const ONLY_PROJECTS: ProjectFixtureDir[] = []
@@ -107,99 +106,106 @@ for (const project of NEXT_PROJECTS) {
107106
})
108107
}
109108

110-
// Since next-14-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
111-
// We want to specifically test typescript files with next-14 as there have been known problems with
112-
// module: bundler and cypress compatibility
113-
describe(`Working with next-14-tsconfig-tailwind`, () => {
114-
beforeEach(() => {
115-
cy.scaffoldProject('next-14-tsconfig-tailwind')
116-
cy.openProject('next-14-tsconfig-tailwind', ['--component'])
117-
cy.startAppServer('component')
118-
})
109+
const NEXT_PROJECTS_TSCONFIG_TAILWIND: ProjectFixtureDir[] = ['next-14-tsconfig-tailwind', 'next-15-tsconfig-tailwind']
119110

120-
it('should mount a passing test', () => {
121-
cy.visitApp()
122-
cy.specsPageIsVisible()
123-
cy.contains('page.cy.tsx').click()
124-
cy.waitForSpecToFinish({ passCount: 1 })
125-
})
111+
for (const project of NEXT_PROJECTS_TSCONFIG_TAILWIND) {
112+
// Since next-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
113+
// We want to specifically test typescript files with next as there have been known problems with
114+
// module: bundler and cypress compatibility
115+
describe(`Working with ${project}`, () => {
116+
beforeEach(() => {
117+
cy.scaffoldProject(project)
118+
cy.openProject(project, ['--component'])
119+
cy.startAppServer('component')
120+
})
121+
122+
it('should mount a passing test', () => {
123+
cy.visitApp()
124+
cy.specsPageIsVisible()
125+
cy.contains('page.cy.tsx').click()
126+
cy.waitForSpecToFinish({ passCount: 1 })
127+
})
128+
129+
it('should live-reload on src changes', () => {
130+
cy.visitApp()
131+
cy.specsPageIsVisible()
126132

127-
it('should live-reload on src changes', () => {
128-
cy.visitApp()
129-
cy.specsPageIsVisible()
133+
cy.contains('page.cy.tsx').click()
134+
cy.waitForSpecToFinish({ passCount: 1 })
130135

131-
cy.contains('page.cy.tsx').click()
132-
cy.waitForSpecToFinish({ passCount: 1 })
136+
cy.withCtx(async (ctx) => {
137+
const indexPath = ctx.path.join('app', 'page.tsx')
133138

134-
cy.withCtx(async (ctx) => {
135-
const indexPath = ctx.path.join('app', 'page.tsx')
139+
await ctx.actions.file.writeFileInProject(
140+
indexPath,
141+
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
142+
)
143+
})
136144

137-
await ctx.actions.file.writeFileInProject(
138-
indexPath,
139-
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
140-
)
141-
})
145+
cy.waitForSpecToFinish({ failCount: 1 })
146+
if (project !== 'next-15-tsconfig-tailwind') {
147+
// code frames not fully working with next 15
148+
cy.get('.test-err-code-frame').should('be.visible')
149+
}
142150

143-
cy.waitForSpecToFinish({ failCount: 1 })
144-
cy.get('.test-err-code-frame').should('be.visible')
151+
cy.withCtx(async (ctx) => {
152+
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
145153

146-
cy.withCtx(async (ctx) => {
147-
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
154+
await ctx.actions.file.writeFileInProject(
155+
indexTestPath,
156+
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
157+
)
158+
})
148159

149-
await ctx.actions.file.writeFileInProject(
150-
indexTestPath,
151-
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
152-
)
160+
cy.waitForSpecToFinish({ passCount: 1 })
153161
})
154162

155-
cy.waitForSpecToFinish({ passCount: 1 })
156-
})
163+
it('should show compilation errors on src changes', () => {
164+
cy.visitApp()
165+
cy.specsPageIsVisible()
157166

158-
it('should show compilation errors on src changes', () => {
159-
cy.visitApp()
160-
cy.specsPageIsVisible()
167+
cy.contains('page.cy.tsx').click()
168+
cy.waitForSpecToFinish({ passCount: 1 })
161169

162-
cy.contains('page.cy.tsx').click()
163-
cy.waitForSpecToFinish({ passCount: 1 })
170+
// Create compilation error
171+
cy.withCtx(async (ctx) => {
172+
const indexPath = ctx.path.join('app', 'page.tsx')
164173

165-
// Create compilation error
166-
cy.withCtx(async (ctx) => {
167-
const indexPath = ctx.path.join('app', 'page.tsx')
174+
await ctx.actions.file.writeFileInProject(
175+
indexPath,
176+
(await ctx.file.readFileInProject(indexPath)).replace('export', 'expart'),
177+
)
178+
})
168179

169-
await ctx.actions.file.writeFileInProject(
170-
indexPath,
171-
(await ctx.file.readFileInProject(indexPath)).replace('export', 'expart'),
172-
)
180+
// The test should fail and the stack trace should appear in the command log
181+
cy.waitForSpecToFinish({ failCount: 1 })
182+
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
173183
})
174184

175-
// The test should fail and the stack trace should appear in the command log
176-
cy.waitForSpecToFinish({ failCount: 1 })
177-
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
178-
})
185+
it('should detect new spec', { retries: 15 }, () => {
186+
cy.visitApp()
187+
cy.specsPageIsVisible()
179188

180-
it('should detect new spec', { retries: 15 }, () => {
181-
cy.visitApp()
182-
cy.specsPageIsVisible()
189+
cy.withCtx(async (ctx) => {
190+
const newTestPath = ctx.path.join('app', 'New.cy.tsx')
191+
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
183192

184-
cy.withCtx(async (ctx) => {
185-
const newTestPath = ctx.path.join('app', 'New.cy.tsx')
186-
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
193+
await ctx.actions.file.writeFileInProject(
194+
newTestPath,
195+
await ctx.file.readFileInProject(indexTestPath),
196+
)
197+
})
187198

188-
await ctx.actions.file.writeFileInProject(
189-
newTestPath,
190-
await ctx.file.readFileInProject(indexTestPath),
191-
)
199+
cy.contains('New.cy.tsx').click()
200+
cy.waitForSpecToFinish({ passCount: 1 })
192201
})
193202

194-
cy.contains('New.cy.tsx').click()
195-
cy.waitForSpecToFinish({ passCount: 1 })
196-
})
197-
198-
// Make sure tailwind styles are appearing in the test.
199-
it('should allow import of global styles in support file', { retries: 15 }, () => {
200-
cy.visitApp()
201-
cy.specsPageIsVisible()
202-
cy.contains('page-styles.cy.tsx').click()
203-
cy.waitForSpecToFinish({ passCount: 1 })
203+
// Make sure tailwind styles are appearing in the test.
204+
it('should allow import of global styles in support file', { retries: 15 }, () => {
205+
cy.visitApp()
206+
cy.specsPageIsVisible()
207+
cy.contains('page-styles.cy.tsx').click()
208+
cy.waitForSpecToFinish({ passCount: 1 })
209+
})
204210
})
205-
})
211+
}

packages/scaffold-config/src/dependencies.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const WIZARD_DEPENDENCY_NEXT = {
5858
package: 'next',
5959
installer: 'next',
6060
description: 'The React Framework for Production',
61-
minVersion: '^14.0.0',
61+
minVersion: '^14.0.0 || ^15.0.0',
6262
} as const
6363

6464
export const WIZARD_DEPENDENCY_ANGULAR_CLI = {

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,12 @@ describe('detectFramework', () => {
131131
expect(actual.bundler).to.eq('vite')
132132
})
133133

134-
// keeping array style as to make it easier to support future next versions
135-
;['14.0.0'].forEach((v) => {
134+
;['14.0.0', '15.0.0'].forEach((v) => {
136135
it(`Next.js v${v}`, async () => {
137136
const projectPath = await scaffoldMigrationProject('nextjs-unconfigured')
138137

139138
fakeDepsInNodeModules(projectPath, [
140-
{ dependency: 'react', version: '18.0.0' },
139+
{ dependency: 'react', version: v === '15.0.0' ? '19.0.0-rc.1' : '18.0.0' },
141140
{ dependency: 'next', version: v },
142141
])
143142

system-tests/projects/next-14/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "next-latest",
2+
"name": "next-14",
33
"version": "0.0.0-test",
44
"scripts": {
55
"build": "next build",
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
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
:root {
6+
--foreground-rgb: 0, 0, 0;
7+
--background-start-rgb: 214, 219, 220;
8+
--background-end-rgb: 255, 255, 255;
9+
}
10+
11+
@media (prefers-color-scheme: dark) {
12+
:root {
13+
--foreground-rgb: 255, 255, 255;
14+
--background-start-rgb: 0, 0, 0;
15+
--background-end-rgb: 0, 0, 0;
16+
}
17+
}
18+
19+
body {
20+
color: rgb(var(--foreground-rgb));
21+
background: linear-gradient(
22+
to bottom,
23+
transparent,
24+
rgb(var(--background-end-rgb))
25+
)
26+
rgb(var(--background-start-rgb));
27+
}
28+
29+
@layer utilities {
30+
.text-balance {
31+
text-wrap: balance;
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Metadata } from 'next'
2+
import { Inter } from 'next/font/google'
3+
import './globals.css'
4+
5+
const inter = Inter({ subsets: ['latin'] })
6+
7+
export const metadata: Metadata = {
8+
title: 'Create Next App',
9+
description: 'Generated by create next app',
10+
}
11+
12+
export default function RootLayout ({
13+
children,
14+
}: Readonly<{
15+
children: React.ReactNode
16+
}>) {
17+
return (
18+
<html lang="en">
19+
<body className={inter.className}>{children}</body>
20+
</html>
21+
)
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import Home from './page'
3+
4+
describe('<Home />', () => {
5+
it('renders', () => {
6+
cy.mount(<Home />)
7+
cy.contains('h1', 'Welcome to Next.js!')
8+
9+
// verify tailwind classes are applied correctly via import from support file.
10+
cy.get('main').should('have.css', 'background-color', 'rgb(245, 158, 11)')
11+
})
12+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import Home from './page'
3+
4+
describe('<Home />', () => {
5+
it('renders', () => {
6+
cy.mount(<Home />)
7+
cy.contains('h1', 'Welcome to Next.js!')
8+
})
9+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Home () {
2+
return (
3+
<main className="bg-amber-500">
4+
<h1> Welcome to Next.js! </h1>
5+
</main>
6+
)
7+
}

0 commit comments

Comments
 (0)