feat: support next 15 [run ci] (#30654)

This commit is contained in:
Bill Glesias
2024-11-21 16:36:23 -05:00
committed by GitHub
parent 15c5761eb6
commit f4aec3ed46
21 changed files with 1506 additions and 84 deletions

View File

@@ -40,6 +40,7 @@ in this [GitHub issue](https://github.com/cypress-io/cypress/issues/30447). Addr
**Features:**
- 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).
- Cypress Component Testing now supports `Next.js` version 15. Addresses [#30445](https://github.com/cypress-io/cypress/issues/30445).
**Bugfixes:**

View File

@@ -1,7 +1,6 @@
/// <reference path="../support/e2e.ts" />
import type { ProjectFixtureDir } from '@tooling/system-tests/lib/fixtureDirs'
// keeping this structure for future versions of next.js. Next.js 15 has released but we have not yet added support
const NEXT_PROJECTS: ProjectFixtureDir[] = ['next-14']
// Add to this list to focus on a particular permutation
const ONLY_PROJECTS: ProjectFixtureDir[] = []
@@ -107,99 +106,106 @@ for (const project of NEXT_PROJECTS) {
})
}
// Since next-14-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
// We want to specifically test typescript files with next-14 as there have been known problems with
// module: bundler and cypress compatibility
describe(`Working with next-14-tsconfig-tailwind`, () => {
beforeEach(() => {
cy.scaffoldProject('next-14-tsconfig-tailwind')
cy.openProject('next-14-tsconfig-tailwind', ['--component'])
cy.startAppServer('component')
})
const NEXT_PROJECTS_TSCONFIG_TAILWIND: ProjectFixtureDir[] = ['next-14-tsconfig-tailwind', 'next-15-tsconfig-tailwind']
it('should mount a passing test', () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
})
it('should live-reload on src changes', () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.withCtx(async (ctx) => {
const indexPath = ctx.path.join('app', 'page.tsx')
await ctx.actions.file.writeFileInProject(
indexPath,
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
)
for (const project of NEXT_PROJECTS_TSCONFIG_TAILWIND) {
// Since next-tsconfig-tailwind does not use the fixture directory we need to write our own test suite
// We want to specifically test typescript files with next as there have been known problems with
// module: bundler and cypress compatibility
describe(`Working with ${project}`, () => {
beforeEach(() => {
cy.scaffoldProject(project)
cy.openProject(project, ['--component'])
cy.startAppServer('component')
})
cy.waitForSpecToFinish({ failCount: 1 })
cy.get('.test-err-code-frame').should('be.visible')
cy.withCtx(async (ctx) => {
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
await ctx.actions.file.writeFileInProject(
indexTestPath,
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
)
it('should mount a passing test', () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
})
cy.waitForSpecToFinish({ passCount: 1 })
})
it('should live-reload on src changes', () => {
cy.visitApp()
cy.specsPageIsVisible()
it('should show compilation errors on src changes', () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.withCtx(async (ctx) => {
const indexPath = ctx.path.join('app', 'page.tsx')
// Create compilation error
cy.withCtx(async (ctx) => {
const indexPath = ctx.path.join('app', 'page.tsx')
await ctx.actions.file.writeFileInProject(
indexPath,
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
)
})
await ctx.actions.file.writeFileInProject(
indexPath,
(await ctx.file.readFileInProject(indexPath)).replace('export', 'expart'),
)
cy.waitForSpecToFinish({ failCount: 1 })
if (project !== 'next-15-tsconfig-tailwind') {
// code frames not fully working with next 15
cy.get('.test-err-code-frame').should('be.visible')
}
cy.withCtx(async (ctx) => {
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
await ctx.actions.file.writeFileInProject(
indexTestPath,
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
)
})
cy.waitForSpecToFinish({ passCount: 1 })
})
// The test should fail and the stack trace should appear in the command log
cy.waitForSpecToFinish({ failCount: 1 })
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
})
it('should show compilation errors on src changes', () => {
cy.visitApp()
cy.specsPageIsVisible()
it('should detect new spec', { retries: 15 }, () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.withCtx(async (ctx) => {
const newTestPath = ctx.path.join('app', 'New.cy.tsx')
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
// Create compilation error
cy.withCtx(async (ctx) => {
const indexPath = ctx.path.join('app', 'page.tsx')
await ctx.actions.file.writeFileInProject(
newTestPath,
await ctx.file.readFileInProject(indexTestPath),
)
await ctx.actions.file.writeFileInProject(
indexPath,
(await ctx.file.readFileInProject(indexPath)).replace('export', 'expart'),
)
})
// The test should fail and the stack trace should appear in the command log
cy.waitForSpecToFinish({ failCount: 1 })
cy.contains('The following error originated from your test code, not from Cypress.').should('exist')
})
cy.contains('New.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
})
it('should detect new spec', { retries: 15 }, () => {
cy.visitApp()
cy.specsPageIsVisible()
// Make sure tailwind styles are appearing in the test.
it('should allow import of global styles in support file', { retries: 15 }, () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page-styles.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
cy.withCtx(async (ctx) => {
const newTestPath = ctx.path.join('app', 'New.cy.tsx')
const indexTestPath = ctx.path.join('app', 'page.cy.tsx')
await ctx.actions.file.writeFileInProject(
newTestPath,
await ctx.file.readFileInProject(indexTestPath),
)
})
cy.contains('New.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
})
// Make sure tailwind styles are appearing in the test.
it('should allow import of global styles in support file', { retries: 15 }, () => {
cy.visitApp()
cy.specsPageIsVisible()
cy.contains('page-styles.cy.tsx').click()
cy.waitForSpecToFinish({ passCount: 1 })
})
})
})
}

View File

@@ -58,7 +58,7 @@ export const WIZARD_DEPENDENCY_NEXT = {
package: 'next',
installer: 'next',
description: 'The React Framework for Production',
minVersion: '^14.0.0',
minVersion: '^14.0.0 || ^15.0.0',
} as const
export const WIZARD_DEPENDENCY_ANGULAR_CLI = {

View File

@@ -131,13 +131,12 @@ describe('detectFramework', () => {
expect(actual.bundler).to.eq('vite')
})
// keeping array style as to make it easier to support future next versions
;['14.0.0'].forEach((v) => {
;['14.0.0', '15.0.0'].forEach((v) => {
it(`Next.js v${v}`, async () => {
const projectPath = await scaffoldMigrationProject('nextjs-unconfigured')
fakeDepsInNodeModules(projectPath, [
{ dependency: 'react', version: '18.0.0' },
{ dependency: 'react', version: v === '15.0.0' ? '19.0.0-rc.1' : '18.0.0' },
{ dependency: 'next', version: v },
])

View File

@@ -1,5 +1,5 @@
{
"name": "next-latest",
"name": "next-14",
"version": "0.0.0-test",
"scripts": {
"build": "next build",

View File

@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -0,0 +1,36 @@
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).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
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.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
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.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@@ -0,0 +1,33 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}

View File

@@ -0,0 +1,22 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout ({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}

View File

@@ -0,0 +1,12 @@
import React from 'react'
import Home from './page'
describe('<Home />', () => {
it('renders', () => {
cy.mount(<Home />)
cy.contains('h1', 'Welcome to Next.js!')
// verify tailwind classes are applied correctly via import from support file.
cy.get('main').should('have.css', 'background-color', 'rgb(245, 158, 11)')
})
})

View File

@@ -0,0 +1,9 @@
import React from 'react'
import Home from './page'
describe('<Home />', () => {
it('renders', () => {
cy.mount(<Home />)
cy.contains('h1', 'Welcome to Next.js!')
})
})

View File

@@ -0,0 +1,7 @@
export default function Home () {
return (
<main className="bg-amber-500">
<h1> Welcome to Next.js! </h1>
</main>
)
}

View File

@@ -0,0 +1,24 @@
import path from 'path'
import { defineConfig } from 'cypress'
export default defineConfig({
component: {
devServer: {
framework: 'next',
bundler: 'webpack',
// Necessary due to cypress/react resolving from cypress/node_modules rather than the project root
webpackConfig: {
resolve: {
alias: {
'react': path.resolve(__dirname, './node_modules/react'),
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
},
},
},
},
fixturesFolder: false,
},
// These tests should run quickly / fail quickly,
// since we intentionally causing error states for testing
defaultCommandTimeout: 1000,
})

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
<!-- Used by Next.js to inject CSS. -->
<div id="__next_css__DO_NOT_USE__"></div>
</head>
<body>
<div data-cy-root></div>
</body>
</html>

View File

@@ -0,0 +1,40 @@
// ***********************************************************
// This example support/component.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
import 'tailwindcss/tailwind.css'
// NOTE: we need to use the CJS require since we are using webpack alias to resolve the correct React (19),
// as the root of the monorepo is different (18). Webpack aliases don't support esm, so using the ESM import of
// 'cypress/react' will try to source esm dependencies from the root, which will have the wrong version of react/react-dom
// and throw an error. This does NOT have actual bearing on the end user experience, just our ability to test
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { mount } = require('cypress/react')
// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
Cypress.Commands.add('mount', mount)
// Example use:
// cy.mount(<MyComponent />)

View File

@@ -0,0 +1,8 @@
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
reactStrictMode: true,
swcMinify: true,
}
export default nextConfig

View File

@@ -0,0 +1,23 @@
{
"name": "next-15-tsconfig-tailwind",
"version": "0.0.0-test",
"scripts": {
"build": "next build",
"dev": "next dev",
"lint": "next lint",
"start": "next start"
},
"dependencies": {
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1,21 @@
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [],
}
export default config

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
/* important to test 'moduleResolution: "bundler"' as Cypress needs to coerce this to "node" in order to parse the config correctly. @see https://github.com/cypress-io/cypress/pull/28709 */
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

File diff suppressed because it is too large Load Diff