diff --git a/website/docs/API.md b/website/docs/API.md index 5fdefce56..35eff6489 100644 --- a/website/docs/API.md +++ b/website/docs/API.md @@ -2,47 +2,11 @@ id: api title: API --- +import TOCInline from '@theme/TOCInline'; -### Table of contents: - -- [`render`](#render) - - [`render` options](#render-options) - - [`...queries`](#queries) - - [`update`](#update) - - [`unmount`](#unmount) - - [`debug`](#debug) - - [`toJSON`](#tojson) - - [`root`](#root) - - [`UNSAFE_root`](#unsaferoot) -- [`screen`](#screen) -- [`cleanup`](#cleanup) -- [`fireEvent`](#fireevent) -- [`fireEvent[eventName]`](#fireeventeventname) - - [`fireEvent.press`](#fireeventpress) - - [`fireEvent.changeText`](#fireeventchangetext) - - [`fireEvent.scroll`](#fireeventscroll) -- [`waitFor`](#waitfor) - - [Using a React Native version \< 0.71 with Jest fake timers](#using-a-react-native-version--071-with-jest-fake-timers) -- [`waitForElementToBeRemoved`](#waitforelementtoberemoved) -- [`within`, `getQueriesForElement`](#within-getqueriesforelement) -- [`queryBy*` APIs](#queryby-apis) -- [`queryAll*` APIs](#queryall-apis) -- [`act`](#act) -- [`renderHook`](#renderhook) - - [`callback`](#callback) - - [`options` (Optional)](#options-optional) - - [`RenderHookResult` object](#renderhookresult-object) - - [Examples](#examples) -- [Configuration](#configuration) - - [`configure`](#configure) - - [`resetToDefaults()`](#resettodefaults) - - [Environment variables](#environment-variables) -- [Accessibility](#accessibility) - - [`isHiddenFromAccessibility`](#ishiddenfromaccessibility) - -This page gathers public API of React Native Testing Library along with usage examples. - -## `render` + + +## `render` API - [`Example code`](https://github.com/callstack/react-native-testing-library/blob/main/src/__tests__/render.test.tsx) @@ -79,7 +43,7 @@ Latest `render` result is kept in [`screen`](#screen) variable that can be impor Using `screen` instead of destructuring `render` result is recommended approach. See [this article](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen) from Kent C. Dodds for more details. ::: -### `render` options +### Options {#render-options} The behavior of `render` method can be customized by passing various options as a second argument of `RenderOptions` type: @@ -257,7 +221,7 @@ This API typically will return a composite view which goes against recommended t This API has been previously named `container` for compatibility with [React Testing Library](https://testing-library.com/docs/react-testing-library/api#container-1). However, despite the same name, the actual behavior has been signficantly different, hence the name change to `UNSAFE_root`. ::: -## `screen` +## `screen` API ```ts let screen: RenderResult; @@ -274,48 +238,7 @@ This can also be used to build test utils that would normally require to be in r const debugText = () => screen.debug({ mapProps: (props) => ({}) }); ``` -## `cleanup` - -```ts -const cleanup: () => void; -``` - -Unmounts React trees that were mounted with `render` and clears `screen` variable that holds latest `render` output. - -:::info -Please note that this is done automatically if the testing framework you're using supports the `afterEach` global (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test. -::: - -For example, if you're using the `jest` testing framework, then you would need to use the `afterEach` hook like so: - -```jsx -import { cleanup, render } from '@testing-library/react-native/pure'; -import { View } from 'react-native'; - -afterEach(cleanup); - -it('renders a view', () => { - render(); - // ... -}); -``` - -The `afterEach(cleanup)` call also works in `describe` blocks: - -```jsx -describe('when logged in', () => { - afterEach(cleanup); - - it('renders the user', () => { - render(); - // ... - }); -}); -``` - -Failing to call `cleanup` when you've called `render` could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests). - -## `fireEvent` +## `fireEvent` API ```ts function fireEvent( @@ -374,13 +297,7 @@ render( fireEvent(screen.getByPlaceholderText('my placeholder'), 'blur'); ``` -## `fireEvent[eventName]` - -```ts -fireEvent[eventName](element: ReactTestInstance, ...data: Array): void -``` - -Convenience methods for common events like: `press`, `changeText`, `scroll`. +FireEvent exposes convenience methods for common events like: `press`, `changeText`, `scroll`. ### `fireEvent.press` @@ -520,7 +437,9 @@ expect(onEndReached).toHaveBeenCalled(); If you're noticing that components are not being found on a list, even after mocking a scroll event, try changing the [`initialNumToRender`](https://reactnative.dev/docs/flatlist#initialnumtorender) that you have set. If you aren't comfortable changing the code to accept this prop from the unit test, try using an e2e test that might better suit what use case you're attempting to replicate. ::: -## `waitFor` +## Helper functions + +### `waitFor` - [`Example code`](https://github.com/callstack/react-native-testing-library/blob/main/src/__tests__/waitFor.test.tsx) @@ -573,7 +492,7 @@ Avoiding side effects in `expectation` callback can be partially enforced with t It is also recommended to have a [single assertion per each `waitFor`](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback) for more consistency and faster failing tests. If you want to make several assertions, then they should be in seperate `waitFor` calls. In many cases you won't actually need to wrap the second assertion in `waitFor` since the first one will do the waiting required for asynchronous change to happen. -### Using a React Native version < 0.71 with Jest fake timers +#### Using a React Native version < 0.71 with Jest fake timers :::caution When using a version of React Native < 0.71 and modern fake timers (the default for `Jest` >= 27), `waitFor` won't work (it will always timeout even if `expectation()` doesn't throw) unless you use the custom [@testing-library/react-native preset](https://github.com/callstack/react-native-testing-library#custom-jest-preset). @@ -624,7 +543,7 @@ In order to properly use `waitFor` you need at least React >=16.9.0 (featuring a If you receive warnings related to `act()` function consult our [Undestanding Act](./UnderstandingAct.md) function document. ::: -## `waitForElementToBeRemoved` +### `waitForElementToBeRemoved` - [`Example code`](https://github.com/callstack/react-native-testing-library/blob/main/src/__tests__/waitForElementToBeRemoved.test.tsx) @@ -665,7 +584,7 @@ In order to properly use `waitForElementToBeRemoved` you need at least React >=1 If you receive warnings related to `act()` function consult our [Undestanding Act](./UnderstandingAct.md) function document. ::: -## `within`, `getQueriesForElement` +### `within`, `getQueriesForElement` - [`Example code`](https://github.com/callstack/react-native-testing-library/blob/main/src/__tests__/within.test.tsx) @@ -696,37 +615,55 @@ Use cases for scoped queries include: - queries scoped to a single item inside a FlatList containing many items - queries scoped to a single screen in tests involving screen transitions (e.g. with react-navigation) -## `queryBy*` APIs -Each of the `getBy*` APIs listed in the render section above have a complimentary `queryBy*` API. The `getBy*` APIs will throw errors if a proper node cannot be found. This is normally the desired effect. However, if you want to make an assertion that an element is not present in the hierarchy, then you can use the `queryBy*` API instead: +### `act` -```jsx -import { render, screen } from '@testing-library/react-native'; +Useful function to help testing components that use hooks API. By default any `render`, `update`, `fireEvent`, and `waitFor` calls are wrapped by this function, so there is no need to wrap it manually. This method is re-exported from [`react-test-renderer`](https://github.com/facebook/react/blob/main/packages/react-test-renderer/src/ReactTestRenderer.js#L567]). -render(
); -const submitButton = screen.queryByText('submit'); -expect(submitButton).not.toBeOnTheScreen(); // it doesn't exist +Consult our [Undestanding Act function](./UnderstandingAct.md) document for more understanding of its intricacies. + +### `cleanup` + +```ts +const cleanup: () => void; ``` -## `queryAll*` APIs +Unmounts React trees that were mounted with `render` and clears `screen` variable that holds latest `render` output. + +:::info +Please note that this is done automatically if the testing framework you're using supports the `afterEach` global (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test. +::: -Each of the query APIs have a corresponding `queryAll*` version that always returns an array of matching nodes. `getAll*` is the same but throws when the array has a length of 0. +For example, if you're using the `jest` testing framework, then you would need to use the `afterEach` hook like so: ```jsx -import { render } from '@testing-library/react-native'; +import { cleanup, render } from '@testing-library/react-native/pure'; +import { View } from 'react-native'; + +afterEach(cleanup); -render(); -const submitButtons = screen.queryAllByText('submit'); -expect(submitButtons).toHaveLength(3); // expect 3 elements +it('renders a view', () => { + render(); + // ... +}); ``` -## `act` +The `afterEach(cleanup)` call also works in `describe` blocks: -Useful function to help testing components that use hooks API. By default any `render`, `update`, `fireEvent`, and `waitFor` calls are wrapped by this function, so there is no need to wrap it manually. This method is re-exported from [`react-test-renderer`](https://github.com/facebook/react/blob/main/packages/react-test-renderer/src/ReactTestRenderer.js#L567]). +```jsx +describe('when logged in', () => { + afterEach(cleanup); -Consult our [Undestanding Act function](./UnderstandingAct.md) document for more understanding of its intricacies. + it('renders the user', () => { + render(); + // ... + }); +}); +``` + +Failing to call `cleanup` when you've called `render` could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests). -## `renderHook` +## `renderHook` API Defined as: @@ -767,13 +704,11 @@ export const useCount = () => { The `renderHook` function accepts the following arguments: -### `callback` - -The function that is called each `render` of the test component. This function should call one or more hooks for testing. +Callback is a function that is called each `render` of the test component. This function should call one or more hooks for testing. The `props` passed into the callback will be the `initialProps` provided in the `options` to `renderHook`, unless new props are provided by a subsequent `rerender` call. -### `options` (Optional) +### `options` A `RenderHookOptions` object to modify the execution of the `callback` function, containing the following properties: @@ -785,7 +720,7 @@ The initial values to pass as `props` to the `callback` function of `renderHook` A React component to wrap the test component in when rendering. This is usually used to add context providers from `React.createContext` for the hook to access with `useContext`. -### `RenderHookResult` object +### `RenderHookResult` ```ts interface RenderHookResult { @@ -797,15 +732,15 @@ interface RenderHookResult { The `renderHook` function returns an object that has the following properties: -#### `result` +### `result` The `current` value of the `result` will reflect the latest of whatever is returned from the `callback` passed to `renderHook`. The `Result` type is determined by the type passed to or inferred by the `renderHook` call. -#### `rerender` +### `rerender` A function to rerender the test component, causing any hooks to be recalculated. If `newProps` are passed, they will replace the `callback` function's `initialProps` for subsequent rerenders. The `Props` type is determined by the type passed to or inferred by the `renderHook` call. -#### `unmount` +### `unmount` A function to unmount the test component. This is commonly used to trigger cleanup effects for `useEffect` hooks. diff --git a/website/docs/GettingStarted.md b/website/docs/GettingStarted.md index 7ee75b529..6687ff301 100644 --- a/website/docs/GettingStarted.md +++ b/website/docs/GettingStarted.md @@ -2,7 +2,6 @@ id: getting-started title: Getting Started --- - ## 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. diff --git a/website/docs/MigrationV11.md b/website/docs/MigrationV11.md index ae9d1c99a..192469301 100644 --- a/website/docs/MigrationV11.md +++ b/website/docs/MigrationV11.md @@ -2,16 +2,19 @@ id: migration-v11 title: Migration to 11.0 --- +import TOCInline from '@theme/TOCInline'; Migration to React Native Testing Library version 11 from version 9.x or 10.x should be a relatively easy task due small amount of breaking changes. + + # Breaking changes -## Update to Jest 28 if you use fake timers +### Update to Jest 28 if you use fake timers If you use fake timers in any of your tests you should update your Jest dependencies to version 28. This is due to the fact that [`jest.useFakeTimers()` config structure](https://jestjs.io/docs/jest-object#jestusefaketimersfaketimersconfig) has changed. -## Refactor legacy `waitForOptions` position +### Refactor legacy `waitForOptions` position In version 9 we introducted query `options` parameters for each query type. This affected all `findBy` and `findAllBy` queries because their signatures changed e.g. from: @@ -41,13 +44,13 @@ should become findByText(/Text/, {}, { timeout: 1000 }) ``` -## Triggering non-touch events on targets with `pointerEvents="box-none"` prop +### Triggering non-touch events on targets with `pointerEvents="box-none"` prop Up to version 10, RNTL disables all events for a target with `pointerEvents="box-none"`. This behavior is counter to how React Native itself functions. From version 11, RNTL continues to disable `press` event for these targets but allows triggering other events, e.g. `layout`. -# All changes +## All changes * chore(breaking): update Jest to 28 by @mdjastrzebski in https://github.com/callstack/react-native-testing-library/pull/1008 * refactor(breaking): remove legacy wait for options support by @mdjastrzebski in https://github.com/callstack/react-native-testing-library/pull/1018 @@ -59,6 +62,6 @@ From version 11, RNTL continues to disable `press` event for these targets but a * chore: Organise a11y queries by predicate by @MattAgn in https://github.com/callstack/react-native-testing-library/pull/977 * chore: reenable skipped byText tests by @mdjastrzebski in https://github.com/callstack/react-native-testing-library/pull/1017 -# Full Changelog +## Full Changelog https://github.com/callstack/react-native-testing-library/compare/v10.1.1...v11.0.0 diff --git a/website/docs/MigrationV12.md b/website/docs/MigrationV12.md index 003e54c7c..b809b3560 100644 --- a/website/docs/MigrationV12.md +++ b/website/docs/MigrationV12.md @@ -2,26 +2,30 @@ id: migration-v12 title: Migration to 12.0 --- - -React Native Testing Library 12 introduces a handful of breaking changes compared to 11.x versions. We believe they were necessary to improve the experience using the library and help the users [fall into the pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/) when writing meaningful tests. You will find migration instructions for each and every change described below. +import TOCInline from '@theme/TOCInline'; :::note If you use [Jest Native matchers](https://github.com/testing-library/jest-native), which we recommend, then you should upgrade it to version 5.4.2 or higher. ::: -# Breaking changes -## 1. All queries exclude elements hidden from accessibility by default +React Native Testing Library 12 introduces a handful of breaking changes compared to 11.x versions. We believe they were necessary to improve the experience using the library and help the users [fall into the pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/) when writing meaningful tests. You will find migration instructions for each and every change described below. + + + +## Breaking changes + +### 1. All queries exclude elements hidden from accessibility by default Elements that are hidden from accessiblity, e.g. elements on non-active screen when using React Navigation, now will not be matched by default by all queries. This is the effect of switching the default value for global config option `defaultIncludeHiddenElements`(api#defaultincludehiddenelements-option) to `false`. Previous behaviour of matching hidden elements can be enabled on query level using [includeHiddenElements](api-queries#includehiddenelements-option) query options or globally using `defaultIncludeHiddenElements`(api#defaultincludehiddenelements-option) configuration option. -## 2. `*ByRole` queries now return only accessibility elements +### 2. `*ByRole` queries now return only accessibility elements `*ByRole` queries now return only accessibility elements, either explicitly marked with `accessible` prop or implicit ones where this status is derived from component type itself (e.g `Text`, `TextInput`, `Switch`, but not `View`). You may need to adjust relevant components under test to make sure they pass `isAccessibilityElement` check. -### Examples +#### Examples Let's assume we are using `getByRole("button")` query. Following elements will match: @@ -47,14 +51,14 @@ While following elements will not match: Button ``` -## 3. `*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return host elements +### 3. `*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return host elements `*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return [host elements](testing-env#host-and-composite-components), which is consistent with other queries. While potentially breaking, this should not cause issues in tests if you are using recommended queries and Jest Matchers from Jest Native package. Problematic cases may include: directly checking some prop values (without using Jest Native matchers), referencing other nodes using `parent` or `children` props, examining `type` property of `ReactTestInstance`, etc. -## 4. `container` API has been renamed to `UNSAFE_root`. +### 4. `container` API has been renamed to `UNSAFE_root`. Historically `container` was supposed to mimic the [RTL's container](https://testing-library.com/docs/react-testing-library/api/#container). However it turned out not so relevant in RNTL's environment, where we actually used it to return React Test Renderer's root instance. @@ -62,6 +66,6 @@ RNTL v12 introduces `root` API as an alternative that returns a root **host** el If you use `toBeOnTheScreen` matcher from [@testing-library/jest-native](https://github.com/testing-library/jest-native) your tests will fail because it uses the `container` api. To fix this, update `@testing-library/jest-native` to version 5.4.2. -# Full Changelog +## Full Changelog https://github.com/callstack/react-native-testing-library/compare/v11.5.2...v12.0.0 diff --git a/website/docs/MigrationV2.md b/website/docs/MigrationV2.md index c5a113fc8..ea9cab0c5 100644 --- a/website/docs/MigrationV2.md +++ b/website/docs/MigrationV2.md @@ -5,6 +5,10 @@ title: Migration to 2.0 This guide describes steps necessary to migrate from React Native Testing Library `v1.x` to `v2.0`. +import TOCInline from '@theme/TOCInline'; + + + ## Dropping Node 8 Node 8 reached its EOL more than 5 months ago, so it's about time to target the library to Node 10. If you used lower version, you'll have to upgrade to v10, but we recommend using the latest LTS version. diff --git a/website/docs/MigrationV7.md b/website/docs/MigrationV7.md index d362c2f4e..fdf421e99 100644 --- a/website/docs/MigrationV7.md +++ b/website/docs/MigrationV7.md @@ -2,6 +2,7 @@ id: migration-v7 title: Migration to 7.0 --- +import TOCInline from '@theme/TOCInline'; :::caution We renamed the `react-native-testing-library` npm package to `@testing-library/react-native`, officially joining the "Testing Library" family 🎉. @@ -9,14 +10,13 @@ We renamed the `react-native-testing-library` npm package to `@testing-library/r As the version 7.0 involves merging two libraries together, there are two variants for migration guide, dependent on library you used previously: -- [Guide for `react-native-testing-library` users](#guide-for-react-native-testing-library-users) -- [Guide for `@testing-library/react-native` users](#guide-for-testing-libraryreact-native-users) + -# Guide for `react-native-testing-library` users +## Guide for `react-native-testing-library` users This guide describes steps necessary to migrate from React Native Testing Library `v2.x` or `v6.0` to `v7.0`. -## Renaming the library +### Renaming the library 1. Install `@testing-library/react-native`. 1. Uninstall `react-native-testing-library`. @@ -26,7 +26,7 @@ You may have noticed a strange v2 to v7 upgrade, skipping versions 3, 4, 5 and 6 For branding purposes we keep the "React Native Testing Library" name, similar to "React Testing Library". Only the npm published package is changing. The code repository also stays the same under Callstack governance. -## New aliases +### New aliases To improve compatibility with React Testing Library, and ease the migration for `@testing-library/react-native` users using version below v7, we've introduced new aliases to our accessibility queries: @@ -36,7 +36,7 @@ To improve compatibility with React Testing Library, and ease the migration for We like the new names and consider removing the aliases in future releases. -## Renaming `ByPlaceholder` queries +### Renaming `ByPlaceholder` queries To improve compatibility with React Testing Library, and to ease the migration for `@testing-library/react-native` users using version below v7, we've renamed following queries: @@ -44,31 +44,31 @@ To improve compatibility with React Testing Library, and to ease the migration f Please replace all occurrences of these queries in your codebase. -## `fireEvent` support for disabled components +### `fireEvent` support for disabled components To improve compatibility with the real React Native environment `fireEvent` now performs checks whether the component is "disabled" before firing an event on it. It uses the Responder system to establish should the event fire, which resembles the actual React Native runtime closer than we used to. If your code contained any workarounds for preventing events firing on disabled events, you should now be able to remove them. -# Guide for `@testing-library/react-native` users +## Guide for `@testing-library/react-native` users This guide describes steps necessary to migrate from `@testing-library/react-native` from `v6.0` to `v7.0`. Although the name stays the same, this is a different library, sourced at [Callstack GitHub repository](https://github.com/callstack/react-native-testing-library). We made sure the upgrade path is as easy for you as possible. -## Renaming "wait" helpers +### Renaming "wait" helpers The `wait` and `waitForElement` helpers are replaced by `waitFor`. Please rename all occurrences of these in your codebase. -## Changes to `ByTestId` queries +### Changes to `ByTestId` queries The `ByTestId` queries don't accept RegExps. Please use strings instead. We're happy to accept PRs adding this functionality :). -## No `ByTitle` queries +### No `ByTitle` queries Our library doesn't implement `ByTitle` queries, which are targetting components with `title` prop, specifically `Button` and `RefreshControl`. If your tests only use `ByTitle` to target `Button` components, you can replace them with `ByText` queries, since React Native renders `Text` under the hood. If you need to query `RefreshControl` component and can't figure out other way around it, you can use e.g. `UNSAFE_getByProps({title})` query. -## No custom Jest configuration +### No custom Jest configuration Use the official React Native preset for Jest: @@ -83,7 +83,7 @@ Use the official React Native preset for Jest: We're told this also speeds up your tests startup on cold cache. Using official preset has another benefit – the library is compatible with any version of React Native without introducing breaking changes. -## Cleanup is included by default +### Cleanup is included by default Cleaning up (unmounting) components after each test is included by default in the same manner as in React Testing Library. Please remove this setup file from Jest config: @@ -97,15 +97,15 @@ Cleaning up (unmounting) components after each test is included by default in th You can opt-out of this behavior by running tests with `RNTL_SKIP_AUTO_CLEANUP=true` flag or importing from `@testing-library/react-native/pure`. We encourage you to keep the default though. -## No [NativeTestInstance](https://www.native-testing-library.com/docs/api-test-instance) abstraction +### No [NativeTestInstance](https://www.native-testing-library.com/docs/api-test-instance) abstraction We don't provide any abstraction over `ReactTestInstance` returned by queries, but allow to use it directly to access queried component's `props` or `type` for that example. -## No `container` nor `baseElement` returned from `render` +### No `container` nor `baseElement` returned from `render` There's no `container` returned from the `render` function. If you must, use `react-test-renderer` directly, although we advise against doing so. We also don't implement `baseElement` because of that, since there's no `document.documentElement` nor `container`. -## Firing events changes +### Firing events changes There are slight differences in how `fireEvent` works in both libraries: diff --git a/website/docs/MigrationV9.md b/website/docs/MigrationV9.md index 3da587dc6..1720bea5e 100644 --- a/website/docs/MigrationV9.md +++ b/website/docs/MigrationV9.md @@ -2,9 +2,12 @@ id: migration-v9 title: Migration to 9.0 --- +import TOCInline from '@theme/TOCInline'; Version 7.0 brought React Native Testing Library into the `@testing-library` family. Since it has been implemented independently from its web counterpart – the React Testing Library – there are some differences in the API and behavior. Version 9.0 solves several of these problems. + + ## Support for text match options a.k.a string precision API This is a backward compatible change. diff --git a/website/docs/Queries.md b/website/docs/Queries.md index e99cc5b8b..25a6aa83a 100644 --- a/website/docs/Queries.md +++ b/website/docs/Queries.md @@ -2,63 +2,36 @@ id: api-queries title: Queries --- +import TOCInline from '@theme/TOCInline'; -### Table of contents: - -- [Variants](#variants) - - [getBy](#getby) - - [getAllBy](#getallby) - - [queryBy](#queryby) - - [queryAllBy](#queryallby) - - [findBy](#findby) - - [findAllBy](#findallby) -- [Queries](#queries) - - [Options](#options) - - [`ByRole`](#byrole) - - [`ByText`](#bytext) - - [`ByPlaceholderText`](#byplaceholdertext) - - [`ByDisplayValue`](#bydisplayvalue) - - [`ByTestId`](#bytestid) - - [`ByLabelText`](#bylabeltext) - - [`ByHintText`, `ByA11yHint`, `ByAccessibilityHint`](#byhinttext-bya11yhint-byaccessibilityhint) - - [`ByA11yState`, `ByAccessibilityState` (deprecated)](#bya11ystate-byaccessibilitystate-deprecated) - - [`ByA11yValue`, `ByAccessibilityValue` (deprecated)](#bya11yvalue-byaccessibilityvalue-deprecated) -- [Common options](#common-options) - - [`includeHiddenElements` option](#includehiddenelements-option) -- [TextMatch](#textmatch) - - [Examples](#examples) - - [Precision](#precision) - - [Normalization](#normalization) -- [Unit testing helpers](#unit-testing-helpers) - - [`UNSAFE_ByType`](#unsafebytype) - - [`UNSAFE_ByProps`](#unsafebyprops) - -## Variants + + +## Query Variants > `getBy*` queries are shown by default in the [query documentation](#queries) > below. -### getBy +### `getBy*` queries `getBy*` queries return the first matching node for a query, and throw an error if no elements match or if more than one match is found. If you need to find more than one element, then use `getAllBy`. -### getAllBy +### `getAllBy*` queries `getAllBy*` queries return an array of all matching nodes for a query, and throw an error if no elements match. -### queryBy +### `queryBy*` queries `queryBy*` queries return the first matching node for a query, and return `null` if no elements match. This is useful for asserting an element that is not present. This throws if more than one match is found (use `queryAllBy` instead). -### queryAllBy +### `queryAllBy*` queries `queryAllBy*` queries return an array of all matching nodes for a query, and return an empty array (`[]`) when no elements match. -### findBy +### `findBy*` queries `findBy*` queries return a promise which resolves when a matching element is found. The promise is rejected if no elements match or if more than one match is found after a default timeout of 1000 ms. If you need to find more than one element, then use `findAllBy*`. -### findAllBy +### `findAllBy*` queries `findAllBy*` queries return a promise which resolves to an array of matching elements. The promise is rejected if no elements match after a default timeout of 1000 ms. @@ -74,7 +47,7 @@ In cases when your `findBy*` and `findAllBy*` queries throw when not able to fin In order to properly use `findBy*` and `findAllBy*` queries you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.61 (which comes with React >=16.9.0). ::: -## Queries +## Query Predicates _Note: most methods like this one return a [`ReactTestInstance`](https://reactjs.org/docs/test-renderer.html#testinstance) with following properties that you may be interested in:_ @@ -87,10 +60,6 @@ type ReactTestInstance = { }; ``` -### Options - -Usually query first argument can be a **string** or a **regex**. All queries take at least the [`hidden`](#hidden-option) option as an optionnal second argument and some queries accept more options which change string matching behaviour. See [TextMatch](#textmatch) for more info. - ### `ByRole` > getByRole, getAllByRole, queryByRole, queryAllByRole, findByRole, findAllByRole @@ -138,7 +107,7 @@ const element2 = screen.getByRole('button', { name: 'Hello' }); const element3 = screen.getByRole('button', { name: 'Hello', disabled: true }); ``` -#### Options +#### Options {#byrole-options} `name`: Finds an element with given `role`/`accessibilityRole` and an accessible name (equivalent to `byText` or `byLabelText` query). @@ -416,9 +385,11 @@ const element = screen.getByA11yValue({ now: 25 }); const element2 = screen.getByA11yValue({ text: /25/ }); ``` -## Common options +### Common options + +Usually query first argument can be a **string** or a **regex**. All queries take at least the [`hidden`](#hidden-option) option as an optionnal second argument and some queries accept more options which change string matching behaviour. See [TextMatch](#textmatch) for more info. -### `includeHiddenElements` option +#### `includeHiddenElements` option All queries have the `includeHiddenElements` option which affects whether [elements hidden from accessibility](./API.md#ishiddenfromaccessibility) are matched by the query. By default queries will not match hidden elements, because the users of the app would not be able to see such elements. @@ -443,7 +414,7 @@ expect( ).toBeOnTheScreen(); ``` -## TextMatch +## TextMatch type ```ts type TextMatch = string | RegExp; @@ -538,7 +509,7 @@ screen.getByText(node, 'text', { }); ``` -## Unit testing helpers +## Legacy unit testing helpers > Use sparingly and responsibly, escape hatches here diff --git a/website/docs/ReactNavigation.md b/website/docs/ReactNavigation.md index d27c80736..faa4a2571 100644 --- a/website/docs/ReactNavigation.md +++ b/website/docs/ReactNavigation.md @@ -3,6 +3,10 @@ id: react-navigation title: React Navigation --- +:::caution +This examples has not been updated for a long time and not showcase current recommended testing practices. We plan to update this document soon. +::: + This section deals with integrating `@testing-library/react-native` with `react-navigation`, using Jest. ## Stack Navigator diff --git a/website/docs/ReduxIntegration.md b/website/docs/ReduxIntegration.md index 16ef46fff..49620a236 100644 --- a/website/docs/ReduxIntegration.md +++ b/website/docs/ReduxIntegration.md @@ -3,6 +3,11 @@ id: redux-integration title: Redux Integration --- +:::caution +This examples has not been updated for a long time and not showcase current recommended testing practices. We plan to update this document soon. +::: + + This section deals with testing RN applications developed with Redux. We will be developing a simple TODO application capable of adding and removing an item. Once included, the timestamp is included. ## Setting up diff --git a/website/docs/TestingEnvironment.md b/website/docs/TestingEnvironment.md index 5a5d09a4f..594c33534 100644 --- a/website/docs/TestingEnvironment.md +++ b/website/docs/TestingEnvironment.md @@ -2,23 +2,24 @@ id: testing-env title: Testing Environment --- +import TOCInline from '@theme/TOCInline'; :::info This document is intended for more advanced audience. You should be able to write integration or component tests without reading this. It is intended for people who want to better understand internals of our testing environment, e.g. in order to contribute to the codebase. ::: -## Testing Environment - React Native Testing Library allows you to write integration and component tests for your React Native app or library. While the JSX code used in tests closely resembles your React Native app, the things are not quite as simple as they might appear. In this document we will describe the key elements of our testing environment and highlight things to be aware of when writing more advanced tests or diagnosing issues. -### React renderers + + +## React renderers React allows you to write declarative code using JSX, write function or class components, or use hooks like `useState`. In order to output the results of your components it needs to work with a renderer. Every React app uses some type of renderer: React Native is a renderer for mobile apps, web apps use React DOM, and there are other more [specialised renderers](https://github.com/chentsulin/awesome-react-renderer) that can e.g. render to console or HTML canvas. When you run your tests in React Native Testing Library, somewhat contrary to what the name suggest, they are actually **not** using React Native renderer. This is because this renderer needs to be run on iOS or Android operating system, so it would need to run on device or simulator. -### React Test Renderer +## React Test Renderer Instead, RNTL uses React Test Renderer which is a specialised renderer that allows rendering to pure JavaScript objects without access to mobile OS, and that can run in a Node.js environment using Jest (or any other JavaScript test runner). @@ -39,7 +40,7 @@ Disadvantages: It’s worth noting that React Testing Library (web one), works a bit different. While RTL also runs in Jest, it also has access to simulated browser DOM environment from jsdom package, so it can use a regular React DOM renderer. Unfortunately, there is no similar React Native runtime environment package. This is probably due to to the fact that while browser environment is well defined and highly standardised, the React Native environment is in constant evolution, in sync with the evolution of underlying OS-es. Maintaining such environment would require duplicating countless React Native behaviours, and keeping that in sync as React Native evolves. -### Element tree +## Element tree Invoking `render()` function results in creation of an element tree. This is done internally by invoking `TestRenderer.create()` method. The output tree represents your React Native component tree, each node of that tree is an “instance” of some React component (to be more precise: each node represents a React fiber, and only class components have instances, while function components store the hook state using fiber). @@ -58,7 +59,7 @@ interface ReactTestInstance { Based on: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer/index.d.ts -### Host and composite components +## Host and composite components One of the most important aspects of the element tree is that it is composed of both host and composite components: @@ -94,7 +95,7 @@ Not all React Native components are organised this way, e.g. when you use `Press * children prop passed in JSX ``` -#### Differentiating between host and composite elements +### Differentiating between host and composite elements Any easy way to differentiate between host and composite elements is the `type` prop of `ReactTestInstance`: @@ -109,11 +110,11 @@ function isHostElement(element: ReactTestInstance) { } ``` -### Tree nodes +## Tree nodes We encourage you to only assert values on host views in your tests, because they represent the user interface view and controls that the user will be able to see and interact with. Users cannot see, or interact with, composite views as they exist purely in JavaScript domain and do not generate any visible UI. -#### Asserting props +### Asserting props As an example, if you make assertions on a `style` prop of a composite element, there is no guarantee that the style will be visible to the user, as the component author can forget to pass this prop to some underlying `View` or other host component. Similarly `onPress` event handler on a composite prop can be unreachable by the user. @@ -129,7 +130,7 @@ function ForgotToPassPropsButton({ title, onPress, style }) { In the above example user defined components accepts both `onPress` and `style` props but does not pass it (through `Pressable`) to host views, so these props will not affect the user interface. Additionally, React Native and other libraries might pass some of the props under different names or transform their values between composite and host components. -### Tree navigation +## Tree navigation :::caution You should avoid navigating over element tree, as this makes your testing code fragile and may result in false positives. This section is more relevant for people how want to contribute to our codebase. @@ -139,7 +140,7 @@ When navigating a tree of react elements using `parent` or `children` props of a Inside RNTL we have various tree navigation helpers: `getHostParent`, `getHostChildren`, etc. These are intentionally not exported as using them is not a recommended practice. -### Queries +## Queries Most of the Testing Library queries return host components, in order to encourage best practices described above. diff --git a/website/docs/Troubleshooting.md b/website/docs/Troubleshooting.md index 1dde5c4b2..4c0db9c64 100644 --- a/website/docs/Troubleshooting.md +++ b/website/docs/Troubleshooting.md @@ -2,8 +2,11 @@ id: troubleshooting title: Troubleshooting --- +import TOCInline from '@theme/TOCInline'; -This guide describes common issues found by users when integrating React Native Test Library to their projects. +This guide describes common issues found by users when integrating React Native Test Library to their projects: + + ## Matching React Native, React & React Test Renderer versions diff --git a/website/docs/UnderstandingAct.md b/website/docs/UnderstandingAct.md index 2a8f891bd..5a49f385b 100644 --- a/website/docs/UnderstandingAct.md +++ b/website/docs/UnderstandingAct.md @@ -2,10 +2,13 @@ id: understanding-act title: Understanding Act function --- +import TOCInline from '@theme/TOCInline'; When writing RNTL tests one of the things that confuses developers the most are cryptic [`act()`](https://reactjs.org/docs/testing-recipes.html#act) function errors logged into console. In this article I will try to build an understanding of the purpose and behaviour of `act()` so you can build your tests with more confidence. -## The act warnings + + +## `act` warnings Let’s start with typical `act()` warnings logged to console. There are two kinds of these issues, let’s call the first one the "sync `act()`" warning: @@ -28,7 +31,7 @@ testing behaviour, interleaving multiple act calls and mixing their scopes. You - await act(async () => ...); ``` -## Synchronous act +## Synchronous `act` ### Responsibility @@ -100,7 +103,7 @@ As of React version of 18.1.0, the `act` implementation is defined in the [React RNTL exports `act` for convenience of the users as defined in the [act.ts source file](https://github.com/callstack/react-native-testing-library/blob/main/src/act.ts). That file refers to [ReactTestRenderer.js source](https://github.com/facebook/react/blob/ce13860281f833de8a3296b7a3dad9caced102e9/packages/react-test-renderer/src/ReactTestRenderer.js#L52) file from React Test Renderer package, which finally leads to React act implementation in ReactAct.js (already mentioned above). -## Asynchronous act +## Asynchronous `act` So far we have seen synchronous version of `act` which runs its callback immediately. This can deal with things like synchronous effects or mocks using already resolved promises. However, not all component code is synchronous. Frequently our components or mocks contain some asynchronous behaviours like `setTimeout` calls or network calls. Starting from React 16.9, `act` can also be called in asynchronous mode. In such case `act` implementation checks that the passed callback returns [object resembling promise](https://github.com/facebook/react/blob/ce13860281f833de8a3296b7a3dad9caced102e9/packages/react/src/ReactAct.js#L60). diff --git a/website/docs/UserEvent.md b/website/docs/UserEvent.md index 6c799d937..81cee6758 100644 --- a/website/docs/UserEvent.md +++ b/website/docs/UserEvent.md @@ -2,20 +2,9 @@ id: user-event title: User Event --- +import TOCInline from '@theme/TOCInline'; -### Table of contents - -- [Comparison with Fire Event API](#comparison-with-fire-event-api) -- [`setup()`](#setup) - - [Options](#options) -- [`press()`](#press) -- [`longPress()`](#longpress) - - [Options](#options-1) -- [`type()`](#type) - - [Options](#options-2) - - [Sequence of events](#sequence-of-events) -- [`clear()`](#clear) - - [Sequence of events](#sequence-of-events-1) + :::caution User Event API is in beta stage. @@ -47,7 +36,7 @@ const user = userEvent.setup(); Creates an User Event object instance which can be used to trigger events. -### Options +### Options {#setup-options} - `delay` - controls the default delay between subsequent events, e.g. keystrokes. - `advanceTimers` - time advancement utility function that should be used for fake timers. The default setup handles both real timers and Jest fake timers. @@ -85,7 +74,7 @@ await user.longPress(element); Simulates a long press user interaction. In React Native the `longPress` event is emitted when the press duration exceeds long press threshold (by default 500 ms). In other aspects this actions behaves similar to regular `press` action, e.g. by emitting `pressIn` and `pressOut` events. The press duration is customisable through the options. This should be useful if you use the `delayLongPress` prop. When using real timers this will take 500 ms so it is highly recommended to use that API with fake timers to prevent test taking a long time to run. -### Options +### Options {#longpress-options} - `duration` - duration of the press in miliseconds. Default value is 500 ms. ## `type()` @@ -114,7 +103,7 @@ This function supports only host `TextInput` elements. Passing other element typ This function will add text to the text already present in the text input (as specified by `value` or `defaultValue` props). In order to replace existing text, use [`clear()`](#clear) helper first. ::: -### Options +### Options {#type-options} - `skipPress` - if true, `pressIn` and `pressOut` events will not be triggered. - `submitEditing` - if true, `submitEditing` event will be triggered after typing the text. diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 1c2dccdc1..101ce8aa6 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -24,6 +24,10 @@ const siteConfig = { { href: repoUrl, label: 'GitHub', position: 'right' }, ], }, + tableOfContents: { + minHeadingLevel: 2, + maxHeadingLevel: 3, + }, footer: { style: 'dark', links: [