diff --git a/website/docs/13.x/docs/api/events/fire-event.mdx b/website/docs/13.x/docs/api/events/fire-event.mdx index c2ab6895..65af25fb 100644 --- a/website/docs/13.x/docs/api/events/fire-event.mdx +++ b/website/docs/13.x/docs/api/events/fire-event.mdx @@ -1,8 +1,6 @@ # Fire Event API -```ts -function fireEvent(element: ReactTestInstance, eventName: string, ...data: unknown[]): void; -``` +## `fireEvent` {#fire-event} :::note For common events like `press` or `type` it's recommended to use [User Event API](docs/api/events/user-event) as it offers @@ -11,6 +9,10 @@ more realistic event simulation by emitting a sequence of events with proper eve Use Fire Event for cases not supported by User Event and for triggering event handlers on composite components. ::: +```ts +function fireEvent(element: ReactTestInstance, eventName: string, ...data: unknown[]): void; +``` + The `fireEvent` API allows you to trigger all kinds of event handlers on both host and composite components. It will try to invoke a single event handler traversing the component tree bottom-up from passed element and trying to find enabled event handler named `onXxx` when `xxx` is the name of the event passed. Unlike User Event, this API does not automatically pass event object to event handler, this is responsibility of the user to construct such object. @@ -57,14 +59,17 @@ FireEvent exposes convenience methods for common events like: `press`, `changeTe ### `fireEvent.press` {#press} -``` -fireEvent.press: (element: ReactTestInstance, ...data: Array) => void -``` - :::note It is recommended to use the User Event [`press()`](docs/api/events/user-event#press) helper instead as it offers more realistic simulation of press interaction, including pressable support. ::: +```tsx +fireEvent.press: ( + element: ReactTestInstance, + ...data: Array, +) => void +``` + Invokes `press` event handler on the element or parent element in the tree. ```jsx @@ -93,14 +98,17 @@ expect(onPressMock).toHaveBeenCalledWith(eventData); ### `fireEvent.changeText` {#change-text} -``` -fireEvent.changeText: (element: ReactTestInstance, ...data: Array) => void -``` - :::note It is recommended to use the User Event [`type()`](docs/api/events/user-event#type) helper instead as it offers more realistic simulation of text change interaction, including key-by-key typing, element focus, and other editing events. ::: +```tsx +fireEvent.changeText: ( + element: ReactTestInstance, + ...data: Array, +) => void +``` + Invokes `changeText` event handler on the element or parent element in the tree. ```jsx @@ -121,8 +129,15 @@ fireEvent.changeText(screen.getByPlaceholderText('Enter data'), CHANGE_TEXT); ### `fireEvent.scroll` {#scroll} -``` -fireEvent.scroll: (element: ReactTestInstance, ...data: Array) => void +:::note +Prefer using [`user.scrollTo`](docs/api/events/user-event#scrollto) over `fireEvent.scroll` for `ScrollView`, `FlatList`, and `SectionList` components. User Event provides a more realistic event simulation based on React Native runtime behavior. +::: + +```tsx +fireEvent.scroll: ( + element: ReactTestInstance, + ...data: Array, +) => void ``` Invokes `scroll` event handler on the element or parent element in the tree. @@ -152,12 +167,10 @@ fireEvent.scroll(screen.getByText('scroll-view'), eventData); ``` :::note - Prefer using [`user.scrollTo`](docs/api/events/user-event#scrollto) over `fireEvent.scroll` for `ScrollView`, `FlatList`, and `SectionList` components. User Event provides a more realistic event simulation based on React Native runtime behavior. - ::: -## `fireEventAsync` +## `fireEventAsync` {#fire-event-async} :::info RNTL minimal version @@ -173,7 +186,7 @@ async function fireEventAsync( ): Promise; ``` -The `fireEventAsync` function is the async version of `fireEvent` designed for working with React 19 and React Suspense. This function uses async `act` function internally to ensure all pending React updates are executed during event handling. +The `fireEventAsync` function is the async version of [`fireEvent`](#fire-event) designed for working with React 19 and React Suspense. This function uses async `act` function internally to ensure all pending React updates are executed during event handling. ```jsx import { renderAsync, screen, fireEventAsync } from '@testing-library/react-native'; @@ -190,24 +203,45 @@ Like `fireEvent`, `fireEventAsync` also provides convenience methods for common ### `fireEventAsync.press` {#async-press} -``` -fireEventAsync.press: (element: ReactTestInstance, ...data: Array) => Promise +:::note +It is recommended to use the User Event [`press()`](docs/api/events/user-event#press) helper instead as it offers more realistic simulation of press interaction, including pressable support. +::: + +```tsx +fireEventAsync.press: ( + element: ReactTestInstance, + ...data: Array, +) => Promise ``` -Async version of `fireEvent.press` designed for React 19 and React Suspense. Use when press event handlers trigger suspense boundaries. +Async version of [`fireEvent.press`](#press) designed for React 19 and React Suspense. Use when `press` event handlers trigger suspense boundaries. ### `fireEventAsync.changeText` {#async-change-text} -``` -fireEventAsync.changeText: (element: ReactTestInstance, ...data: Array) => Promise +:::note +It is recommended to use the User Event [`type()`](docs/api/events/user-event#type) helper instead as it offers more realistic simulation of text change interaction, including key-by-key typing, element focus, and other editing events. +::: + +```tsx +fireEventAsync.changeText: ( + element: ReactTestInstance, + ...data: Array, +) => Promise ``` -Async version of `fireEvent.changeText` designed for React 19 and React Suspense. Use when changeText event handlers trigger suspense boundaries. +Async version of [`fireEvent.changeText`](#change-text) designed for React 19 and React Suspense. Use when `changeText` event handlers trigger suspense boundaries. ### `fireEventAsync.scroll` {#async-scroll} -``` -fireEventAsync.scroll: (element: ReactTestInstance, ...data: Array) => Promise +:::note +Prefer using [`user.scrollTo`](docs/api/events/user-event#scrollto) over `fireEventAsync.scroll` for `ScrollView`, `FlatList`, and `SectionList` components. User Event provides a more realistic event simulation based on React Native runtime behavior. +::: + +```tsx +fireEventAsync.scroll: ( + element: ReactTestInstance, + ...data: Array, +) => Promise ``` -Async version of `fireEvent.scroll` designed for React 19 and React Suspense. Use when scroll event handlers trigger suspense boundaries. +Async version of [`fireEvent.scroll`](#scroll) designed for React 19 and React Suspense. Use when `scroll` event handlers trigger suspense boundaries. diff --git a/website/docs/13.x/docs/api/events/user-event.mdx b/website/docs/13.x/docs/api/events/user-event.mdx index f554bc53..4684faeb 100644 --- a/website/docs/13.x/docs/api/events/user-event.mdx +++ b/website/docs/13.x/docs/api/events/user-event.mdx @@ -226,10 +226,6 @@ Events will not be emitted if the `editable` prop is set to `false`. ## `scrollTo()` \{#scroll-to} -:::note -`scrollTo` interaction has been introduced in RNTL v12.4.0. -::: - ```ts scrollTo( element: ReactTestInstance, diff --git a/website/docs/13.x/docs/api/misc/render-hook.mdx b/website/docs/13.x/docs/api/misc/render-hook.mdx index eab8e856..a581ad12 100644 --- a/website/docs/13.x/docs/api/misc/render-hook.mdx +++ b/website/docs/13.x/docs/api/misc/render-hook.mdx @@ -1,5 +1,7 @@ # `renderHook` function +## `renderHook` + ```ts function renderHook( hookFn: (props?: Props) => Result, @@ -41,15 +43,15 @@ Callback is a function that is called each `render` of the test component. This 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` +### `options` A `RenderHookOptions` object to modify the execution of the `callback` function, containing the following properties: -### `initialProps` {#initial-props} +#### `initialProps` {#initial-props} The initial values to pass as `props` to the `callback` function of `renderHook`. The `Props` type is determined by the type passed to or inferred by the `renderHook` call. -### `wrapper` +#### `wrapper` 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`. @@ -58,7 +60,7 @@ A React component to wrap the test component in when rendering. This is usually Set to `false` to disable concurrent rendering. Otherwise, `render` will default to using concurrent rendering used in the React Native New Architecture. -## Result +### Result ```ts interface RenderHookResult { @@ -70,23 +72,23 @@ 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. -## Examples +### Examples Here we present some extra examples of using `renderHook` API. -### With `initialProps` +#### With `initialProps` ```ts const useCount = (initialCount: number) => { @@ -117,7 +119,7 @@ it('should increment count', () => { }); ``` -### With `wrapper` +#### With `wrapper` ```tsx it('should use context value', () => { @@ -150,18 +152,18 @@ Async versions of `renderHook` designed for working with React 19 and React Susp ```ts interface RenderHookAsyncResult { result: { current: Result }; - rerender: (props: Props) => Promise; - unmount: () => Promise; + rerenderAsync: (props: Props) => Promise; + unmountAsync: () => Promise; } ``` -The `RenderHookAsyncResult` differs from `RenderHookResult` in that `rerender` and `unmount` are async functions. +The `RenderHookAsyncResult` differs from `RenderHookResult` in that `rerenderAsync` and `unmountAsync` are async functions. ```ts import { renderHookAsync, act } from '@testing-library/react-native'; test('should handle async hook behavior', async () => { - const { result, rerender } = await renderHookAsync(useAsyncHook); + const { result, rerenderAsync } = await renderHookAsync(useAsyncHook); // Test initial state expect(result.current.loading).toBe(true); @@ -172,7 +174,7 @@ test('should handle async hook behavior', async () => { }); // Re-render to get updated state - await rerender(); + await rerenderAsync(); expect(result.current.loading).toBe(false); }); ``` diff --git a/website/docs/13.x/docs/api/render.mdx b/website/docs/13.x/docs/api/render.mdx index b7560e94..9ae4417a 100644 --- a/website/docs/13.x/docs/api/render.mdx +++ b/website/docs/13.x/docs/api/render.mdx @@ -1,4 +1,6 @@ -# `render` function +# `render` API + +## `render` function {#render} ```jsx function render( @@ -20,11 +22,11 @@ test('basic test', () => { > When using React context providers, like Redux Provider, you'll likely want to wrap rendered component with them. In such cases, it's convenient to create your own custom `render` method. [Follow this great guide on how to set this up](https://testing-library.com/docs/react-testing-library/setup#custom-render). -## Options +### Options The behavior of the `render` method can be customized by passing various options as a second argument of the `RenderOptions` type: -### `wrapper` option +#### `wrapper` ```ts wrapper?: React.ComponentType, @@ -32,12 +34,12 @@ wrapper?: React.ComponentType, This option allows you to wrap the tested component, passed as the first option to the `render()` function, in an additional wrapper component. This is useful for creating reusable custom render functions for common React Context providers. -### `concurrentRoot` option {#concurrent-root} +#### `concurrentRoot` {#concurrent-root} Set to `false` to disable concurrent rendering. Otherwise, `render` will default to using concurrent rendering used in the React Native New Architecture. -### `createNodeMock` option +#### `createNodeMock` {#create-node-mock} ```ts createNodeMock?: (element: React.Element) => unknown, @@ -45,7 +47,7 @@ createNodeMock?: (element: React.Element) => unknown, This option allows you to pass `createNodeMock` option to `ReactTestRenderer.create()` method in order to allow for custom mock refs. You can learn more about this option from [React Test Renderer documentation](https://reactjs.org/docs/test-renderer.html#ideas). -### `unstable_validateStringsRenderedWithinText` option +#### `unstable_validateStringsRenderedWithinText` ```ts unstable_validateStringsRenderedWithinText?: boolean; @@ -59,13 +61,13 @@ This **experimental** option allows you to replicate React Native behavior of th React Test Renderer does not enforce this check; hence, by default, React Native Testing Library also does not check this. That might result in runtime errors when running your code on a device, while the code works without errors in tests. -## Result +### Result The `render` function returns the same queries and utilities as the [`screen`](docs/api/screen) object. We recommended using the `screen` object as more developer-friendly way. See [this article](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen) from Kent C. Dodds for more details. -## `renderAsync` function +## `renderAsync` function {#render-async} :::info RNTL minimal version @@ -80,7 +82,7 @@ async function renderAsync( ): Promise; ``` -The `renderAsync` function is the async version of `render` designed for working with React 19 and React Suspense. This function uses async `act` function internally to ensure all pending React updates are executed during rendering. +The `renderAsync` function is the async version of [`render`](#render) designed for working with React 19 and React Suspense. This function uses async `act` function internally to ensure all pending React updates are executed during rendering. ```jsx import { renderAsync, screen } from '@testing-library/react-native'; diff --git a/website/docs/13.x/docs/api/screen.mdx b/website/docs/13.x/docs/api/screen.mdx index d4016d4f..d9cf2586 100644 --- a/website/docs/13.x/docs/api/screen.mdx +++ b/website/docs/13.x/docs/api/screen.mdx @@ -46,16 +46,14 @@ Re-render the in-memory tree with a new root element. This simulates a React upd _Also available under `updateAsync` alias_ :::info RNTL minimal version - This API requires RNTL v13.3.0 or later. - ::: ```ts function rerenderAsync(element: React.Element): Promise; ``` -Async versions of `rerender` designed for working with React 19 and React Suspense. This method uses async `act` function internally to ensure all pending React updates are executed during updating. +Async versions of [`rerender`](#rerender) designed for working with React 19 and React Suspense. This method uses async `act` function internally to ensure all pending React updates are executed during updating. ```jsx import { renderAsync, screen } from '@testing-library/react-native'; @@ -63,9 +61,7 @@ import { renderAsync, screen } from '@testing-library/react-native'; test('async rerender test', async () => { await renderAsync(); - // Use async rerender when component has suspense or async behavior await screen.rerenderAsync(); - expect(screen.getByText('updated')).toBeOnTheScreen(); }); ``` @@ -87,21 +83,17 @@ Usually you should not need to call `unmount` as it is done automatically if you ### `unmountAsync` :::info RNTL minimal version - This API requires RNTL v13.3.0 or later. - ::: ```ts function unmountAsync(): Promise; ``` -Async version of `unmount` designed for working with React 19 and React Suspense. This method uses async `act` function internally to ensure all pending React updates are executed during unmounting. +Async version of [`unmount`](#unmount) designed for working with React 19 and React Suspense. This method uses async `act` function internally to ensure all pending React updates are executed during unmounting. :::note - Usually you should not need to call `unmountAsync` as it is done automatically if your test runner supports `afterEach` hook (like Jest, mocha, Jasmine). - ::: ### `debug` diff --git a/website/docs/13.x/docs/guides/_meta.json b/website/docs/13.x/docs/guides/_meta.json index 5ae12d7e..4b5f8b68 100644 --- a/website/docs/13.x/docs/guides/_meta.json +++ b/website/docs/13.x/docs/guides/_meta.json @@ -1 +1 @@ -["how-to-query", "troubleshooting", "faq", "community-resources"] +["how-to-query", "react-19", "troubleshooting", "faq", "community-resources"] diff --git a/website/docs/13.x/docs/guides/react-19.mdx b/website/docs/13.x/docs/guides/react-19.mdx new file mode 100644 index 00000000..6a98a860 --- /dev/null +++ b/website/docs/13.x/docs/guides/react-19.mdx @@ -0,0 +1,68 @@ +# React 19 & Suspense Support + +React 19 introduced full support for React Suspense, [`React.use()`](https://react.dev/reference/react/use), and other async rendering features to React Native [0.78.0](https://github.com/facebook/react-native/releases/tag/v0.78.0). + +When testing components that use these features, React requires the [`async act`](https://react.dev/reference/react/act) helper to handle async state updates. This means React Native Testing Library needs new async versions of its core APIs. These async APIs work with both React 18 and React 19. + +## New async APIs + +RNTL 13.3 introduces async versions of the core testing APIs to handle React 19's async rendering: + +**Rendering APIs:** +- **[`renderAsync`](docs/api/render#render-async)** - async version of `render` +- **[`screen.rerenderAsync`](docs/api/screen#rerender-async)** - async version of `screen.rerender` +- **[`screen.unmountAsync`](docs/api/screen#unmount-async)** - async version of `screen.unmount` + +**Event APIs:** +- **[`fireEventAsync`](docs/api/events/fire-event#fire-event-async)** - async version of `fireEvent` + +## APIs that remain unchanged + +Many existing APIs continue to work without modification: + +- **[Query methods](docs/api/queries)**: `screen.getBy*`, `screen.queryBy*`, `screen.findBy*` - all work the same +- **[User Event API](docs/api/events/user-event)** - already async, so no API changes needed +- **[Jest matchers](docs/api/jest-matchers)** - work with already-rendered output, so no changes required + +## What changes in your tests + +### Making tests async + +The main change is using [`renderAsync`](docs/api/render#renderasync) instead of `render`, which requires: +1. Making your test function `async` +2. Adding `await` before `renderAsync` + +```tsx +// Synchronous approach (React 18 pattern) +test('my component', () => { + render(); + expect(screen.getByText('Hello')).toBeOnTheScreen(); +}); + +// Async approach (React 19 ready) +test('my component', async () => { + await renderAsync(); + expect(screen.getByText('Hello')).toBeOnTheScreen(); +}); +``` + +### When to use async APIs + +Use the async APIs when your components: +- Use React Suspense for data fetching or code splitting +- Call `React.use()` for reading promises or context +- Have async state updates that need proper `act()` handling + +## Migration strategy + +### New projects +Use the async-ready APIs (`renderAsync`, User Event, Jest Matchers, etc.) from the start. They work with both React 18 and React 19. + +### Existing projects +You can migrate gradually: +- **Existing tests** continue to work with synchronous APIs (`render`, etc.) +- **New tests** should use async APIs +- **Tests with Suspense/`React.use()`** must use async APIs + +### Future direction +Async APIs will become the default recommendation as React 19 adoption grows. Starting with them now saves migration effort later.