Skip to content

Latest commit

 

History

History
574 lines (402 loc) · 18.6 KB

File metadata and controls

574 lines (402 loc) · 18.6 KB

RNTL v14 API Reference

Complete API reference for @testing-library/react-native v14.x (React 19+).

Test renderer: test-renderer (not react-test-renderer) Element type: HostElement (not ReactTestInstance)

Table of Contents

  • Core Pattern
  • Key Rule: Always await
  • render
  • screen
  • Queries
  • User Event
  • Fire Event
  • Jest Matchers
  • Async Utilities
  • Other Helpers
  • renderHook
  • act
  • Configuration
  • Accessibility
  • Removed APIs
  • Migration Codemods

Core Pattern

import { render, screen, userEvent } from '@testing-library/react-native';

jest.useFakeTimers(); // recommended when using userEvent

test('description', async () => {
  const user = userEvent.setup();
  await render(<Component />); // async in v14 — always await

  const button = screen.getByRole('button', { name: 'Submit' });
  await user.press(button);

  expect(screen.getByText('Done')).toBeOnTheScreen();
});

Key Rule: Always await

In v14, the following APIs are async and must always be awaited:

  • await render(<Component />)
  • await fireEvent.press(element) (and all fireEvent variants)
  • await screen.rerender(<Component />)
  • await screen.unmount()
  • await renderHook(() => useHook())
  • await act(() => { ... }) (even with sync callbacks)

render

async function render(
  component: React.Element<any>,
  options?: RenderOptions,
): Promise<RenderResult>;

render returns a Promise<RenderResult> — always await it.

There is no renderAsync in v14. The standard render is already async.

Options

Option Type Description
wrapper React.ComponentType<any> Wraps tested component (useful for context providers)
createNodeMock (element) => unknown Custom mock refs

Note: concurrentRoot and unstable_validateStringsRenderedWithinText are removed in v14 (concurrent rendering is always on, string validation is always on).

Example

import { render, screen } from '@testing-library/react-native';

await render(<MyApp />);
expect(screen.getByRole('button', { name: 'start' })).toBeOnTheScreen();

// With wrapper
await render(<MyComponent />, {
  wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
});

screen

let screen: {
  ...queries;
  rerender(element: React.Element<unknown>): Promise<void>;  // async
  unmount(): Promise<void>;                                    // async
  debug(options?: { message?: string; mapProps?: MapPropsFunction }): void;
  toJSON(): RendererJSON | null;
  container: HostElement;  // safe root host element
  root: HostElement;       // root host element
};

The screen object provides queries and utilities for the currently rendered UI. Assigned after render(), cleared after each test via auto-cleanup.

Methods

  • rerender(element) — Re-render with new root element. Async — must await. Triggers lifecycle events.
  • unmount() — Unmount the tree. Async — must await. Usually not needed (auto-cleanup).
  • debug({ message?, mapProps? }) — Pretty-print the rendered tree. Use mapProps to filter/transform props in output.
  • toJSON() — Get JSON representation (for snapshot testing).
  • container — Root host element (safe accessor, replaces UNSAFE_root).
  • root — Root host element.

Note: UNSAFE_root is removed in v14. Use container or root instead.


Queries

Each query = variant + predicate (e.g., getByRole = getBy + ByRole).

Query Variants

Variant Assertion Return Type Async
getBy* Exactly one match HostElement (throws if 0 or >1) No
getAllBy* At least one match HostElement[] (throws if 0) No
queryBy* Zero or one match HostElement | null (throws if >1) No
queryAllBy* No assertion HostElement[] (empty if 0) No
findBy* Exactly one match Promise<HostElement> Yes
findAllBy* At least one match Promise<HostElement[]> Yes

findBy* / findAllBy* accept optional waitForOptions: { timeout?, interval?, onTimeout? }.

Query Predicates

*ByRole (preferred)

getByRole(role: TextMatch, options?: {
  name?: TextMatch;
  disabled?: boolean;
  selected?: boolean;
  checked?: boolean | 'mixed';
  busy?: boolean;
  expanded?: boolean;
  value?: { min?: number; max?: number; now?: number; text?: TextMatch };
  includeHiddenElements?: boolean;
}): HostElement;

Matches elements by role or accessibilityRole. Element must be an accessibility element:

  • Text, TextInput, Switch are by default
  • View needs accessible={true} (or use Pressable/TouchableOpacity)

Common roles: button, text, heading (alias: header), searchbox, switch, checkbox, radio, img, link, alert, menu, menuitem, tab, tablist, progressbar, slider, spinbutton, timer, toolbar.

screen.getByRole('button', { name: 'Submit' });
screen.getByRole('button', { name: 'Submit', disabled: true });
screen.getByRole('checkbox', { checked: true });
screen.getByRole('slider', { value: { now: 50, min: 0, max: 100 } });

*ByLabelText

getByLabelText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches by aria-label/accessibilityLabel or text content of element referenced by aria-labelledby/accessibilityLabelledBy.

*ByPlaceholderText

getByPlaceholderText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches TextInput by placeholder prop.

*ByText

getByText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches by text content. Joins <Text> siblings to find matches (like RN runtime).

*ByDisplayValue

getByDisplayValue(value: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches TextInput by current display value.

*ByHintText

getByHintText(hint: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches by accessibilityHint prop. Also available as getByA11yHint / getByAccessibilityHint.

*ByTestId (last resort)

getByTestId(testId: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;

Matches by testID prop. Use only when other queries don't work.

TextMatch

type TextMatch = string | RegExp;
  • String: exact full match by default. Use { exact: false } for case-insensitive substring match.
  • RegExp: substring match by default. Use anchors (^...$) for full match.
screen.getByText('Hello World'); // exact full match
screen.getByText('llo Worl', { exact: false }); // substring, case-insensitive
screen.getByText(/World/); // regex substring
screen.getByText(/^hello world$/i); // regex full match, case-insensitive

Common Query Options

  • includeHiddenElements (alias: hidden): Include elements hidden from accessibility. Default: false.
  • exact: Default true. When false, matches substrings case-insensitively. No effect on RegExp.
  • normalizer: Custom text normalization function. Default trims whitespace and collapses multiple spaces. Use getDefaultNormalizer({ trim?, collapseWhitespace? }) to customize.

User Event

Prefer userEvent over fireEvent. User Event simulates realistic interaction sequences on host elements only, with proper event data.

Setup

const user = userEvent.setup(options?: { delay?: number; advanceTimers?: (delay: number) => Promise<void> | void });

press(element)

await user.press(element): Promise<void>;

Full press lifecycle: pressInpresspressOut. Minimum 130ms. Use fake timers to speed up.

longPress(element, options?)

await user.longPress(element, { duration?: number }): Promise<void>;

Long press (default 500ms). Emits pressIn, longPress, pressOut.

type(element, text, options?)

await user.type(textInput, text: string, options?: {
  skipPress?: boolean;    // skip pressIn/pressOut
  skipBlur?: boolean;     // skip endEditing/blur
  submitEditing?: boolean; // trigger submitEditing
}): Promise<void>;

Character-by-character typing on TextInput. Appends to existing text (use clear() first to replace).

Event sequence per character: keyPresschangechangeTextselectionChange (+ contentSizeChange for multiline).

clear(element)

await user.clear(textInput): Promise<void>;

Clears TextInput content. Events: focusselectionChangekeyPresschangechangeTextselectionChangeendEditingblur.

paste(element, text)

await user.paste(textInput, text: string): Promise<void>;

Pastes text into TextInput. Events: focusselectionChangechangechangeTextselectionChangeendEditingblur.

scrollTo(element, options)

await user.scrollTo(scrollView, options: {
  y: number; momentumY?: number;   // vertical scroll
  // OR
  x: number; momentumX?: number;   // horizontal scroll
  contentSize?: { width: number; height: number };
  layoutMeasurement?: { width: number; height: number };
}): Promise<void>;

Scrolls a host ScrollView (including FlatList). Match scroll direction to horizontal prop. Pass contentSize and layoutMeasurement for FlatList updates. Remembers last scroll position between calls.


Fire Event

Use when userEvent doesn't support the event or when triggering events on composite elements.

Async in v14 — always await:

async function fireEvent(
  element: HostElement,
  eventName: string,
  ...data: unknown[]
): Promise<void>;

Traverses tree bottom-up looking for onXxx handler. Does not auto-pass event data.

Convenience Methods

await fireEvent.press(element, ...data): Promise<void>;
await fireEvent.changeText(element, ...data): Promise<void>;
await fireEvent.scroll(element, ...data): Promise<void>;

There is no fireEventAsync in v14. The standard fireEvent is already async.


Jest Matchers

Available automatically with any @testing-library/react-native import. No setup needed.

Element Existence

Matcher Description
toBeOnTheScreen() Element is attached to the element tree

Element Content

Matcher Signature Description
toHaveTextContent() (text: string | RegExp, options?: { exact?, normalizer? }) Text content match
toContainElement() (element: HostElement | null) Contains child element
toBeEmptyElement() No children or text content

Element State

Matcher Description
toHaveDisplayValue(value: string | RegExp, options?) TextInput display value
toHaveAccessibilityValue({ min?, max?, now?, text? }) Accessibility value (partial match)
toBeEnabled() / toBeDisabled() Disabled state via aria-disabled (checks ancestors)
toBeSelected() Selected state via aria-selected
toBeChecked() / toBePartiallyChecked() Checked state via aria-checked
toBeExpanded() / toBeCollapsed() Expanded state via aria-expanded
toBeBusy() Busy state via aria-busy

Element Style

Matcher Description
toBeVisible() Not hidden (display: none, opacity: 0, or hidden from a11y)
toHaveStyle(style: StyleProp<Style>) Specific style match

Other

Matcher Signature Description
toHaveAccessibleName() (name?: string | RegExp, options?: { exact?, normalizer? }) Accessible name from aria-label/aria-labelledby or text content
toHaveProp() (name: string, value?: unknown) Prop existence/value check (last resort)

Async Utilities

waitFor

function waitFor<T>(
  expectation: () => T,
  options?: { timeout?: number; interval?: number },
): Promise<T>;

Runs expectation every interval (default 50ms) until timeout (default 1000ms). Callback must throw on failure. Auto-detects and works with Jest fake timers.

Rules:

  • No side effects inside callback (no fireEvent/userEvent)
  • One assertion per waitFor
  • Never pass empty callback
  • Prefer findBy* over waitFor + getBy*

waitForElementToBeRemoved

function waitForElementToBeRemoved<T>(
  expectation: () => T,
  options?: { timeout?: number; interval?: number },
): Promise<T>;

Waits until the queried element is removed. Element must be initially present.


Other Helpers

within

function within(element: HostElement): Queries;

Scoped queries on a subtree. Useful for querying within a single FlatList item or a specific screen.

const item = within(screen.getByTestId('list-item-1'));
expect(item.getByText('Title')).toBeOnTheScreen();

cleanup

function cleanup(): void;

Unmounts rendered trees and clears screen. Automatic after each test (if test runner supports afterEach). Don't call manually unless using @testing-library/react-native/pure.


renderHook

Async in v14:

async function renderHook<Result, Props>(
  hookFn: (props?: Props) => Result,
  options?: { initialProps?: Props; wrapper?: React.ComponentType },
): Promise<{
  result: { current: Result };
  rerender: (props: Props) => Promise<void>;
  unmount: () => Promise<void>;
}>;

There is no renderHookAsync in v14. The standard renderHook is already async.

Renders a test component that calls the provided hook. Use act() when calling functions returned by the hook that trigger state updates.

const { result } = await renderHook(() => useCount());
expect(result.current.count).toBe(0);
await act(() => {
  result.current.increment();
});
expect(result.current.count).toBe(1);

// With wrapper
await renderHook(() => useHook(), {
  wrapper: ({ children }) => <Provider>{children}</Provider>,
});

act

In v14, act always returns a Promise<T> and must be awaited, even with sync callbacks:

async function act<T>(callback: () => T): Promise<T>;
await act(() => {
  result.current.increment();
});

Usually not needed — render, fireEvent, userEvent, and waitFor handle it internally.


Configuration

function configure(
  options: Partial<{
    asyncUtilTimeout: number; // default timeout for waitFor/findBy* (default: 1000ms)
    defaultIncludeHiddenElements: boolean; // default for includeHiddenElements option (default: false)
    defaultDebugOptions: Partial<DebugOptions>;
  }>,
): void;

function resetToDefaults(): void;

Note: concurrentRoot option is removed (always on). unstable_validateStringsRenderedWithinText is removed (always on).

Environment Variables

Variable Description
RNTL_SKIP_AUTO_CLEANUP=true Disable automatic cleanup after each test
RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true Disable auto-detection of fake timers

Accessibility

isHiddenFromAccessibility

function isHiddenFromAccessibility(element: HostElement | null): boolean;

Also available as isInaccessible() alias.

Element is hidden when it or any ancestor has:

  • display: none style
  • aria-hidden={true}
  • accessibilityElementsHidden={true} (iOS)
  • importantForAccessibility="no-hide-descendants" (Android)
  • Sibling with aria-modal={true} or accessibilityViewIsModal={true} (iOS)

Removed APIs

The following are removed in v14 and must not be used:

  • renderAsync — Use render (already async)
  • fireEventAsync — Use fireEvent (already async)
  • renderHookAsync — Use renderHook (already async)
  • rerenderAsync / unmountAsync — Use rerender / unmount (already async)
  • update() — Use rerender()
  • getQueriesForElement() — Use within()
  • UNSAFE_root — Use container or root
  • UNSAFE_getByType, UNSAFE_getAllByType, UNSAFE_queryByType, UNSAFE_queryAllByType — Removed
  • UNSAFE_getByProps, UNSAFE_getAllByProps, UNSAFE_queryByProps, UNSAFE_queryAllByProps — Removed
  • concurrentRoot option — Always on
  • unstable_validateStringsRenderedWithinText option — Always on

Migration Codemods

Use RNTL codemods to automate migration from v13:

  • rntl-v14-update-deps — Updates react-test-renderer to test-renderer in package.json
  • rntl-v14-async-functions — Adds await to render, fireEvent, rerender, unmount, renderHook, and act calls