diff --git a/docs/02-app/01-building-your-application/08-testing/01-vitest.mdx b/docs/02-app/01-building-your-application/08-testing/01-vitest.mdx new file mode 100644 index 0000000000000..5b79dbfcc85b6 --- /dev/null +++ b/docs/02-app/01-building-your-application/08-testing/01-vitest.mdx @@ -0,0 +1,202 @@ +--- +title: Setting up Vitest with Next.js +nav: Vitest +description: Learn how to set up Vitest with Next.js for Unit Testing. +--- + +Vite and React Testing Library are frequently used together for **Unit Testing**. This guide will show you how to setup Vitest with Next.js and write your first tests. + +> **Good to know:** Since `async` Server Components are new to the React ecosystem, Vitest currently does not support them. While you can still run **unit tests** for synchronous Server and Client Components, we recommend using an **E2E tests** for `async` components. + +## Quickstart + +You can use `create-next-app` with the Next.js [with-vitest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started: + +```bash filename="Terminal" +npx create-next-app@latest --example with-vitest with-jest-vitest +``` + +## Manual Setup + +To manually set up Vitest, install `vitest` and the following packages as dev dependencies: + +```bash filename="Terminal" +npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react +# or +yarn add -D vitest @vitejs/plugin-react jsdom @testing-library/react @vitejs/plugin-react +# or +pnpm install -D vitest @vitejs/plugin-react jsdom @testing-library/react +``` + +Create a `vitest.config.ts|js` file in the root of your project, and add the following options: + +```ts filename="vitest.config.ts" switcher +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + }, +}) +``` + +```js filename="vitest.config.js" switcher +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + }, +}) +``` + +For more information on configuring Vitest, please refer to the [Vitest Cofiguration](https://vitest.dev/config/#configuration) docs. + +Then, add a `test` script to your `package.json`: + +```json filename="package.json" +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "vitest" + } +} +``` + +When you run `npm run test`, Vitest will **watch** for changes in your project by default. + +## Creating your first Vitest Unit Test + +Check that everything is working by creating a test to check if the `` component successfully renders a heading: + + + +```tsx filename="app/page.tsx" switcher +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```jsx filename="app/page.js" switcher +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```tsx filename="__tests__/page.test.tsx" switcher +import { expect, test } from 'vitest' +import { render, screen } from '@testing-library/react' +import Page from '../app/page' + +test('Page', () => { + render() + expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined() +}) +``` + +```jsx filename="__tests__/page.test.jsx" switcher +import { expect, test } from 'vitest' +import { render, screen } from '@testing-library/react' +import Page from '../app/page' + +test('Page', () => { + render() + expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined() +}) +``` + +> **Good to know**: The example above uses the common `__tests__` convention, but test files can also be colocated inside the `app` router. + +
+ + + +```tsx filename="pages/index.tsx" switcher +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```jsx filename="pages/index.jsx" switcher +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```tsx filename="__tests__/index.test.tsx" switcher +import { expect, test } from 'vitest' +import { render, screen } from '@testing-library/react' +import Page from '../pages/index' + +test('Page', () => { + render() + expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined() +}) +``` + +```jsx filename="__tests__/index.test.jsx" switcher +import { expect, test } from 'vitest' +import { render, screen } from '@testing-library/react' +import Page from '../pages/index' + +test('Page', () => { + render() + expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined() +}) +``` + +
+ +## Running your tests + +Then, run the following command to run your tests: + +```bash filename="Terminal" +npm run test +# or +yarn test +# or +pnpm test +``` + +## Additional Resources + +You may find these resources helpful: + +- [Next.js with Vitest example](https://github.com/vercel/next.js/tree/canary/examples/with-vitest) +- [Vitest Docs](https://vitest.dev/guide/) +- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) diff --git a/docs/02-app/01-building-your-application/08-testing/02-jest.mdx b/docs/02-app/01-building-your-application/08-testing/02-jest.mdx new file mode 100644 index 0000000000000..e9f923ea78e9c --- /dev/null +++ b/docs/02-app/01-building-your-application/08-testing/02-jest.mdx @@ -0,0 +1,387 @@ +--- +title: Setting up Jest with Next.js +nav: Jest +description: Learn how to set up Jest with Next.js for Unit Testing and Snapshot Testing. +--- + +Jest and React Testing Library are frequently used together for **Unit Testing** and **Snapshot Testing**. This guide will show you how to set up Jest with Next.js and write your first tests. + +> **Good to know:** Since `async` Server Components are new to the React ecosystem, Jest currently does not support them. While you can still run **unit tests** for synchronous Server and Client Components, we recommend using an **E2E tests** for `async` components. + +## Quickstart + +You can use `create-next-app` with the Next.js [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started: + +```bash filename="Terminal" +npx create-next-app@latest --example with-jest with-jest-app +``` + +## Manual setup + +Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. + +To set up Jest, install `jest` and the following packages as dev dependencies: + +```bash filename="Terminal" +npm install -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom +# or +yarn add -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom +# or +pnpm install -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom +``` + +Generate a basic Jest configuration file by running the following command: + +```bash filename="Terminal" +npm init jest@latest +# or +yarn create jest@latest +# or +pnpm create jest@latest +``` + +This will take you through a series of prompts to setup Jest for your project, including automatically creating a `jest.config.ts|js` file. + +Update your config file to use `next/jest`. This transformer has all the necessary configuration options for Jest to work with Next.js: + +```ts filename="jest.config.ts" switcher +import type { Config } from 'jest' +import nextJest from 'next/jest.js' + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +const config: Config = { + coverageProvider: 'v8', + testEnvironment: 'jsdom', + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.ts'], +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +export default createJestConfig(config) +``` + +```js filename="jest.config.js" switcher +const nextJest = require('next/jest') + +/** @type {import('jest').Config} */ +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +const config = { + coverageProvider: 'v8', + testEnvironment: 'jsdom', + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.ts'], +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +module.exports = createJestConfig(config) +``` + +Under the hood, `next/jest` is automatically configuring Jest for you, including: + +- Setting up `transform` using the [Next.js Compiler](/docs/architecture/nextjs-compiler) +- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`next/font`](/docs/pages/building-your-application/optimizing/fonts) +- Loading `.env` (and all variants) into `process.env` +- Ignoring `node_modules` from test resolving and transforms +- Ignoring `.next` from test resolving +- Loading `next.config.js` for flags that enable SWC transforms + +> **Good to know**: To test environment variables directly, load them manually in a separate setup script or in your `jest.config.ts` file. For more information, please see [Test Environment Variables](/docs/pages/building-your-application/configuring/environment-variables#test-environment-variables). + + + +## Setting up Jest (with Babel) + +If you opt out of the [Next.js Compiler](/docs/architecture/nextjs-compiler) and use Babel instead, you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. + +Here are the recommended options to configure Jest for Next.js: + +```js filename="jest.config.js" +module.exports = { + collectCoverage: true, + // on node 14.x coverage provider v8 offers good speed and more or less good report + coverageProvider: 'v8', + collectCoverageFrom: [ + '**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts', + '!**/node_modules/**', + '!/out/**', + '!/.next/**', + '!/*.config.js', + '!/coverage/**', + ], + moduleNameMapper: { + // Handle CSS imports (with CSS modules) + // https://jestjs.io/docs/webpack#mocking-css-modules + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + + // Handle CSS imports (without CSS modules) + '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', + + // Handle image imports + // https://jestjs.io/docs/webpack#handling-static-assets + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, + + // Handle module aliases + '^@/components/(.*)$': '/components/$1', + + // Handle @next/font + '@next/font/(.*)': `/__mocks__/nextFontMock.js`, + // Handle next/font + 'next/font/(.*)': `/__mocks__/nextFontMock.js`, + // Disable server-only + 'server-only': `/__mocks__/empty.js`, + }, + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.js'], + testPathIgnorePatterns: ['/node_modules/', '/.next/'], + testEnvironment: 'jsdom', + transform: { + // Use babel-jest to transpile tests with the next/babel preset + // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object + '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], + }, + transformIgnorePatterns: [ + '/node_modules/', + '^.+\\.module\\.(css|sass|scss)$', + ], +} +``` + +You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). We also recommend reviewing [`next/jest` configuration](https://github.com/vercel/next.js/blob/e02fe314dcd0ae614c65b505c6daafbdeebb920e/packages/next/src/build/jest/jest.ts) to see how Next.js configures Jest. + +### Handling stylesheets and image imports + +Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. + +Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: + +```js filename="__mocks__/fileMock.js" +module.exports = 'test-file-stub' +``` + +```js filename="__mocks__/styleMock.js" +module.exports = {} +``` + +For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). + +## Handling Fonts + +To handle fonts, create the `nextFontMock.js` file inside the `__mocks__` directory, and add the following configuration: + +```js filename="__mocks__/nextFontMock.js" +module.exports = new Proxy( + {}, + { + get: function getter() { + return () => ({ + className: 'className', + variable: 'variable', + style: { fontFamily: 'fontFamily' }, + }) + }, + } +) +``` + + + +## Optional: Handling Absolute Imports and Module Path Aliases + +If your project is using [Module Path Aliases](/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: + +```json filename="tsconfig.json or jsconfig.json" +{ + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "baseUrl": "./", + "paths": { + "@/components/*": ["components/*"] + } + } +} +``` + +```js filename="jest.config.js" +moduleNameMapper: { + // ... + '^@/components/(.*)$': '/components/$1', +} +``` + +## Optional: Extend Jest with custom matchers + +`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: + +```ts filename="jest.config.ts" switcher +setupFilesAfterEnv: ['/jest.setup.ts'] +``` + +```js filename="jest.config.js" switcher +setupFilesAfterEnv: ['/jest.setup.js'] +``` + +Then, inside `jest.setup.ts`, add the following import: + +```ts filename="jest.setup.ts" switcher +import '@testing-library/jest-dom' +``` + +```js filename="jest.setup.js" switcher +import '@testing-library/jest-dom' +``` + +> **Good to know:**[`extend-expect` was removed in `v6.0`](https://github.com/testing-library/jest-dom/releases/tag/v6.0.0), so if you are using `@testing-library/jest-dom` before version 6, you will need to import `@testing-library/jest-dom/extend-expect` instead. + +If you need to add more setup options before each test, you can add them to the `jest.setup.js` file above. + +## Add a test script to `package.json`: + +Finally, add a Jest `test` script to your `package.json` file: + +```json filename="package.json" highlight={6-7} +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "jest", + "test:watch": "jest --watch" + } +} +``` + +`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). + +### Creating your first test: + +Your project is now ready to run tests. Create a folder called `__tests__` in your project's root directory. + + + +For example, we can add a test to check if the `` component successfully renders a heading: + +```jsx filename="pages/index.js +export default function Home() { + return

Home

+} +``` + +```jsx filename="__tests__/index.test.js" +import '@testing-library/jest-dom' +import { render, screen } from '@testing-library/react' +import Home from '../pages/index' + +describe('Home', () => { + it('renders a heading', () => { + render() + + const heading = screen.getByRole('heading', { level: 1 }) + + expect(heading).toBeInTheDocument() + }) +}) +``` + + + + + +For example, we can add a test to check if the `` component successfully renders a heading: + +```jsx filename="app/page.js +import Link from 'next/link' + +export default async function Home() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```jsx filename="__tests__/page.test.jsx" +import '@testing-library/jest-dom' +import { render, screen } from '@testing-library/react' +import Page from '../app/page' + +describe('Page', () => { + it('renders a heading', () => { + render() + + const heading = screen.getByRole('heading', { level: 1 }) + + expect(heading).toBeInTheDocument() + }) +}) +``` + +
+ + + +Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes in your component: + + + +```jsx filename="__tests__/snapshot.js" +import { render } from '@testing-library/react' +import Home from '../pages/index' + +it('renders homepage unchanged', () => { + const { container } = render() + expect(container).toMatchSnapshot() +}) +``` + +> **Good to know**: Test files should not be included inside the Pages Router because any files inside the Pages Router are considered routes. + + + + + +```jsx filename="__tests__/snapshot.js" +import { render } from '@testing-library/react' +import Page from '../app/page' + +it('renders homepage unchanged', () => { + const { container } = render() + expect(container).toMatchSnapshot() +}) +``` + + + +## Running your tests + +Then, run the following command to run your tests: + +```bash filename="Terminal" +npm run test +# or +yarn test +# or +pnpm test +``` + +## Additional Resources + +For further reading, you may find these resources helpful: + +- [Next.js with Jest example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) +- [Jest Docs](https://jestjs.io/docs/getting-started) +- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) +- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. diff --git a/docs/02-app/01-building-your-application/08-testing/03-playwright.mdx b/docs/02-app/01-building-your-application/08-testing/03-playwright.mdx new file mode 100644 index 0000000000000..edb66aebec180 --- /dev/null +++ b/docs/02-app/01-building-your-application/08-testing/03-playwright.mdx @@ -0,0 +1,134 @@ +--- +title: Setting up Playwright with Next.js +nav: Playwright +description: Learn how to set up Playwright with Next.js for End-to-End (E2E) testing. +--- + +Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** testing. This guide will show you how to set up Playwright with Next.js and write your first tests. + + + +## Quickstart + +The fastest way to get started is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright configured. + +```bash filename="Terminal" +npx create-next-app@latest --example with-playwright with-playwright-app +``` + + + +## Manual setup + +To install Playwright, run the following command: + +```bash filename="Terminal" +npm init playwright +# or +yarn create playwright +# or +pnpm create playwright +``` + +This will take you through a series of prompts to setup and configure Playwright for your project, including adding a `playwright.config.ts` file. Please refer to the [Playwright installation guide](https://playwright.dev/docs/intro#installation) for the step-by-step guide. + +## Creating your first Playwright E2E test + +Create two new Next.js pages: + + + +```tsx filename="app/page.tsx" +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```tsx filename="app/about/page.tsx" +import Link from 'next/link' + +export default function Page() { + return ( +
+

About

+ Home +
+ ) +} +``` + + + +```tsx filename="pages/index.ts" +import Link from 'next/link' + +export default function Home() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```tsx filename="pages/about.ts" +import Link from 'next/link' + +export default function About() { + return ( +
+

About

+ Home +
+ ) +} +``` + +
+ +Then, add a test to verify that your navigation is working correctly: + +```ts filename="tests/example.spec.ts" +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('http://localhost:3000/') + // Find an element with the text 'About' and click on it + await page.click('text=About') + // The new URL should be "/about" (baseURL is used there) + await expect(page).toHaveURL('http://localhost:3000/about') + // The new page should contain an h1 with "About" + await expect(page.locator('h1')).toContainText('About') +}) +``` + +> **Good to know**: +> +> You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` [configuration file](https://playwright.dev/docs/test-configuration). + +### Running your Playwright tests + +Playwright will simulate a user navigating your application using three browsers: Chromium, Firefox and Webkit, this requires your Next.js server to be running. We recommend running your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npx playwright test` in another terminal window to run the Playwright tests. + +> **Good to know**: Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-webserver/) feature to let Playwright start the development server and wait until it's fully available. + +### Running Playwright on Continuous Integration (CI) + +Playwright will by default run your tests in the [headless mode](https://playwright.dev/docs/ci#running-headed). To install all the Playwright dependencies, run `npx playwright install-deps`. + +You can learn more about Playwright and Continuous Integration from these resources: + +- [Next.js with Playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright) +- [Playwright on your CI provider](https://playwright.dev/docs/ci) +- [Playwright Discord](https://discord.com/invite/playwright-807756831384403968) diff --git a/docs/02-app/01-building-your-application/08-testing/04-cypress.mdx b/docs/02-app/01-building-your-application/08-testing/04-cypress.mdx new file mode 100644 index 0000000000000..88e7e235d7448 --- /dev/null +++ b/docs/02-app/01-building-your-application/08-testing/04-cypress.mdx @@ -0,0 +1,288 @@ +--- +title: Setting up Cypress with Next.js +nav: Cypress +description: Learn how to set up Cypress with Next.js for End-to-End (E2E) and Component Testing. +--- + +[Cypress](https://www.cypress.io/) is a test runner used for **End-to-End (E2E)** and **Component Testing**. This page will show you how to set up Cypress with Next.js and write your first tests. + +> **Warning:** +> +> - For **component testing**, Cypress currently does not support [Next.js version 14](https://github.com/cypress-io/cypress/issues/28185) and `async` Server Components. These issues are being tracked. Fow now, component testing works with Next.js version 13, and we recommend E2E testing for `async` Server Components. +> - Cypress currently does not support [TypeScript version 5](https://github.com/cypress-io/cypress/issues/27731) with `moduleResolution:"bundler"`. This issue is being tracked. + + + +## Quickstart + +You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. + +```bash filename="Terminal" +npx create-next-app@latest --example with-cypress with-cypress-app +``` + + + +## Manual setup + +To manually set up Cypress, install `cypress` as a dev dependency: + +```bash filename="Terminal" +npm install -D cypress +# or +yarn add -D cypress +# or +pnpm install -D cypress +``` + +Add the Cypress `open` command to the `package.json` scripts field: + +```json filename="package.json" +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "cypress:open": "cypress open" + } +} +``` + +Run Cypress for the first time to open the Cypress testing suite: + +```bash filename="Terminal" +npm run cypress:open +``` + +You can choose to configure **E2E Testing** and/or **Component Testing**. Selecting any of these options will automatically create a `cypress.config.js` file and a `cypress` folder in your project. + +## Creating your first Cypress E2E test + +Ensure your `cypress.config.js` file has the following configuration: + +```ts filename="cypress.config.ts" +import { defineConfig } from 'cypress' + +export default defineConfig({ + e2e: { + setupNodeEvents(on, config) {}, + }, +}) +``` + +```js filename="cypress.config.js" +const { defineConfig } = require('cypress') + +module.exports = defineConfig({ + e2e: { + setupNodeEvents(on, config) {}, + }, +}) +``` + +Then, create two new Next.js files: + + + +```jsx filename="app/page.js" +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```jsx filename="app/about/page.js" +import Link from 'next/link' + +export default function Page() { + return ( +
+

About

+ Home +
+ ) +} +``` + +
+ + + +```jsx filename="pages/index.js" +import Link from 'next/link' + +export default function Home() { + return ( +
+

Home

+ About +
+ ) +} +``` + +```jsx filename="pages/about.js" +import Link from 'next/link' + +export default function About() { + return ( +
+

About

+ Home +
+ ) +} +``` + +
+ +Add a test to check your navigation is working correctly: + +```js filename="cypress/e2e/app.cy.js" +describe('Navigation', () => { + it('should navigate to the about page', () => { + // Start from the index page + cy.visit('http://localhost:3000/') + + // Find a link with an href attribute containing "about" and click it + cy.get('a[href*="about"]').click() + + // The new url should include "/about" + cy.url().should('include', '/about') + + // The new page should contain an h1 with "About" + cy.get('h1').contains('About') + }) +}) +``` + +### Running E2E Tests + +Cypress will simulate a user navigating your application, this requires your Next.js server to be running. We recommend running your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build && npm run start` to build your Next.js application, then run `npm run cypress:open` in another terminal window to start Cypress and run your E2E testing suite. + +> **Good to know:** +> +> - You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` by adding `baseUrl: 'http://localhost:3000'` to the `cypress.config.js` configuration file. +> - Alternatively, you can install the `start-server-and-test` package to run the Next.js production server in conjuction with Cypress. After installation, add `"test": "start-server-and-test start http://localhost:3000 cypress"` to your `package.json` scripts field. Remember to rebuild your application after new changes. + +## Creating your first Cypress component test + +Component tests build and mount a specific component without having to bundle your whole application or start a server. + +Select **Component Testing** in the Cypress app, then select **Next.js** as your front-end framework. A `cypress/component` folder will be created in your project, and a `cypress.config.js` file will be updated to enable component testing. + +Ensure your `cypress.config.js` file has the following configuration: + +```ts filename="cypress.config.ts" +import { defineConfig } from 'cypress' + +export default defineConfig({ + component: { + devServer: { + framework: 'next', + bundler: 'webpack', + }, + }, +}) +``` + +```js filename="cypress.config.js" +const { defineConfig } = require('cypress') + +module.exports = defineConfig({ + component: { + devServer: { + framework: 'next', + bundler: 'webpack', + }, + }, +}) +``` + +Assuming the same components from the previous section, add a test to validate a component is rendering the expected output: + + + +```tsx filename="cypress/component/about.cy.tsx" +import Page from '../../app/page' + +describe('', () => { + it('should render and display expected content', () => { + // Mount the React component for the Home page + cy.mount() + + // The new page should contain an h1 with "Home" + cy.get('h1').contains('Home') + + // Validate that a link with the expected URL is present + // Following the link is better suited to an E2E test + cy.get('a[href="/about"]').should('be.visible') + }) +}) +``` + + + + + +```jsx filename="cypress/component/about.cy.js" +import AboutPage from '../../pages/about' + +describe('', () => { + it('should render and display expected content', () => { + // Mount the React component for the About page + cy.mount() + + // The new page should contain an h1 with "About page" + cy.get('h1').contains('About') + + // Validate that a link with the expected URL is present + // *Following* the link is better suited to an E2E test + cy.get('a[href="/"]').should('be.visible') + }) +}) +``` + + + +> **Good to know**: +> +> - Cypress currently doesn't support component testing for `async` Server Components. We recommend using E2E testing. +> - Since component tests do not require a Next.js server, features like `` that rely on a server being available may not function out-of-the-box. + +### Running Component Tests + +Run `npm run cypress:open` in your terminal to start Cypress and run your component testing suite. + +## Continuous Integration (CI) + +In addition to interactive testing, you can also run Cypress headlessly using the `cypress run` command, which is better suited for CI environments: + +```json filename="package.json" +{ + "scripts": { + //... + "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"", + "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"", + "component": "cypress open --component", + "component:headless": "cypress run --component" + } +} +``` + +You can learn more about Cypress and Continuous Integration from these resources: + +- [Next.js with Cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) +- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) +- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) +- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) +- [Cypress Discord](https://discord.com/invite/cypress) diff --git a/docs/02-app/01-building-your-application/08-testing/index.mdx b/docs/02-app/01-building-your-application/08-testing/index.mdx new file mode 100644 index 0000000000000..51fa0c40333ca --- /dev/null +++ b/docs/02-app/01-building-your-application/08-testing/index.mdx @@ -0,0 +1,24 @@ +--- +title: Testing +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Vitest, and Jest. +--- + +In React and Next.js, there are a few different types of tests you can write, each with its own purpose and use cases. This page provides an overview of testing strategies and commonly used tools you can use. + +- **Unit testing** involves testing individual units (or blocks of code) in isolation. In React, units can be a single function, hook, or component. + - **Component testing** is a more focused version of unit testing where the primary subject of the tests is React components. This may involve testing how components are rendered, their interaction with props, and their behavior in response to user events. + - **Integration testing** involves testing how multiple units work together. This can be a combination of components, hooks, and functions. +- **End-to-End (E2E) Testing** involves testing user flows in an environment that simulates real user scenarios, like the browser. This means testing specific tasks (e.g. signup flow) in a production-like environment. +- **Snapshot testing** involves capturing the rendered output of a component and saving it to a snapshot file. When tests run, the current rendered output of the component is compared against the saved snapshot. Changes in the snapshot are used to indicate unexpected changes in behavior. + + + +## Async Server Components + +Since `async` Server Components are new to the React ecosystem, some tools may not yet fully support them. We recommend using **End-to-End Testing** over **Unit Testing** for `async` components. + + + +## Guides + +See the guides below to learn how to set up Next.js with these commonly used testing tools: diff --git a/docs/02-app/01-building-your-application/08-deploying/01-static-exports.mdx b/docs/02-app/01-building-your-application/09-deploying/01-static-exports.mdx similarity index 100% rename from docs/02-app/01-building-your-application/08-deploying/01-static-exports.mdx rename to docs/02-app/01-building-your-application/09-deploying/01-static-exports.mdx diff --git a/docs/02-app/01-building-your-application/08-deploying/index.mdx b/docs/02-app/01-building-your-application/09-deploying/index.mdx similarity index 100% rename from docs/02-app/01-building-your-application/08-deploying/index.mdx rename to docs/02-app/01-building-your-application/09-deploying/index.mdx diff --git a/docs/02-app/01-building-your-application/09-upgrading/01-codemods.mdx b/docs/02-app/01-building-your-application/10-upgrading/01-codemods.mdx similarity index 100% rename from docs/02-app/01-building-your-application/09-upgrading/01-codemods.mdx rename to docs/02-app/01-building-your-application/10-upgrading/01-codemods.mdx diff --git a/docs/02-app/01-building-your-application/09-upgrading/02-app-router-migration.mdx b/docs/02-app/01-building-your-application/10-upgrading/02-app-router-migration.mdx similarity index 100% rename from docs/02-app/01-building-your-application/09-upgrading/02-app-router-migration.mdx rename to docs/02-app/01-building-your-application/10-upgrading/02-app-router-migration.mdx diff --git a/docs/02-app/01-building-your-application/09-upgrading/03-version-14.mdx b/docs/02-app/01-building-your-application/10-upgrading/03-version-14.mdx similarity index 100% rename from docs/02-app/01-building-your-application/09-upgrading/03-version-14.mdx rename to docs/02-app/01-building-your-application/10-upgrading/03-version-14.mdx diff --git a/docs/02-app/01-building-your-application/09-upgrading/04-from-vite.mdx b/docs/02-app/01-building-your-application/10-upgrading/04-from-vite.mdx similarity index 100% rename from docs/02-app/01-building-your-application/09-upgrading/04-from-vite.mdx rename to docs/02-app/01-building-your-application/10-upgrading/04-from-vite.mdx diff --git a/docs/02-app/01-building-your-application/09-upgrading/index.mdx b/docs/02-app/01-building-your-application/10-upgrading/index.mdx similarity index 100% rename from docs/02-app/01-building-your-application/09-upgrading/index.mdx rename to docs/02-app/01-building-your-application/10-upgrading/index.mdx diff --git a/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx b/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx deleted file mode 100644 index b5dbf85eb8384..0000000000000 --- a/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx +++ /dev/null @@ -1,537 +0,0 @@ ---- -title: Testing -description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library. ---- - -
- Examples - -- [Next.js with Cypress](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) -- [Next.js with Playwright](https://github.com/vercel/next.js/tree/canary/examples/with-playwright) -- [Next.js with Jest and React Testing Library](https://github.com/vercel/next.js/tree/canary/examples/with-jest) -- [Next.js with Vitest](https://github.com/vercel/next.js/tree/canary/examples/with-vitest) - -
- -Learn how to set up Next.js with commonly used testing tools: [Cypress](#cypress), [Playwright](#playwright), and [Jest with React Testing Library](#jest-and-react-testing-library). - -## Cypress - -Cypress is a test runner used for **End-to-End (E2E)** and **Component Testing**. - -### Quickstart - -You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. - -```bash filename="Terminal" -npx create-next-app@latest --example with-cypress with-cypress-app -``` - -### Manual setup - -To get started with Cypress, install the `cypress` package: - -```bash filename="Terminal" -npm install --save-dev cypress -``` - -Add Cypress to the `package.json` scripts field: - -```json filename="package.json" -{ - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "cypress:open": "cypress open" - } -} -``` - -Run Cypress for the first time to generate examples that use their recommended folder structure: - -```bash filename="Terminal" -npm run cypress:open -``` - -You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. - -### Should I use E2E or Component Tests? - -The [Cypress docs contain a guide](https://docs.cypress.io/guides/core-concepts/testing-types) on the difference between these two types of tests and when it is appropriate to use each. - -### Creating your first Cypress E2E test - -Assuming the following two Next.js pages: - -```jsx filename="pages/index.js" -import Link from 'next/link' - -export default function Home() { - return ( - - ) -} -``` - -```jsx filename="pages/about.js" -export default function About() { - return ( -
-

About Page

- Homepage -
- ) -} -``` - -Add a test to check your navigation is working correctly: - -```js filename="cypress/e2e/app.cy.js" -describe('Navigation', () => { - it('should navigate to the about page', () => { - // Start from the index page - cy.visit('http://localhost:3000/') - - // Find a link with an href attribute containing "about" and click it - cy.get('a[href*="about"]').click() - - // The new url should include "/about" - cy.url().should('include', '/about') - - // The new page should contain an h1 with "About page" - cy.get('h1').contains('About Page') - }) -}) -``` - -You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `baseUrl: 'http://localhost:3000'` to the `cypress.config.js` configuration file. - -### Creating your first Cypress component test - -Component tests build and mount a specific component without having to bundle your whole application or launch a server. This allows for more performant tests that still provide visual feedback and the same API used for Cypress E2E tests. - -> **Good to know**: Since component tests do not launch a Next.js server, capabilities like `` and `getServerSideProps` which rely on a server being available will not function out-of-the-box. See the [Cypress Next.js docs](https://docs.cypress.io/guides/component-testing/react/overview#Nextjs) for examples of getting these features working within component tests. - -Assuming the same components from the previous section, add a test to validate a component is rendering the expected output: - -```jsx filename="pages/about.cy.js" -import AboutPage from './about.js' - -describe('', () => { - it('should render and display expected content', () => { - // Mount the React component for the About page - cy.mount() - - // The new page should contain an h1 with "About page" - cy.get('h1').contains('About Page') - - // Validate that a link with the expected URL is present - // *Following* the link is better suited to an E2E test - cy.get('a[href="/"]').should('be.visible') - }) -}) -``` - -### Running your Cypress tests - -#### E2E Tests - -Since Cypress E2E tests are testing a real Next.js application they require the Next.js server to be running prior to starting Cypress. We recommend running your tests against your production code to more closely resemble how your application will behave. - -Run `npm run build` and `npm run start`, then run `npm run cypress -- --e2e` in another terminal window to start Cypress and run your E2E testing suite. - -> **Good to know**: Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjunction with Cypress. Remember to rebuild your application after new changes. - -#### Component Tests - -Run `npm run cypress -- --component` to start Cypress and execute your component testing suite. - -### Getting ready for Continuous Integration (CI) - -You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: - -```json filename="package.json" -{ - "scripts": { - //... - "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"", - "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"", - "component": "cypress open --component", - "component:headless": "cypress run --component" - } -} -``` - -You can learn more about Cypress and Continuous Integration from these resources: - -- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) -- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) -- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) -- [Cypress Discord](https://discord.com/invite/cypress) - -## Playwright - -Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** and **Integration** tests across all platforms. - -### Quickstart - -The fastest way to get started is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright all set up. - -```bash filename="Terminal" -npx create-next-app@latest --example with-playwright with-playwright-app -``` - -### Manual setup - -You can also use `npm init playwright` to add Playwright to an existing `NPM` project. - -To manually get started with Playwright, install the `@playwright/test` package: - -```bash filename="Terminal" -npm install --save-dev @playwright/test -``` - -Add Playwright to the `package.json` scripts field: - -```json filename="package.json" -{ - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "test:e2e": "playwright test" - } -} -``` - -### Creating your first Playwright end-to-end test - -Assuming the following two Next.js pages: - -```jsx filename="pages/index.js" -import Link from 'next/link' - -export default function Home() { - return ( - - ) -} -``` - -```jsx filename="pages/about.js" -export default function About() { - return ( -
-

About Page

-
- ) -} -``` - -Add a test to verify that your navigation is working correctly: - -```ts filename="e2e/example.spec.ts" switcher -import { test, expect } from '@playwright/test' - -test('should navigate to the about page', async ({ page }) => { - // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) - await page.goto('http://localhost:3000/') - // Find an element with the text 'About Page' and click on it - await page.click('text=About') - // The new URL should be "/about" (baseURL is used there) - await expect(page).toHaveURL('http://localhost:3000/about') - // The new page should contain an h1 with "About Page" - await expect(page.locator('h1')).toContainText('About Page') -}) -``` - -```js filename="e2e/example.spec.js" switcher -import { test, expect } from '@playwright/test' - -test('should navigate to the about page', async ({ page }) => { - // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) - await page.goto('http://localhost:3000/') - // Find an element with the text 'About Page' and click on it - await page.click('text=About') - // The new URL should be "/about" (baseURL is used there) - await expect(page).toHaveURL('http://localhost:3000/about') - // The new page should contain an h1 with "About Page" - await expect(page.locator('h1')).toContainText('About Page') -}) -``` - -You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` configuration file. - -### Running your Playwright tests - -Since Playwright is testing a real Next.js application, it requires the Next.js server to be running prior to starting Playwright. It is recommended to run your tests against your production code to more closely resemble how your application will behave. - -Run `npm run build` and `npm run start`, then run `npm run test:e2e` in another terminal window to run the Playwright tests. - -> **Good to know**: Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-webserver/) feature to let Playwright start the development server and wait until it's fully available. - -### Running Playwright on Continuous Integration (CI) - -Playwright will by default run your tests in the [headless mode](https://playwright.dev/docs/ci#running-headed). To install all the Playwright dependencies, run `npx playwright install-deps`. - -You can learn more about Playwright and Continuous Integration from these resources: - -- [Getting started with Playwright](https://playwright.dev/docs/intro) -- [Use a development server](https://playwright.dev/docs/test-webserver/) -- [Playwright on your CI provider](https://playwright.dev/docs/ci) -- [Playwright Discord](https://discord.com/invite/playwright-807756831384403968) - -## Jest and React Testing Library - -Jest and React Testing Library are frequently used together for **Unit Testing**. There are three ways you can start using Jest within your Next.js application: - -1. Using one of our [quickstart examples](#quickstart-2) -2. With the [Next.js Rust Compiler](#setting-up-jest-with-the-rust-compiler) -3. With [Babel](#setting-up-jest-with-babel) - -The following sections will go through how you can set up Jest with each of these options: - -### Quickstart - -You can use `create-next-app` with the [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started with Jest and React Testing Library: - -```bash filename="Terminal" -npx create-next-app@latest --example with-jest with-jest-app -``` - -### Setting up Jest (with the Rust Compiler) - -Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. - -To set up Jest, install `jest`, `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`: - -```bash filename="Terminal" -npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom -``` - -Create a `jest.config.mjs` file in your project's root directory and add the following: - -```js filename="jest.config.mjs" -import nextJest from 'next/jest.js' - -const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files in your test environment - dir: './', -}) - -// Add any custom config to be passed to Jest -/** @type {import('jest').Config} */ -const config = { - // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.js'], - - testEnvironment: 'jest-environment-jsdom', -} - -// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async -export default createJestConfig(config) -``` - -Under the hood, `next/jest` is automatically configuring Jest for you, including: - -- Setting up `transform` using [SWC](/docs/architecture/nextjs-compiler) -- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`next/font`](/docs/pages/building-your-application/optimizing/fonts) -- Loading `.env` (and all variants) into `process.env` -- Ignoring `node_modules` from test resolving and transforms -- Ignoring `.next` from test resolving -- Loading `next.config.js` for flags that enable SWC transforms - -> **Good to know**: To test environment variables directly, load them manually in a separate setup script or in your `jest.config.js` file. For more information, please see [Test Environment Variables](/docs/pages/building-your-application/configuring/environment-variables#test-environment-variables). - -### Setting up Jest (with Babel) - -If you opt out of the [Rust Compiler](/docs/architecture/nextjs-compiler), you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. - -Here are the recommended options to configure Jest for Next.js: - -```js filename="jest.config.js" -module.exports = { - collectCoverage: true, - // on node 14.x coverage provider v8 offers good speed and more or less good report - coverageProvider: 'v8', - collectCoverageFrom: [ - '**/*.{js,jsx,ts,tsx}', - '!**/*.d.ts', - '!**/node_modules/**', - '!/out/**', - '!/.next/**', - '!/*.config.js', - '!/coverage/**', - ], - moduleNameMapper: { - // Handle CSS imports (with CSS modules) - // https://jestjs.io/docs/webpack#mocking-css-modules - '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', - - // Handle CSS imports (without CSS modules) - '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', - - // Handle image imports - // https://jestjs.io/docs/webpack#handling-static-assets - '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, - - // Handle module aliases - '^@/components/(.*)$': '/components/$1', - }, - // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.js'], - testPathIgnorePatterns: ['/node_modules/', '/.next/'], - testEnvironment: 'jsdom', - transform: { - // Use babel-jest to transpile tests with the next/babel preset - // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object - '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], - }, - transformIgnorePatterns: [ - '/node_modules/', - '^.+\\.module\\.(css|sass|scss)$', - ], -} -``` - -You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). - -**Handling stylesheets and image imports** - -Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: - -```js filename="__mocks__/fileMock.js" -module.exports = { - src: '/img.jpg', - height: 24, - width: 24, - blurDataURL: 'data:image/png;base64,imagedata', -} -``` - -```js filename="__mocks__/styleMock.js" -module.exports = {} -``` - -For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). - -**Optional: Extend Jest with custom matchers** - -`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: - -```js filename="jest.config.js" -setupFilesAfterEnv: ['/jest.setup.js'] -``` - -Then, inside `jest.setup.js`, add the following import: - -```js filename="jest.setup.js" -import '@testing-library/jest-dom' -``` - -> [`extend-expect` was removed in `v6.0`](https://github.com/testing-library/jest-dom/releases/tag/v6.0.0), so if you are using `@testing-library/jest-dom` before version 6, you will need to import `@testing-library/jest-dom/extend-expect` instead. - -If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. - -**Optional: Absolute Imports and Module Path Aliases** - -If your project is using [Module Path Aliases](/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: - -```json filename="tsconfig.json or jsconfig.json" -{ - "compilerOptions": { - "module": "esnext", - "moduleResolution": "node", - "baseUrl": "./", - "paths": { - "@/components/*": ["components/*"] - } - } -} -``` - -```js filename="jest.config.js" -moduleNameMapper: { - '^@/components/(.*)$': '/components/$1', -} -``` - -### Creating your tests: - -**Add a test script to package.json** - -Add the Jest executable in watch mode to the `package.json` scripts: - -```json filename="package.json" -{ - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "test": "jest --watch" - } -} -``` - -`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). - -**Create your first tests** - -Your project is now ready to run tests. Follow Jest's convention by adding tests to the `__tests__` folder in your project's root directory. - -For example, we can add a test to check if the `` component successfully renders a heading: - -```jsx filename="__tests__/index.test.js" -import { render, screen } from '@testing-library/react' -import Home from '../pages/index' -import '@testing-library/jest-dom' - -describe('Home', () => { - it('renders a heading', () => { - render() - - const heading = screen.getByRole('heading', { - name: /welcome to next\.js!/i, - }) - - expect(heading).toBeInTheDocument() - }) -}) -``` - -Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `` component: - -```jsx filename="__tests__/snapshot.js" -import { render } from '@testing-library/react' -import Home from '../pages/index' - -it('renders homepage unchanged', () => { - const { container } = render() - expect(container).toMatchSnapshot() -}) -``` - -> **Good to know**: Test files should not be included inside the Pages Router because any files inside the Pages Router are considered routes. - -**Running your test suite** - -Run `npm run test` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. - -For further reading, you may find these resources helpful: - -- [Jest Docs](https://jestjs.io/docs/getting-started) -- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) -- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. - -## Community Packages and Examples - -The Next.js community has created packages and articles you may find helpful: - -- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. -- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. diff --git a/docs/03-pages/01-building-your-application/07-testing/01-vitest.mdx b/docs/03-pages/01-building-your-application/07-testing/01-vitest.mdx new file mode 100644 index 0000000000000..14ec92b7209cd --- /dev/null +++ b/docs/03-pages/01-building-your-application/07-testing/01-vitest.mdx @@ -0,0 +1,8 @@ +--- +title: Setting up Vitest with Next.js +nav: Jest +description: Learn how to set up Next.js with Vitest and React Testing Library - two popular unit testing libraries. +source: app/building-your-application/testing/vitest +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/07-testing/02-jest.mdx b/docs/03-pages/01-building-your-application/07-testing/02-jest.mdx new file mode 100644 index 0000000000000..83e6593b3d31f --- /dev/null +++ b/docs/03-pages/01-building-your-application/07-testing/02-jest.mdx @@ -0,0 +1,8 @@ +--- +title: Setting up Jest with Next.js +nav: Jest +description: Learn how to set up Next.js with Jest for Unit Testing. +source: app/building-your-application/testing/jest +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/07-testing/03-playwright.mdx b/docs/03-pages/01-building-your-application/07-testing/03-playwright.mdx new file mode 100644 index 0000000000000..79f5313141b78 --- /dev/null +++ b/docs/03-pages/01-building-your-application/07-testing/03-playwright.mdx @@ -0,0 +1,8 @@ +--- +title: Setting up Playwright with Next.js +nav: Playwright +description: Learn how to set up Next.js with Playwright for End-to-End (E2E) and Integration testing. +source: app/building-your-application/testing/playwright +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/07-testing/04-cypress.mdx b/docs/03-pages/01-building-your-application/07-testing/04-cypress.mdx new file mode 100644 index 0000000000000..7eb9761c191a5 --- /dev/null +++ b/docs/03-pages/01-building-your-application/07-testing/04-cypress.mdx @@ -0,0 +1,8 @@ +--- +title: Setting up Cypress with Next.js +nav: Cypress +description: Learn how to set up Next.js with Cypress for End-to-End (E2E) and Component Testing. +source: app/building-your-application/testing/cypress +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/07-testing/index.mdx b/docs/03-pages/01-building-your-application/07-testing/index.mdx new file mode 100644 index 0000000000000..ac033a4bb07c8 --- /dev/null +++ b/docs/03-pages/01-building-your-application/07-testing/index.mdx @@ -0,0 +1,7 @@ +--- +title: Testing +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Vitest, and Jest. +source: app/building-your-application/testing +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/07-deploying/01-production-checklist.mdx b/docs/03-pages/01-building-your-application/08-deploying/01-production-checklist.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/07-deploying/01-production-checklist.mdx rename to docs/03-pages/01-building-your-application/08-deploying/01-production-checklist.mdx diff --git a/docs/03-pages/01-building-your-application/07-deploying/02-static-exports.mdx b/docs/03-pages/01-building-your-application/08-deploying/02-static-exports.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/07-deploying/02-static-exports.mdx rename to docs/03-pages/01-building-your-application/08-deploying/02-static-exports.mdx diff --git a/docs/03-pages/01-building-your-application/07-deploying/03-multi-zones.mdx b/docs/03-pages/01-building-your-application/08-deploying/03-multi-zones.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/07-deploying/03-multi-zones.mdx rename to docs/03-pages/01-building-your-application/08-deploying/03-multi-zones.mdx diff --git a/docs/03-pages/01-building-your-application/07-deploying/04-ci-build-caching.mdx b/docs/03-pages/01-building-your-application/08-deploying/04-ci-build-caching.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/07-deploying/04-ci-build-caching.mdx rename to docs/03-pages/01-building-your-application/08-deploying/04-ci-build-caching.mdx diff --git a/docs/03-pages/01-building-your-application/07-deploying/index.mdx b/docs/03-pages/01-building-your-application/08-deploying/index.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/07-deploying/index.mdx rename to docs/03-pages/01-building-your-application/08-deploying/index.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/01-codemods.mdx b/docs/03-pages/01-building-your-application/09-upgrading/01-codemods.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/01-codemods.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/01-codemods.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/02-app-router-migration.mdx b/docs/03-pages/01-building-your-application/09-upgrading/02-app-router-migration.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/02-app-router-migration.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/02-app-router-migration.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/03-version-14.mdx b/docs/03-pages/01-building-your-application/09-upgrading/03-version-14.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/03-version-14.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/03-version-14.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/04-version-13.mdx b/docs/03-pages/01-building-your-application/09-upgrading/04-version-13.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/04-version-13.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/04-version-13.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/05-version-12.mdx b/docs/03-pages/01-building-your-application/09-upgrading/05-version-12.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/05-version-12.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/05-version-12.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/06-version-11.mdx b/docs/03-pages/01-building-your-application/09-upgrading/06-version-11.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/06-version-11.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/06-version-11.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/07-version-10.mdx b/docs/03-pages/01-building-your-application/09-upgrading/07-version-10.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/07-version-10.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/07-version-10.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/08-version-9.mdx b/docs/03-pages/01-building-your-application/09-upgrading/08-version-9.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/08-version-9.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/08-version-9.mdx diff --git a/docs/03-pages/01-building-your-application/08-upgrading/index.mdx b/docs/03-pages/01-building-your-application/09-upgrading/index.mdx similarity index 100% rename from docs/03-pages/01-building-your-application/08-upgrading/index.mdx rename to docs/03-pages/01-building-your-application/09-upgrading/index.mdx diff --git a/examples/with-cypress/src/app/about/page.tsx b/examples/with-cypress/app/about/page.tsx similarity index 82% rename from examples/with-cypress/src/app/about/page.tsx rename to examples/with-cypress/app/about/page.tsx index ed9c3282df302..97a39ab3f32bb 100644 --- a/examples/with-cypress/src/app/about/page.tsx +++ b/examples/with-cypress/app/about/page.tsx @@ -1,5 +1,5 @@ import AboutComponent from '../../components/about-component' -import styles from '../Home.module.css' +import styles from '../../styles/Home.module.css' export default function About() { return ( diff --git a/examples/with-cypress/src/app/layout.tsx b/examples/with-cypress/app/layout.tsx similarity index 72% rename from examples/with-cypress/src/app/layout.tsx rename to examples/with-cypress/app/layout.tsx index 393fdcb7e5590..1736211d598b4 100644 --- a/examples/with-cypress/src/app/layout.tsx +++ b/examples/with-cypress/app/layout.tsx @@ -1,13 +1,16 @@ import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import './globals.css' +import '../styles/globals.css' import { ReactNode } from 'react' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { - title: 'Nextjs with cypress', - description: 'Nextjs with cypress', + title: 'Next.js with Cypress', + description: 'Next.js with Cypress', + icons: { + icon: '/favicon.ico', + }, } export default function RootLayout({ children }: { children: ReactNode }) { diff --git a/examples/with-cypress/src/app/page.tsx b/examples/with-cypress/app/page.tsx similarity index 89% rename from examples/with-cypress/src/app/page.tsx rename to examples/with-cypress/app/page.tsx index b3f7f531e9d12..c87503b776ba6 100644 --- a/examples/with-cypress/src/app/page.tsx +++ b/examples/with-cypress/app/page.tsx @@ -1,17 +1,10 @@ -import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' -import styles from './Home.module.css' +import styles from '../styles/Home.module.css' export default function Home() { return (
- - Create Next App - - - -

Welcome to Next.js! diff --git a/examples/with-cypress/src/components/about-component.cy.tsx b/examples/with-cypress/components/about-component.cy.tsx similarity index 84% rename from examples/with-cypress/src/components/about-component.cy.tsx rename to examples/with-cypress/components/about-component.cy.tsx index 55169f3a1a34c..54a31e5c130ce 100644 --- a/examples/with-cypress/src/components/about-component.cy.tsx +++ b/examples/with-cypress/components/about-component.cy.tsx @@ -1,4 +1,4 @@ -import AboutComponent from '../../src/components/about-component' +import AboutComponent from './components/about-component' /* eslint-disable */ // Disable ESLint to prevent failing linting inside the Next.js repo. // If you're using ESLint on your project, we recommend installing the ESLint Cypress plugin instead: @@ -6,7 +6,7 @@ import AboutComponent from '../../src/components/about-component' // Cypress Component Test describe('', () => { - it('should render and display expected content', () => { + it('should render and display expected content in a Client Component', () => { // Mount the React component for the About page cy.mount() diff --git a/examples/with-cypress/src/components/about-component.tsx b/examples/with-cypress/components/about-component.tsx similarity index 80% rename from examples/with-cypress/src/components/about-component.tsx rename to examples/with-cypress/components/about-component.tsx index 1b27af4866c6c..98ee2a1f7f76e 100644 --- a/examples/with-cypress/src/components/about-component.tsx +++ b/examples/with-cypress/components/about-component.tsx @@ -1,6 +1,8 @@ +'use client' + import Link from 'next/link' import React from 'react' -import styles from '../app/Home.module.css' +import styles from '../styles/Home.module.css' export default function AboutComponent() { return ( diff --git a/examples/with-cypress/cypress.config.ts b/examples/with-cypress/cypress.config.ts index b840962a64a4f..d2d191ac500bb 100644 --- a/examples/with-cypress/cypress.config.ts +++ b/examples/with-cypress/cypress.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'cypress' export default defineConfig({ e2e: { - baseUrl: 'http://localhost:3000', + setupNodeEvents(on, config) {}, }, component: { devServer: { diff --git a/examples/with-cypress/cypress/e2e/app.cy.ts b/examples/with-cypress/cypress/e2e/app.cy.ts index 18258126fb627..f30f800940875 100644 --- a/examples/with-cypress/cypress/e2e/app.cy.ts +++ b/examples/with-cypress/cypress/e2e/app.cy.ts @@ -7,7 +7,7 @@ describe('Navigation', () => { it('should navigate to the about page', () => { // Start from the index page - cy.visit('http://localhost:3000/') + cy.visit('http://localhost:3000') // Find a link with an href attribute containing "about" and click it cy.get('a[href*="about"]').click() diff --git a/examples/with-cypress/cypress/e2e/pages.cy.ts b/examples/with-cypress/cypress/e2e/pages.cy.ts new file mode 100644 index 0000000000000..c22d2c7cd99fb --- /dev/null +++ b/examples/with-cypress/cypress/e2e/pages.cy.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ +// Disable ESLint to prevent failing linting inside the Next.js repo. +// If you're using ESLint on your project, we recommend installing the ESLint Cypress plugin instead: +// https://github.com/cypress-io/eslint-plugin-cypress + +describe('Navigation', () => { + it('should navigate to the about page', () => { + // Start from the index page + cy.visit('http://localhost:3000/home') + + // Find a link with an href attribute containing "about" and click it + cy.get('a[href*="about"]').click() + + // The new url should include "/home/about" + cy.url().should('include', '/home/about') + + // The new page should contain an h1 with "About" + cy.get('h1').contains('About') + }) +}) diff --git a/examples/with-playwright/pages/_app.js b/examples/with-cypress/pages/_app.tsx similarity index 100% rename from examples/with-playwright/pages/_app.js rename to examples/with-cypress/pages/_app.tsx diff --git a/examples/with-cypress/pages/home/about.tsx b/examples/with-cypress/pages/home/about.tsx new file mode 100644 index 0000000000000..5300559f8c863 --- /dev/null +++ b/examples/with-cypress/pages/home/about.tsx @@ -0,0 +1,10 @@ +import Link from 'next/link' + +export default function About() { + return ( +
+

About

+ Home +
+ ) +} diff --git a/examples/with-cypress/pages/home/index.tsx b/examples/with-cypress/pages/home/index.tsx new file mode 100644 index 0000000000000..b8466909837d3 --- /dev/null +++ b/examples/with-cypress/pages/home/index.tsx @@ -0,0 +1,10 @@ +import Link from 'next/link' + +export default function Home() { + return ( +
+

Home

+ About +
+ ) +} diff --git a/examples/with-cypress/src/app/Home.module.css b/examples/with-cypress/styles/Home.module.css similarity index 100% rename from examples/with-cypress/src/app/Home.module.css rename to examples/with-cypress/styles/Home.module.css diff --git a/examples/with-cypress/src/app/globals.css b/examples/with-cypress/styles/globals.css similarity index 100% rename from examples/with-cypress/src/app/globals.css rename to examples/with-cypress/styles/globals.css diff --git a/examples/with-cypress/tsconfig.json b/examples/with-cypress/tsconfig.json index b6ba42e2da28d..0eae8b96e9f90 100644 --- a/examples/with-cypress/tsconfig.json +++ b/examples/with-cypress/tsconfig.json @@ -18,7 +18,8 @@ { "name": "next" } - ] + ], + "strictNullChecks": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] diff --git a/examples/with-jest/.eslintrc.json b/examples/with-jest/.eslintrc.json deleted file mode 100644 index 4f67f8cbc08db..0000000000000 --- a/examples/with-jest/.eslintrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "root": true, - "extends": ["next/core-web-vitals"], - "plugins": ["testing-library"], - "overrides": [ - // Only uses Testing Library lint rules in test files - { - "files": [ - "**/__tests__/**/*.[jt]s?(x)", - "**/?(*.)+(spec|test).[jt]s?(x)" - ], - "extends": ["plugin:testing-library/react"] - } - ] -} diff --git a/examples/with-jest/app/client/page.test.tsx b/examples/with-jest/app/client/page.test.tsx deleted file mode 100644 index 5120843e0a0a7..0000000000000 --- a/examples/with-jest/app/client/page.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @jest-environment jsdom - */ -import { render, screen } from '@testing-library/react' -import ClientComponent from './page' - -it('App Router: Works with Client Components', () => { - render() - expect(screen.getByRole('heading')).toHaveTextContent('Client Component') -}) diff --git a/examples/with-jest/app/client/page.tsx b/examples/with-jest/app/client/page.tsx deleted file mode 100644 index 63a6627147bce..0000000000000 --- a/examples/with-jest/app/client/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -'use client' - -export default function ClientComponent() { - return

Client Component

-} diff --git a/examples/with-jest/app/component.test.tsx b/examples/with-jest/app/counter.test.tsx similarity index 85% rename from examples/with-jest/app/component.test.tsx rename to examples/with-jest/app/counter.test.tsx index afdf088e3f0ea..3c7c59fd3ae6a 100644 --- a/examples/with-jest/app/component.test.tsx +++ b/examples/with-jest/app/counter.test.tsx @@ -2,10 +2,10 @@ * @jest-environment jsdom */ import { fireEvent, render, screen } from '@testing-library/react' -import Component from './component' +import Counter from './counter' it('App Router: Works with Client Components (React State)', () => { - render() + render() expect(screen.getByRole('heading')).toHaveTextContent('0') fireEvent.click(screen.getByRole('button')) expect(screen.getByRole('heading')).toHaveTextContent('1') diff --git a/examples/with-vitest/app/component.tsx b/examples/with-jest/app/counter.tsx similarity index 94% rename from examples/with-vitest/app/component.tsx rename to examples/with-jest/app/counter.tsx index 8430160b348a9..e383b403147b9 100644 --- a/examples/with-vitest/app/component.tsx +++ b/examples/with-jest/app/counter.tsx @@ -1,3 +1,5 @@ +'use client' + import { useState } from 'react' export default function Counter() { diff --git a/examples/with-jest/app/rsc/page.test.tsx b/examples/with-jest/app/page.test.tsx similarity index 100% rename from examples/with-jest/app/rsc/page.test.tsx rename to examples/with-jest/app/page.test.tsx diff --git a/examples/with-jest/app/rsc/page.tsx b/examples/with-jest/app/page.tsx similarity index 84% rename from examples/with-jest/app/rsc/page.tsx rename to examples/with-jest/app/page.tsx index bea3626bc66ff..5175cc603bd58 100644 --- a/examples/with-jest/app/rsc/page.tsx +++ b/examples/with-jest/app/page.tsx @@ -1,5 +1,3 @@ -import 'server-only' - export const metadata = { title: 'App Router', } diff --git a/examples/with-jest/app/utils/add.test.ts b/examples/with-jest/app/utils/add.test.ts new file mode 100644 index 0000000000000..753f075ab4741 --- /dev/null +++ b/examples/with-jest/app/utils/add.test.ts @@ -0,0 +1,5 @@ +import { add } from './add' + +test('Test functions that import server-only', () => { + expect(add(1, 2)).toBe(3) +}) diff --git a/examples/with-jest/app/utils/add.ts b/examples/with-jest/app/utils/add.ts new file mode 100644 index 0000000000000..260a4648004d4 --- /dev/null +++ b/examples/with-jest/app/utils/add.ts @@ -0,0 +1,5 @@ +import 'server-only' + +export function add(a: number, b: number) { + return a + b +} diff --git a/examples/with-jest/jest.config.js b/examples/with-jest/jest.config.js index fd04a1cea7b71..6e2155e610e0c 100644 --- a/examples/with-jest/jest.config.js +++ b/examples/with-jest/jest.config.js @@ -8,7 +8,7 @@ const createJestConfig = nextJest({ // Add any custom config to be passed to Jest const customJestConfig = { setupFilesAfterEnv: ['/jest.setup.js'], - testEnvironment: 'jest-environment-jsdom', + testEnvironment: 'jsdom', } // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async diff --git a/examples/with-jest/package.json b/examples/with-jest/package.json index 2f4be6c38f4ac..b2cb24577c0d4 100644 --- a/examples/with-jest/package.json +++ b/examples/with-jest/package.json @@ -16,7 +16,6 @@ "devDependencies": { "@testing-library/jest-dom": "6.1.2", "@testing-library/react": "14.0.0", - "@testing-library/user-event": "14.4.3", "@types/jest": "29.5.5", "@types/react": "18.2.8", "jest": "29.6.4", diff --git a/examples/with-jest/pages/index.tsx b/examples/with-jest/pages/home/index.tsx similarity index 99% rename from examples/with-jest/pages/index.tsx rename to examples/with-jest/pages/home/index.tsx index c30337b4adc11..98f5bd86ecbdb 100644 --- a/examples/with-jest/pages/index.tsx +++ b/examples/with-jest/pages/home/index.tsx @@ -1,6 +1,5 @@ import Head from 'next/head' import Image from 'next/image' - import styles from '@/pages/index.module.css' export default function Home() { diff --git a/examples/with-jest/tsconfig.json b/examples/with-jest/tsconfig.json index 1e100d941f035..4c98b6f70dc15 100644 --- a/examples/with-jest/tsconfig.json +++ b/examples/with-jest/tsconfig.json @@ -9,7 +9,7 @@ "noEmit": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", @@ -19,8 +19,19 @@ "@/components/*": ["components/*"], "@/pages/*": ["pages/*"], "@/styles/*": ["styles/*"] - } + }, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types.d.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "types.d.ts", + ".next/types/**/*.ts" + ], "exclude": ["node_modules"] } diff --git a/examples/with-playwright/app/about/page.tsx b/examples/with-playwright/app/about/page.tsx new file mode 100644 index 0000000000000..64d02da08da2a --- /dev/null +++ b/examples/with-playwright/app/about/page.tsx @@ -0,0 +1,10 @@ +import Link from 'next/link' + +export default function Page() { + return ( +
+

About

+ Home +
+ ) +} diff --git a/examples/with-playwright/app/layout.tsx b/examples/with-playwright/app/layout.tsx new file mode 100644 index 0000000000000..dbce4ea8e3aeb --- /dev/null +++ b/examples/with-playwright/app/layout.tsx @@ -0,0 +1,11 @@ +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/examples/with-playwright/app/page.tsx b/examples/with-playwright/app/page.tsx new file mode 100644 index 0000000000000..5b67d3ed292b1 --- /dev/null +++ b/examples/with-playwright/app/page.tsx @@ -0,0 +1,10 @@ +import Link from 'next/link' + +export default function Page() { + return ( +
+

Home

+ About +
+ ) +} diff --git a/examples/with-playwright/e2e/app.spec.ts b/examples/with-playwright/e2e/app.spec.ts new file mode 100644 index 0000000000000..08f59e70fdf57 --- /dev/null +++ b/examples/with-playwright/e2e/app.spec.ts @@ -0,0 +1,12 @@ +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('/') + // Find an element with the text 'About' and click on it + await page.click('text=About') + // The new URL should be "/about" (baseURL is used there) + await expect(page).toHaveURL('/about') + // The new page should contain an h1 with "About" + await expect(page.locator('h1')).toContainText('About') +}) diff --git a/examples/with-playwright/e2e/example.spec.ts b/examples/with-playwright/e2e/pages.spec.ts similarity index 87% rename from examples/with-playwright/e2e/example.spec.ts rename to examples/with-playwright/e2e/pages.spec.ts index f552087a355a8..72cf32f827141 100644 --- a/examples/with-playwright/e2e/example.spec.ts +++ b/examples/with-playwright/e2e/pages.spec.ts @@ -2,11 +2,11 @@ import { test, expect } from '@playwright/test' test('should navigate to the about page', async ({ page }) => { // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) - await page.goto('/') + await page.goto('/home') // Find an element with the text 'About Page' and click on it await page.getByText('About Page').click() // The new url should be "/about" (baseURL is used there) - await expect(page).toHaveURL('/about') + await expect(page).toHaveURL('/home/about') // The new page should contain an h1 with "About Page" await expect(page.getByRole('heading', { level: 1 })).toContainText( 'About Page' diff --git a/examples/with-playwright/package.json b/examples/with-playwright/package.json index 25209924f85ee..ede02ffd8049d 100644 --- a/examples/with-playwright/package.json +++ b/examples/with-playwright/package.json @@ -12,6 +12,9 @@ "react-dom": "18.2.0" }, "devDependencies": { - "@playwright/test": "^1.36.2" + "@playwright/test": "^1.40.1", + "@types/node": "20.10.4", + "@types/react": "18.2.43", + "typescript": "5.3.3" } } diff --git a/examples/with-playwright/pages/_app.tsx b/examples/with-playwright/pages/_app.tsx new file mode 100644 index 0000000000000..1e1cec92425c8 --- /dev/null +++ b/examples/with-playwright/pages/_app.tsx @@ -0,0 +1,7 @@ +import '../styles/globals.css' + +function MyApp({ Component, pageProps }) { + return +} + +export default MyApp diff --git a/examples/with-playwright/pages/about.js b/examples/with-playwright/pages/home/about.tsx similarity index 72% rename from examples/with-playwright/pages/about.js rename to examples/with-playwright/pages/home/about.tsx index c70cc9a8b8538..cff45f184e57e 100644 --- a/examples/with-playwright/pages/about.js +++ b/examples/with-playwright/pages/home/about.tsx @@ -1,4 +1,4 @@ -import styles from '../styles/Home.module.css' +import styles from '../../styles/Home.module.css' import Link from 'next/link' export default function About() { @@ -7,7 +7,7 @@ export default function About() {

About Page

- ← Go Back + ← Go Back

diff --git a/examples/with-playwright/pages/index.js b/examples/with-playwright/pages/home/index.tsx similarity index 95% rename from examples/with-playwright/pages/index.js rename to examples/with-playwright/pages/home/index.tsx index 78b39e67083b1..1e379aaa75fc2 100644 --- a/examples/with-playwright/pages/index.js +++ b/examples/with-playwright/pages/home/index.tsx @@ -1,7 +1,7 @@ import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' -import styles from '../styles/Home.module.css' +import styles from '../../styles/Home.module.css' export default function Home() { return ( @@ -23,7 +23,7 @@ export default function Home() {

- +

About Page →

Playwright will test if this link is working.

diff --git a/examples/with-playwright/tsconfig.json b/examples/with-playwright/tsconfig.json new file mode 100644 index 0000000000000..ac7f7f4c57dba --- /dev/null +++ b/examples/with-playwright/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/examples/with-vitest/__tests__/Home.test.tsx b/examples/with-vitest/__tests__/Home.test.tsx index 3b6c636d07919..01fafc73e8add 100644 --- a/examples/with-vitest/__tests__/Home.test.tsx +++ b/examples/with-vitest/__tests__/Home.test.tsx @@ -1,6 +1,6 @@ import { expect, test } from 'vitest' import { render, screen, within } from '@testing-library/react' -import Home from '../pages' +import Home from '../pages/home' test('Pages Router', () => { render() diff --git a/examples/with-vitest/app/client/page.test.tsx b/examples/with-vitest/app/client/page.test.tsx deleted file mode 100644 index abb2cdb8c494f..0000000000000 --- a/examples/with-vitest/app/client/page.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { expect, test } from 'vitest' -import { render, screen } from '@testing-library/react' -import ClientComponent from './page' - -test('App Router: Works with Client Components', () => { - render() - expect( - screen.getByRole('heading', { level: 1, name: 'Client Component' }) - ).toBeDefined() -}) diff --git a/examples/with-vitest/app/client/page.tsx b/examples/with-vitest/app/client/page.tsx deleted file mode 100644 index 63a6627147bce..0000000000000 --- a/examples/with-vitest/app/client/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -'use client' - -export default function ClientComponent() { - return

Client Component

-} diff --git a/examples/with-vitest/app/component.test.tsx b/examples/with-vitest/app/counter.test.tsx similarity index 70% rename from examples/with-vitest/app/component.test.tsx rename to examples/with-vitest/app/counter.test.tsx index 8871a7e5ff4f3..91ff5bfd9f578 100644 --- a/examples/with-vitest/app/component.test.tsx +++ b/examples/with-vitest/app/counter.test.tsx @@ -1,9 +1,9 @@ import { expect, test } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' -import Component from './component' +import Counter from './counter' -test('App Router: Works with Client Components (React State)', () => { - render() +test('App Router: Works with Client Components', () => { + render() expect(screen.getByRole('heading', { level: 2, name: '0' })).toBeDefined() fireEvent.click(screen.getByRole('button')) expect(screen.getByRole('heading', { level: 2, name: '1' })).toBeDefined() diff --git a/examples/with-jest/app/component.tsx b/examples/with-vitest/app/counter.tsx similarity index 94% rename from examples/with-jest/app/component.tsx rename to examples/with-vitest/app/counter.tsx index 8430160b348a9..e383b403147b9 100644 --- a/examples/with-jest/app/component.tsx +++ b/examples/with-vitest/app/counter.tsx @@ -1,3 +1,5 @@ +'use client' + import { useState } from 'react' export default function Counter() { diff --git a/examples/with-vitest/app/rsc/page.test.tsx b/examples/with-vitest/app/page.test.tsx similarity index 52% rename from examples/with-vitest/app/rsc/page.test.tsx rename to examples/with-vitest/app/page.test.tsx index 62be2efc40a9e..1cd07a2ece43d 100644 --- a/examples/with-vitest/app/rsc/page.test.tsx +++ b/examples/with-vitest/app/page.test.tsx @@ -1,13 +1,7 @@ -import { expect, test, vi } from 'vitest' +import { expect, test } from 'vitest' import { render, screen } from '@testing-library/react' import Page from './page' -// Disables a package that checks that code is only executed on the server side. -// Also, this mock can be defined in the Vitest setup file. -vi.mock('server-only', () => { - return {} -}) - test('App Router: Works with Server Components', () => { render() expect( diff --git a/examples/with-vitest/app/rsc/page.tsx b/examples/with-vitest/app/page.tsx similarity index 84% rename from examples/with-vitest/app/rsc/page.tsx rename to examples/with-vitest/app/page.tsx index bea3626bc66ff..5175cc603bd58 100644 --- a/examples/with-vitest/app/rsc/page.tsx +++ b/examples/with-vitest/app/page.tsx @@ -1,5 +1,3 @@ -import 'server-only' - export const metadata = { title: 'App Router', } diff --git a/examples/with-vitest/app/utils/add.test.ts b/examples/with-vitest/app/utils/add.test.ts new file mode 100644 index 0000000000000..96be3edb031ee --- /dev/null +++ b/examples/with-vitest/app/utils/add.test.ts @@ -0,0 +1,12 @@ +import { expect, test, vi } from 'vitest' +import { add } from './add' + +// Disables a package that checks that code is only executed on the server side. +// Also, this mock can be defined in the Vitest setup file. +vi.mock('server-only', () => { + return {} +}) + +test('Test functions that import server-only', () => { + expect(add(1, 2)).toBe(3) +}) diff --git a/examples/with-vitest/app/utils/add.ts b/examples/with-vitest/app/utils/add.ts new file mode 100644 index 0000000000000..260a4648004d4 --- /dev/null +++ b/examples/with-vitest/app/utils/add.ts @@ -0,0 +1,5 @@ +import 'server-only' + +export function add(a: number, b: number) { + return a + b +} diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json index 6a7c05bdc4d0c..83a87a3c7a8fc 100644 --- a/examples/with-vitest/package.json +++ b/examples/with-vitest/package.json @@ -13,14 +13,11 @@ "server-only": "^0.0.1" }, "devDependencies": { - "@testing-library/jest-dom": "6.1.2", "@testing-library/react": "14.0.0", - "@testing-library/user-event": "14.4.3", "@types/node": "20.5.9", "@types/react": "18.2.8", - "@types/testing-library__jest-dom": "6.0.0", - "@vitejs/plugin-react": "4.0.4", - "jsdom": "^22.1.0", + "@vitejs/plugin-react": "^4.2.1", + "jsdom": "^23.0.1", "typescript": "5.2.2", "vitest": "0.34.3" } diff --git a/examples/with-vitest/pages/index.tsx b/examples/with-vitest/pages/home/index.tsx similarity index 97% rename from examples/with-vitest/pages/index.tsx rename to examples/with-vitest/pages/home/index.tsx index ff4dd21141cf2..a4b85e51ece3e 100644 --- a/examples/with-vitest/pages/index.tsx +++ b/examples/with-vitest/pages/home/index.tsx @@ -1,7 +1,7 @@ import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' -import styles from '../styles/Home.module.css' +import styles from '../../styles/Home.module.css' const Home: NextPage = () => { return ( diff --git a/examples/with-vitest/tsconfig.json b/examples/with-vitest/tsconfig.json index 99710e857874f..36ca1e6354822 100644 --- a/examples/with-vitest/tsconfig.json +++ b/examples/with-vitest/tsconfig.json @@ -9,12 +9,17 @@ "noEmit": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", - "incremental": true + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }