diff --git a/README.md b/README.md index dd89dd711..18141b520 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## The problem -You want to write maintainable tests for your React Native components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your testbase to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down. +You want to write maintainable tests for your React Native components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your tests to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down. ## This solution @@ -50,7 +50,7 @@ This library has a `peerDependencies` listing for `react-test-renderer`. Make su ### Additional Jest matchers -In order to use additional React Native-specific jest matchers from [@testing-library/jest-native](https://github.com/testing-library/jest-native) package add it to your project: +To use additional React Native-specific jest matchers from [@testing-library/jest-native](https://github.com/testing-library/jest-native) package add it to your project: #### Using `yarn` @@ -64,7 +64,7 @@ yarn add --dev @testing-library/jest-native npm install --save-dev @testing-library/jest-native ``` -Then automatically add it to your jest tests by using `setupFilesAfterEnv` option in your Jest configuration (it's usually located either in `package.json` under `"jest"` key or in a `jest.config.json` file): +Then automatically add it to your jest tests by using the `setupFilesAfterEnv` option in your Jest configuration (it's usually located either in `package.json` under the `"jest"` key or in a `jest.config.json` file): ```json { @@ -75,9 +75,9 @@ Then automatically add it to your jest tests by using `setupFilesAfterEnv` optio ### Custom Jest Preset (React Native before 0.71) -We generally advise to use the "react-native" preset when testing with this library. +We generally advise using the "react-native" preset when testing with this library. -However, if you use React Native version earlier than 0.71 with [modern Jest fake timers](https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers) (default since Jest 27), you'll need to apply this custom Jest preset or otherwise awaiting promises, like using `waitFor` or `findBy*`, queries will fail with timeout. +However, if you use React Native version earlier than 0.71 with [modern Jest fake timers](https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers) (default since Jest 27), you'll need to apply this custom Jest preset or otherwise awaiting promises, like using `waitFor` or `findBy*`, queries will fail with a timeout. This is a [known issue](https://github.com/facebook/react-native/issues/29303). It happens because React Native's Jest preset overrides native Promise. Our preset restores it to defaults, which is not a problem in most apps out there. @@ -130,11 +130,11 @@ You can find the source of `QuestionsBoard` component and this example [here](ht The [public API](https://callstack.github.io/react-native-testing-library/docs/api) of `@testing-library/react-native` is focused around these essential methods: -- [`render`](https://callstack.github.io/react-native-testing-library/docs/api#render) – deeply renders given React element and returns helpers to query the output components. +- [`render`](https://callstack.github.io/react-native-testing-library/docs/api#render) – deeply renders the given React element and returns helpers to query the output components. - [`fireEvent`](https://callstack.github.io/react-native-testing-library/docs/api#fireevent) - invokes named event handler on the element. -- [`waitFor`](https://callstack.github.io/react-native-testing-library/docs/api#waitfor) - waits for non-deterministic periods of time until queried element is added or times out. -- [`waitForElementToBeRemoved`](https://callstack.github.io/react-native-testing-library/docs/api#waitforelementtoberemoved) - waits for non-deterministic periods of time until queried element is removed or times out. -- [`within`](https://callstack.github.io/react-native-testing-library/docs/api#within) - creates a queries object scoped for given element. +- [`waitFor`](https://callstack.github.io/react-native-testing-library/docs/api#waitfor) - waits for non-deterministic periods of time until the queried element is added or times out. +- [`waitForElementToBeRemoved`](https://callstack.github.io/react-native-testing-library/docs/api#waitforelementtoberemoved) - waits for non-deterministic periods of time until the queried element is removed or times out. +- [`within`](https://callstack.github.io/react-native-testing-library/docs/api#within) - creates a queries object scoped for a given element. ## Migration Guides @@ -150,7 +150,7 @@ The [public API](https://callstack.github.io/react-native-testing-library/docs/a ## Related External Resources -- [Real world extensive examples repo](https://github.com/vanGalilea/react-native-testing) +- [Real-world extensive examples repo](https://github.com/vanGalilea/react-native-testing) - [Where and how to start testing 🧪 your react-native app ⚛️ and how to keep on testin’](https://blog.usejournal.com/where-and-how-to-start-testing-your-react-native-app-%EF%B8%8F-and-how-to-keep-on-testin-ec3464fb9b41) - [Intro to React Native Testing Library & Jest Native](https://youtu.be/CpTQb0XWlRc) diff --git a/website/docs/JestMatchers.md b/website/docs/JestMatchers.md new file mode 100644 index 000000000..36bb5d4ca --- /dev/null +++ b/website/docs/JestMatchers.md @@ -0,0 +1,216 @@ +--- +id: jest-matchers +title: Jest Matchers +--- + +:::note +Built-in Jest matchers require RNTL v12.4.0 or later. +::: + +This guide describes built-in Jest matchers, we recommend using these matchers as they provide more readable tests, better accessibility support, and a better developer experience. + +If you are already using legacy Jest Native matchers we have a [migration guide](migration-jest-native) for moving to the built-in matchers. + +import TOCInline from '@theme/TOCInline'; + + + +## Element Existence + +### `toBeOnTheScreen()` + +```ts +expect(element).toBeOnTheScreen() +``` + +This allows you to assert whether an element is attached to the element tree or not. If you hold a reference to an element and it gets unmounted during the test it will no longer pass this assertion. + +## Element Content + +### `toHaveTextContent()` + +```ts +expect(element).toHaveTextContent( + text: string | RegExp, + options?: { + exact?: boolean; + normalizer?: (text: string) => string; + }, +) +``` + +This allows you to assert whether the given element has the given text content or not. It accepts either `string` or `RegExp` matchers, as well as [text match options](Queries.md#text-match-options) of `exact` and `normalizer`. + +### `toContainElement()` + +```ts +expect(container).toContainElement( + element: ReactTestInstance | null, +) +``` + +This allows you to assert whether the given container element does contain another host element. + +### `toBeEmptyElement()` + +```ts +expect(element).toBeEmptyElement() +``` + +This allows you to assert whether the given element does not have any host child elements or text content. + + + + + +## Element State + +### `toHaveDisplayValue()` + +```ts +expect(element).toHaveDisplayValue( + value: string | RegExp, + options?: { + exact?: boolean; + normalizer?: (text: string) => string; + }, +) +``` + +This allows you to assert whether the given `TextInput` element has a specified display value. It accepts either `string` or `RegExp` matchers, as well as [text match options](Queries.md#text-match-options) of `exact` and `normalizer`. + +### `toHaveAccessibilityValue()` + +```ts +expect(element).toHaveAccessibilityValue( + value: { + min?: number; + max?: number; + now?: number; + text?: string | RegExp; + }, +) +``` + +This allows you to assert whether the given element has a specified accessible value. + +This matcher will assert accessibility value based on `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` and `accessibilityValue` props. Only defined value entries will be used in the assertion, the element might have additional accessibility value entries and still be matched. + +When querying by `text` entry a string or `RegExp` might be used. + + +### `toBeEnabled()` / `toBeDisabled` {#tobeenabled} + +```ts +expect(element).toBeEnabled() +expect(element).toBeDisabled() +``` + +These allow you to assert whether the given element is enabled or disabled from the user's perspective. It relies on the accessibility disabled state as set by `aria-disabled` or `accessibilityState.disabled` props. It will consider a given element disabled when it or any of its ancestors is disabled. + +:::note +These matchers are the negation of each other, and both are provided to avoid double negations in your assertions. +::: + + +### `toBeSelected()` + +```ts +expect(element).toBeSelected() +``` + +This allows you to assert whether the given element is selected from the user's perspective. It relies on the accessibility selected state as set by `aria-selected` or `accessibilityState.selected` props. + + +### `toBeChecked()` / `toBePartiallyChecked()` {#tobechecked} + +```ts +expect(element).toBeChecked() +expect(element).toBePartiallyChecked() +``` + +These allow you to assert whether the given element is checked or partially checked from the user's perspective. It relies on the accessibility checked state as set by `aria-checked` or `accessibilityState.checked` props. + +:::note +* `toBeChecked()` matcher works only on elements with the `checkbox` or `radio` role. +* `toBePartiallyChecked()` matcher works only on elements with the `checkbox` role. +::: + +### `toBeExpanded()` / `toBeCollapsed()` {#tobeexpanded} + +```ts +expect(element).toBeExpanded() +expect(element).toBeCollapsed() +``` + +These allows you to assert whether the given element is expanded or collapsed from the user's perspective. It relies on the accessibility disabled state as set by `aria-expanded` or `accessibilityState.expanded` props. + +:::note +These matchers are the negation of each other for expandable elements (elements with explicit `aria-expanded` or `accessibilityState.expanded` props). However, both won't pass for non-expandable elements (ones without explicit `aria-expanded` or `accessibilityState.expanded` props). +::: + + +### `toBeBusy()` + +```ts +expect(element).toBeBusy() +``` + +This allows you to assert whether the given element is busy from the user's perspective. It relies on the accessibility selected state as set by `aria-busy` or `accessibilityState.busy` props. + +## Element Styles + +### `toBeVisible()` + +```ts +expect(element).toBeVisible() +``` + +This allows you to assert whether the given element is visible from the user's perspective. + +The element is considered invisible when itself or any of its ancestors has `display: none` or `opacity: 0` styles, as well as when it's hidden from accessibility. + +### `toHaveStyle()` + +```ts +expect(element).toHaveStyle( + style: StyleProp