diff --git a/docs/react/testing.md b/docs/react/testing.md deleted file mode 100644 index dd1f2d7d308..00000000000 --- a/docs/react/testing.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sidebar_label: Testing ---- - -# Testing Ionic React - -When an `@ionic/react` application is generated using the Ionic CLI, it is automatically set up for unit testing and integration testing with [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro). The combo provides a great way to test your React components in isolation as well integrated together. For more information on how to get started testing an Ionic React app, see our article [Testing Ionic React Apps with Jest and React Testing Library](https://ionicframework.com/blog/testing-ionic-react-apps-with-jest-and-react-testing-library/). diff --git a/docs/react/testing/introduction.md b/docs/react/testing/introduction.md new file mode 100644 index 00000000000..a8cae30a467 --- /dev/null +++ b/docs/react/testing/introduction.md @@ -0,0 +1,21 @@ +--- +sidebar_label: Introduction +title: Ionic React Testing Introduction +description: Learn how to test an Ionic React application. This document provides an overview of how to test an application built with @ionic/react. +--- + +# Testing Ionic React + +This document provides an overview of how to test an application built with `@ionic/react`. It covers the basics of testing with React, as well as the specific tools and libraries developers can use to test their applications. + +## Introduction + +Testing is an important part of the development process, and it helps to ensure that an application is working as intended. In `@ionic/react`, testing is done using a combination of tools and libraries, including Jest, React Testing Library, Playwright or Cypress. + +## Types of Tests + +There are two types of tests that can be written: + +**Unit Tests**: Unit tests are used to test individual functions and components in isolation. [Jest](https://jestjs.io) and [React Testing Library](https://testing-library.com) are commonly used for unit testing. + +**Integration Tests**: Integration tests are used to test how different components work together. [Cypress](https://www.cypress.io) or [Playwright](https://playwright.dev) are commonly used for integration testing. diff --git a/docs/react/testing/unit-testing/best-practices.md b/docs/react/testing/unit-testing/best-practices.md new file mode 100644 index 00000000000..ce1562d391f --- /dev/null +++ b/docs/react/testing/unit-testing/best-practices.md @@ -0,0 +1,51 @@ +--- +sidebar_label: Best Practices +--- + +# Best Practices + +## IonApp is required for test templates + +In your test template when rendering with React Testing Library, you must wrap your component with an `IonApp` component. This is required for the component to be rendered correctly. + +```tsx title="Example.test.tsx" +import { IonApp } from '@ionic/react'; +import { render } from "@testing-library/react"; + +import Example from './Example'; + +test('example', () => { + render( + + + + ); + ... +}); +``` + +## Use `user-event` for user interactions + +React Testing Library recommends using the `user-event` library for simulating user interactions. This library provides a more realistic simulation of user interactions than the `fireEvent` function provided by React Testing Library. + +```tsx title="Example.test.tsx" +import { IonApp } from '@ionic/react'; +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import Example from './Example'; + +test('example', async () => { + const user = userEvent.setup(); + + render( + + + + ); + + await user.click(screen.getByRole('button', { name: /click me!/i })); +}); +``` + +For more information on `user-event`, see the [user-event documentation](https://testing-library.com/docs/user-event/intro/). diff --git a/docs/react/testing/unit-testing/examples.md b/docs/react/testing/unit-testing/examples.md new file mode 100644 index 00000000000..39275eef669 --- /dev/null +++ b/docs/react/testing/unit-testing/examples.md @@ -0,0 +1,112 @@ +--- +sidebar_label: Examples +title: Ionic React Testing Examples +description: Learn how to test an Ionic React application. This document provides examples of how to test different types of components. +--- + +# Examples + +## Testing a modal presented from a trigger + +This example shows how to test a modal that is presented from a trigger. The modal is presented when the user clicks a button. + +### Example component + +```tsx title="src/Example.tsx" +import { IonButton, IonModal } from '@ionic/react'; + +export default function Example() { + return ( + <> + Open + Modal content + + ); +} +``` + +### Testing the modal + +```tsx title="src/Example.test.tsx" +import { IonApp } from '@ionic/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; + +import Example from './Example'; + +test('button presents a modal when clicked', async () => { + render( + + + + ); + // Simulate a click on the button + fireEvent.click(screen.getByText('Open')); + // Wait for the modal to be presented + await waitFor(() => { + // Assert that the modal is present + expect(screen.getByText('Modal content')).toBeInTheDocument(); + }); +}); +``` + +## Testing a modal presented from useIonModal + +This example shows how to test a modal that is presented using the `useIonModal` hook. The modal is presented when the user clicks a button. + +### Example component + +```tsx title="src/Example.tsx" +import { IonContent, useIonModal, IonHeader, IonToolbar, IonTitle, IonButton, IonPage } from '@ionic/react'; + +const ModalContent: React.FC = () => { + return ( + +
Modal Content
+
+ ); +}; + +const Example: React.FC = () => { + const [present] = useIonModal(ModalContent); + return ( + + + + Blank + + + + present()}> + Open + + + + ); +}; + +export default Example; +``` + +### Testing the modal + +```tsx title="src/Example.test.tsx" +import { IonApp } from '@ionic/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; + +import Example from './Example'; + +test('should present ModalContent when button is clicked', async () => { + render( + + + + ); + // Simulate a click on the button + fireEvent.click(screen.getByText('Open')); + // Wait for the modal to be presented + await waitFor(() => { + // Assert that the modal is present + expect(screen.getByText('Modal Content')).toBeInTheDocument(); + }); +}); +``` diff --git a/docs/react/testing/unit-testing/setup.md b/docs/react/testing/unit-testing/setup.md new file mode 100644 index 00000000000..0e4cf3d1ed1 --- /dev/null +++ b/docs/react/testing/unit-testing/setup.md @@ -0,0 +1,40 @@ +--- +sidebar_label: Setup +title: Ionic React Unit Testing Setup +description: Learn how to set up unit tests for an Ionic React application. +--- + +# Unit Testing Setup + +Ionic requires a few additional steps to set up unit tests. If you are using an Ionic starter project, these steps have already been completed for you. + +### Install React Testing Library + +React Testing Library is a set of utilities that make it easier to test React components. It's used to interact with components and test their behavior. + +```bash +npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event +``` + +### Initialize Ionic React + +Ionic React requires the `setupIonicReact` function to be called before any tests are run. Failing to do so will result in mode-based classes and platform behaviors not being applied to your components. + +In `src/setupTest.ts`, add the following code: + +```diff +import '@testing-library/jest-dom/extend-expect'; + ++ import { setupIonicReact } from '@ionic/react'; + ++ setupIonicReact(); + +// Mock matchmedia +window.matchMedia = window.matchMedia || function () { + return { + matches: false, + addListener: function () { }, + removeListener: function () { } + }; +}; +``` diff --git a/sidebars.js b/sidebars.js index c08a43ada8f..832bec09c8f 100644 --- a/sidebars.js +++ b/sidebars.js @@ -120,7 +120,23 @@ module.exports = { 'react/pwa', 'react/overlays', 'react/storage', - 'react/testing', + { + type: 'category', + label: 'Testing', + items: [ + 'react/testing/introduction', + { + type: 'category', + label: 'Unit Testing', + collapsed: false, + items: [ + 'react/testing/unit-testing/setup', + 'react/testing/unit-testing/examples', + 'react/testing/unit-testing/best-practices', + ], + }, + ], + }, 'react/performance', ], }, diff --git a/vercel.json b/vercel.json index 8bc50e73908..b7f986824c5 100644 --- a/vercel.json +++ b/vercel.json @@ -63,7 +63,8 @@ "source": "/docs/vue/your-first-app/6-deploying-mobile", "destination": "/docs/vue/your-first-app/deploying-mobile" }, - { "source": "/docs/vue/your-first-app/7-live-reload", "destination": "/docs/vue/your-first-app/live-reload" } + { "source": "/docs/vue/your-first-app/7-live-reload", "destination": "/docs/vue/your-first-app/live-reload" }, + { "source": "/docs/react/testing", "destination": "/docs/react/testing/introduction" } ], "rewrites": [ { "source": "/docs", "destination": "/" },