diff --git a/docs/testing.md b/docs/testing.md index f2804e28cf37..d7c002f2fa1a 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -3,12 +3,134 @@
Examples
-Learn how to set up Next.js with three commonly used testing tools: [Jest](https://jestjs.io/docs/tutorial-react), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/), and [Cypress](https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react/). +Learn how to set up Next.js with three commonly used testing tools: [Cypress](https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react/), [Jest](https://jestjs.io/docs/tutorial-react), and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). + +## Cypress + +Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing**. + +### Quickstart + +You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started. + +```bash +npx create-next-app --example with-cypress with-cypress-app +``` + +### Manual setup + +To get started with Cypress, install the `cypress` package: + +```bash +npm install --save-dev cypress +``` + +Add Cypress to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "cypress": "cypress open", +} +``` + +Run Cypress for the first time to generate examples that use their recommended folder structure: + +```bash +npm run cypress +``` + +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. + +### Creating your first Cypress integration test + +Assuming the following two Next.js pages: + +```jsx +// pages/index.js +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx +// pages/about.js +export default function About() { + return ( +
+

About Page

+
+ ) +} +``` + +Add a test to check your navigation is working correctly: + +```jsx +// cypress/integration/app.spec.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.json` configuration file. + +### Running your Cypress tests + +Since Cypress is testing a real Next.js application, it requires 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` in another terminal window to start Cypress. + +> **Note:** 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 conjuction with Cypress. Remember to rebuild your application after new changes. + +### 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 +// package.json + +"scripts": { + //... + "cypress": "cypress open", + "cypress:headless": "cypress run", + "e2e": "start-server-and-test start http://localhost:3000 cypress", + "e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless" +} +``` + +You can learn more about Cypress and Continuous Integration from these resources: + +- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) +- [Official Cypress Github Action](https://github.com/cypress-io/github-action) ## Jest and React Testing Library @@ -185,7 +307,7 @@ it('renders homepage unchanged', () => { }) ``` -Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. +> **Note**: Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. **Running your test suite** @@ -197,126 +319,6 @@ For further reading, you may find these resources helpful: - [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. -## Cypress - -Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing**. - -### Quickstart - -You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started. - -```bash -npx create-next-app --example with-cypress with-cypress-app -``` - -### Manual setup - -To get started with Cypress, install the `cypress` package: - -```bash -npm install --save-dev cypress -``` - -Add Cypress to the `package.json` scripts field: - -```json -"scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "cypress": "cypress open", -} -``` - -Run Cypress for the first time to generate examples that use their recommended folder structure: - -```bash -npm run cypress -``` - -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. - -### Creating your first Cypress integration test - -Assuming the following two Next.js pages: - -```jsx -// pages/index.js -import Link from 'next/link' - -export default function Home() { - return ( - - ) -} -``` - -```jsx -// pages/about.js -export default function About() { - return ( -
-

About Page

-
- ) -} -``` - -Add a test to check your navigation is working correctly: - -```jsx -// cypress/integration/app.spec.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.json` configuration file. - -### Running your Cypress tests - -Because Cypress is testing a real Next.js application, it requires the Next.js server to be running prior to starting Cypress. Run `npm run dev` to start the development server then run `npm run cypress` in another terminal window to start Cypress. - -Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test dev http://localhost:3000 cypress"` to start the Next.js development server when you run Cypress. - -### 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 -// package.json - -"scripts": { - //... - "cypress": "cypress open", - "cypress:headless": "cypress run", - "e2e": "start-server-and-test dev http://localhost:3000 cypress", - "e2e:headless": "start-server-and-test dev http://localhost:3000 cypress:headless" -} -``` - -You can learn more about Cypress and Continuous Integration from these resources: - -- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) -- [Official Cypress Github Action](https://github.com/cypress-io/github-action) - ## Community Packages and Examples The Next.js community has created packages and articles you may find helpful: diff --git a/test/integration/basic/test/development-logs.js b/test/integration/basic/test/development-logs.js index 4d255e52bb7e..5fe56e6bfca0 100644 --- a/test/integration/basic/test/development-logs.js +++ b/test/integration/basic/test/development-logs.js @@ -2,66 +2,39 @@ import webdriver from 'next-webdriver' export default (context) => { - describe('Development Logs', () => { - it('should warn when prefetch is true', async () => { - let browser - try { - browser = await webdriver(context.appPort, '/development-logs') - const browserLogs = await browser.log('browser') - let foundLog = false - browserLogs.forEach((log) => { - if (log.message.includes('Next.js auto-prefetches automatically')) { - foundLog = true - } - }) - expect(foundLog).toBe(true) - } finally { - if (browser) { - await browser.close() + async function getLogs$(path) { + let foundLog = false + let browser + try { + browser = await webdriver(context.appPort, path) + const browserLogs = await browser.log('browser') + + browserLogs.forEach((log) => { + if (log.message.includes('Next.js auto-prefetches automatically')) { + foundLog = true } + }) + } finally { + if (browser) { + await browser.close() } + } + return foundLog + } + describe('Development Logs', () => { + it('should warn when prefetch is true', async () => { + const foundLog = await getLogs$('/development-logs') + expect(foundLog).toBe(true) }) it('should not warn when prefetch is false', async () => { - let browser - try { - browser = await webdriver( - context.appPort, - '/development-logs/link-with-prefetch-false' - ) - const browserLogs = await browser.log('browser') - let found = false - browserLogs.forEach((log) => { - if (log.message.includes('Next.js auto-prefetches automatically')) { - found = true - } - }) - expect(found).toBe(false) - } finally { - if (browser) { - await browser.close() - } - } + const foundLog = await getLogs$( + '/development-logs/link-with-prefetch-false' + ) + expect(foundLog).toBe(false) }) it('should not warn when prefetch is not specified', async () => { - let browser - try { - browser = await webdriver( - context.appPort, - '/development-logs/link-with-no-prefetch' - ) - const browserLogs = await browser.log('browser') - let found = false - browserLogs.forEach((log) => { - if (log.message.includes('Next.js auto-prefetches automatically')) { - found = true - } - }) - expect(found).toBe(false) - } finally { - if (browser) { - await browser.close() - } - } + const foundLog = await getLogs$('/development-logs/link-with-no-prefetch') + expect(foundLog).toBe(false) }) }) }