Skip to content

Commit cf6b29d

Browse files
AtofStrykermschile
andauthored
feat: support Next.JS version 14 (#29558)
* feat: support Next.JS version 14 [run ci] * bump circle cache [run ci] * Update npm/webpack-dev-server/cypress/e2e/next.cy.ts Co-authored-by: Matt Schile <[email protected]> * Update cli/CHANGELOG.md Co-authored-by: Matt Schile <[email protected]> --------- Co-authored-by: Matt Schile <[email protected]>
1 parent 2cf5cf8 commit cf6b29d

35 files changed

+5212
-5116
lines changed

.circleci/cache-version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Bump this version to force CI to re-create the cache from scratch.
22

3-
05-09-24
3+
05-23-24

.circleci/workflows.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ mainBuildFilters: &mainBuildFilters
3131
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
3232
- 'update-v8-snapshot-cache-on-develop'
3333
- 'publish-binary'
34-
- 'feat/vite_5_support'
34+
- 'feat/support_next_14'
3535

3636
# usually we don't build Mac app - it takes a long time
3737
# but sometimes we want to really confirm we are doing the right thing
@@ -42,7 +42,7 @@ macWorkflowFilters: &darwin-workflow-filters
4242
- equal: [ develop, << pipeline.git.branch >> ]
4343
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
4444
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
45-
- equal: [ 'feat/vite_5_support', << pipeline.git.branch >> ]
45+
- equal: [ 'feat/support_next_14', << pipeline.git.branch >> ]
4646
- matches:
4747
pattern: /^release\/\d+\.\d+\.\d+$/
4848
value: << pipeline.git.branch >>
@@ -53,7 +53,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
5353
- equal: [ develop, << pipeline.git.branch >> ]
5454
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
5555
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
56-
- equal: [ 'feat/vite_5_support', << pipeline.git.branch >> ]
56+
- equal: [ 'feat/support_next_14', << pipeline.git.branch >> ]
5757
- matches:
5858
pattern: /^release\/\d+\.\d+\.\d+$/
5959
value: << pipeline.git.branch >>
@@ -76,7 +76,7 @@ windowsWorkflowFilters: &windows-workflow-filters
7676
- equal: [ develop, << pipeline.git.branch >> ]
7777
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
7878
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
79-
- equal: [ 'chore/fix_illegal_characters_in_windows', << pipeline.git.branch >> ]
79+
- equal: [ 'feat/support_next_14', << pipeline.git.branch >> ]
8080
- matches:
8181
pattern: /^release\/\d+\.\d+\.\d+$/
8282
value: << pipeline.git.branch >>
@@ -152,7 +152,7 @@ commands:
152152
name: Set environment variable to determine whether or not to persist artifacts
153153
command: |
154154
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
155-
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feat/vite_5_support" ]]; then
155+
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feat/support_next_14" ]]; then
156156
export SHOULD_PERSIST_ARTIFACTS=true
157157
fi' >> "$BASH_ENV"
158158
# You must run `setup_should_persist_artifacts` command and be using bash before running this command

cli/CHANGELOG.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
2-
## 13.10.1
2+
## 13.11.0
33

4-
_Released 5/28/2024 (PENDING)_
4+
_Released 6/4/2024 (PENDING)_
5+
6+
**Features:**
7+
8+
- Added support for [Next.js 14](https://nextjs.org/blog/next-14) for component testing. Addresses [#28185](https://github.com/cypress-io/cypress/issues/28185).
59

610
**Bugfixes:**
711

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

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

4-
const WEBPACK_REACT: ProjectFixtureDir[] = ['next-12', 'next-12.1.6', 'next-13', 'next-13-tsconfig']
5-
4+
const WEBPACK_REACT: ProjectFixtureDir[] = ['next-12', 'next-12.1.6', 'next-13', 'next-13-tsconfig', 'next-14']
65
// Add to this list to focus on a particular permutation
76
const ONLY_PROJECTS: ProjectFixtureDir[] = []
87

@@ -106,3 +105,100 @@ for (const project of WEBPACK_REACT) {
106105
})
107106
})
108107
}
108+
109+
// Since next-14-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
110+
// We want to specifically test typescript files with next-14 as there have been known problems with
111+
// module: bundler and cypress compatibility
112+
describe(`Working with next-14-tsconfig-tailwind`, () => {
113+
beforeEach(() => {
114+
cy.scaffoldProject('next-14-tsconfig-tailwind')
115+
cy.openProject('next-14-tsconfig-tailwind', ['--component'])
116+
cy.startAppServer('component')
117+
})
118+
119+
it('should mount a passing test', () => {
120+
cy.visitApp()
121+
cy.specsPageIsVisible()
122+
cy.contains('page.cy.tsx').click()
123+
cy.waitForSpecToFinish({ passCount: 1 })
124+
})
125+
126+
it('should live-reload on src changes', () => {
127+
cy.visitApp()
128+
cy.specsPageIsVisible()
129+
130+
cy.contains('page.cy.tsx').click()
131+
cy.waitForSpecToFinish({ passCount: 1 })
132+
133+
cy.withCtx(async (ctx) => {
134+
const indexPath = ctx.path.join('app', 'page.tsx')
135+
136+
await ctx.actions.file.writeFileInProject(
137+
indexPath,
138+
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
139+
)
140+
})
141+
142+
cy.waitForSpecToFinish({ failCount: 1 })
143+
cy.get('.test-err-code-frame').should('be.visible')
144+
145+
cy.withCtx(async (ctx) => {
146+
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
147+
148+
await ctx.actions.file.writeFileInProject(
149+
indexTestPath,
150+
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
151+
)
152+
})
153+
154+
cy.waitForSpecToFinish({ passCount: 1 })
155+
})
156+
157+
it('should show compilation errors on src changes', () => {
158+
cy.visitApp()
159+
cy.specsPageIsVisible()
160+
161+
cy.contains('page.cy.tsx').click()
162+
cy.waitForSpecToFinish({ passCount: 1 })
163+
164+
// Create compilation error
165+
cy.withCtx(async (ctx) => {
166+
const indexPath = ctx.path.join('app', 'page.tsx')
167+
168+
await ctx.actions.file.writeFileInProject(
169+
indexPath,
170+
(await ctx.file.readFileInProject(indexPath)).replace('export', 'expart'),
171+
)
172+
})
173+
174+
// The test should fail and the stack trace should appear in the command log
175+
cy.waitForSpecToFinish({ failCount: 1 })
176+
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
177+
})
178+
179+
it('should detect new spec', { retries: 15 }, () => {
180+
cy.visitApp()
181+
cy.specsPageIsVisible()
182+
183+
cy.withCtx(async (ctx) => {
184+
const newTestPath = ctx.path.join('app', 'New.cy.tsx')
185+
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
186+
187+
await ctx.actions.file.writeFileInProject(
188+
newTestPath,
189+
await ctx.file.readFileInProject(indexTestPath),
190+
)
191+
})
192+
193+
cy.contains('New.cy.tsx').click()
194+
cy.waitForSpecToFinish({ passCount: 1 })
195+
})
196+
197+
// Make sure tailwind styles are appearing in the test.
198+
it('should allow import of global styles in support file', { retries: 15 }, () => {
199+
cy.visitApp()
200+
cy.specsPageIsVisible()
201+
cy.contains('page-styles.cy.tsx').click()
202+
cy.waitForSpecToFinish({ passCount: 1 })
203+
})
204+
})

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export function sourceWebpackDevServer (config: WebpackDevServerConfig, webpackM
222222

223223
throw new CypressWebpackDevServerError(
224224
`Incompatible major versions of webpack and webpack-dev-server!
225-
webpack-dev-server major version ${webpackDevServer.majorVersion} only works with major versions of webpack ${webpackMajorVersion} - saw webpack-dev-server version ${json.version}.
225+
webpack-dev-server major version ${webpackDevServer.majorVersion} only works with major versions of webpack 5 - saw webpack-dev-server version ${json.version}.
226226
If using webpack major version 4, please install webpack-dev-server version 4 to be used with @cypress/webpack-dev-server or upgrade to webpack 5.`,
227227
)
228228
}

npm/webpack-dev-server/test/handlers/nextHandler.spec.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import { WebpackDevServerConfig } from '../../src/devServer'
77
import '../support'
88

99
const expectWatchOverrides = (webpackConfig: Configuration) => {
10-
expect(webpackConfig.watchOptions?.ignored).to.contain('**/node_modules/!(@cypress/webpack-dev-server/dist/browser.js)**')
10+
expect((webpackConfig.watchOptions?.ignored as RegExp)?.test('**/node_modules/!(@cypress/webpack-dev-server/dist/browser.js)**')).to.be.true
1111
}
1212

1313
const expectPagesDir = (webpackConfig: Configuration, projectRoot: string) => {
1414
const ReactLoadablePlugin: any = webpackConfig.plugins?.find((plugin) => plugin.constructor.name === 'ReactLoadablePlugin')
1515

16-
expect(ReactLoadablePlugin.pagesDir).eq(path.join(projectRoot, 'pages'))
16+
expect(ReactLoadablePlugin.pagesOrAppDir).eq(path.join(projectRoot, 'pages'))
1717
}
1818

1919
const expectWebpackSpan = (webpackConfig: Configuration) => {
@@ -54,8 +54,8 @@ describe('nextHandler', function () {
5454
// can take a while since we install node_modules
5555
this.timeout(1000 * 60)
5656

57-
it('sources from a next-12 project', async () => {
58-
const projectRoot = await scaffoldMigrationProject('next-12')
57+
it('sources from a next-14 project', async () => {
58+
const projectRoot = await scaffoldMigrationProject('next-14')
5959

6060
process.chdir(projectRoot)
6161

@@ -75,7 +75,7 @@ describe('nextHandler', function () {
7575
})
7676

7777
it('throws if nodeVersion is set to bundled', async () => {
78-
const projectRoot = await scaffoldMigrationProject('next-12')
78+
const projectRoot = await scaffoldMigrationProject('next-14')
7979

8080
process.chdir(projectRoot)
8181

packages/app/cypress/e2e/runner/ct-framework-errors.cy.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ describe('Next.js', {
136136
numTestsKeptInMemory: 1,
137137
}, () => {
138138
beforeEach(() => {
139-
cy.scaffoldProject('next-12')
139+
cy.scaffoldProject('next-14')
140140
})
141141

142142
it('error conditions', () => {
143143
const verify = loadErrorSpec({
144-
projectName: 'next-12',
144+
projectName: 'next-14',
145145
configFile: 'cypress.config.js',
146146
filePath: 'cypress/Errors.cy.jsx',
147147
failCount: 4,

packages/launchpad/cypress/e2e/config-warning.cy.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ describe('component testing dependency warnings', () => {
258258
})
259259

260260
it('does not show warning for project that does not require bundler to be installed', () => {
261-
cy.scaffoldProject('next-12')
262-
cy.openProject('next-12', ['--component'])
261+
cy.scaffoldProject('next-14')
262+
cy.openProject('next-14', ['--component'])
263263
cy.visitLaunchpad()
264264
cy.skipWelcome()
265265
cy.get('[data-cy="warning-alert"]').should('not.exist')

packages/scaffold-config/src/dependencies.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const WIZARD_DEPENDENCY_NEXT = {
9494
package: 'next',
9595
installer: 'next',
9696
description: 'The React Framework for Production',
97-
minVersion: '^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0',
97+
minVersion: '^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0',
9898
} as const
9999

100100
export const WIZARD_DEPENDENCY_ANGULAR_CLI = {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ describe('detectFramework', () => {
191191
expect(actual.bundler).to.eq('vite')
192192
})
193193

194-
;['10.0.0', '11.0.0', '12.0.0'].forEach((v) => {
194+
;['10.0.0', '11.0.0', '12.0.0', '13.0.0', '14.0.0'].forEach((v) => {
195195
it(`Next.js v${v}`, async () => {
196196
const projectPath = await scaffoldMigrationProject('nextjs-unconfigured')
197197

system-tests/__snapshots__/webpack_dev_server_fresh_spec.ts.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2408,7 +2408,7 @@ Your configFile threw an error from: cypress-webpack.config.ts
24082408
We stopped running your tests because your config file crashed.
24092409
24102410
CypressWebpackDevServerError: Incompatible major versions of webpack and webpack-dev-server!
2411-
webpack-dev-server major version 5 only works with major versions of webpack 4 - saw webpack-dev-server version 5.0.4.
2411+
webpack-dev-server major version 5 only works with major versions of webpack 5 - saw webpack-dev-server version 5.0.4.
24122412
If using webpack major version 4, please install webpack-dev-server version 4 to be used with @cypress/webpack-dev-server or upgrade to webpack 5.
24132413
[stack trace lines]
24142414
`
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import path from 'path'
2+
import { defineConfig } from 'cypress'
3+
4+
export default defineConfig({
5+
component: {
6+
devServer: {
7+
framework: 'next',
8+
bundler: 'webpack',
9+
// Necessary due to cypress/react resolving from cypress/node_modules rather than the project root
10+
webpackConfig: {
11+
resolve: {
12+
alias: {
13+
'react': path.resolve(__dirname, './node_modules/react'),
14+
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
15+
},
16+
},
17+
},
18+
},
19+
fixturesFolder: false,
20+
},
21+
// These tests should run quickly / fail quickly,
22+
// since we intentionally causing error states for testing
23+
defaultCommandTimeout: 1000,
24+
})

0 commit comments

Comments
 (0)