Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: improve api docs #1535

Merged
merged 2 commits into from
Mar 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 143 additions & 66 deletions website/docs/Queries.md
Original file line number Diff line number Diff line change
@@ -3,52 +3,135 @@ id: api-queries
title: Queries
---

import TOCInline from '@theme/TOCInline';
Queries are one of the main building blocks for the React Native Testing Library. They enable you to find relevant elements in the element tree, which represents the your application's user interface when running under tests.

<TOCInline toc={toc} />
## Accessing queries

## Query Variants
All queries described below are accessible in two main ways: through the `screen` object or by capturing the `render` function call result.

> `getBy*` queries are shown by default in the [query documentation](#queries)
> below.
### Using `screen` object

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

test('accessing queries using "screen" object', () => {
render(...);

screen.getByRole("button", { name: "Start" });
})
```

The modern and recommended way of accessing queries is to use the `screen` object exported by the `@testing-library/react-native` package. This object will contain methods of all available queries bound to the most recently rendered UI.

### Using `render` result

```ts
import { render } from '@testing-library/react-native';

test('accessing queries using "render" result', () => {
const { getByRole } = render(...);
getByRole("button", { name: "Start" });
})
```

The classic way is to capture query functions, as they are returned from the `render` function call. This provides access to the same functions as in the case of the `screen` object.

## Query parts

Each query is composed of two parts: variant and predicate, which are separated by the `by` word in the middle of the name.

Consider the following query:

```
getByRole()
```

For this query, `getBy*` is the query variant, and `*ByRole` is the predicate.

## Query variant

The query variants describe the expected number (and timing) of matching elements, so they differ in their return type.

| Variant | Assertion | Return type | Is Async? |
| ----------------------------------------- | ----------------------------- | ------------------------------------------ | --------- |
| [`getBy*`](api-queries#get-by) | Exactly one matching element | `ReactTestInstance` | No |
| [`getAllBy*`](api-queries#get-all-by) | At least one matching element | `Array<ReactTestInstance>` | No |
| [`queryBy*`](api-queries#query-by) | Zero or one matching element | <code>ReactTestInstance &#124; null</code> | No |
| [`queryAllBy*`](api-queries#query-all-by) | No assertion | `Array<ReactTestInstance>` | No |
| [`findBy*`](api-queries#find-by) | Exactly one matching element | `Promise<ReactTestInstance>` | Yes |
| [`findAllBy*`](api-queries#find-all-by) | At least one matching element | `Promise<Array<ReactTestInstance>>` | Yes |

Queries work as implicit assertions on the number of matching elements and will throw an error when the assertion fails.

### `getBy*` queries {#get-by}

`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`.
```ts
getByX(...): ReactTestInstance
```

`getBy*` queries return the single matching element 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*` queries {#get-all-by}

`getAllBy*` queries return an array of all matching nodes for a query, and throw an error if no elements match.
```ts
getAllByX(...): ReactTestInstance[]
```

`getAllBy*` queries return an array of all matching elements for a query and throw an error if no elements match.

### `queryBy*` queries {#query-by}

```ts
queryByX(...): ReactTestInstance | null
```

`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*` queries {#query-all-by}

`queryAllBy*` queries return an array of all matching nodes for a query, and return an empty array (`[]`) when no elements match.
```ts
queryAllByX(...): ReactTestInstance[]
```

`queryAllBy*` queries return an array of all matching nodes for a query and return an empty array (`[]`) when no elements match.

### `findBy*` queries {#find-by}

`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*`.
```ts
findByX(
...,
waitForOptions?: {
timeout?: number,
interval?: number,
},
): Promise<ReactTestInstance>
```

`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 use `findAllBy*` queries.

### `findAllBy*` queries {#find-all-by}

`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.
```ts
findAllByX(
...,
waitForOptions?: {
timeout?: number,
interval?: number,
},
): Promise<ReactTestInstance[]>
```

:::info
`findBy*` and `findAllBy*` queries accept optional `waitForOptions` object argument which can contain `timeout`, `interval` and `onTimeout` properies which have the same meaning as respective options for [`waitFor`](api#waitfor) function.
:::
`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.

:::info
In cases when your `findBy*` and `findAllBy*` queries throw when not able to find matching elements it is useful to pass `onTimeout: () => { screen.debug(); }` callback using `waitForOptions` parameter.
`findBy*` and `findAllBy*` queries accept optional `waitForOptions` object arguments, which can contain `timeout`, `interval` and `onTimeout` properties which have the same meaning as respective options for [`waitFor`](api#waitfor) function.
:::

:::info
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).
In cases when your `findBy*` and `findAllBy*` queries throw when unable to find matching elements, it is helpful to pass `onTimeout: () => { screen.debug(); }` callback using the `waitForOptions` parameter.
:::

## Query Predicates
## 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:_

@@ -61,7 +144,7 @@ type ReactTestInstance = {
};
```

### `ByRole` {#by-role}
### `*ByRole` {#by-role}

> getByRole, getAllByRole, queryByRole, queryAllByRole, findByRole, findAllByRole

@@ -144,33 +227,34 @@ const element3 = screen.getByRole('button', { name: 'Hello', disabled: true });
- See React Native [accessibilityValue](https://reactnative.dev/docs/accessibility#accessibilityvalue) docs to learn more about the accessibility value concept.
- This option can alternatively be expressed using the [`toHaveAccessibilityValue()`](jest-matchers#tohaveaccessibilityvalue) Jest matcher.

### `ByText` {#by-text}
### `*ByLabelText` {#by-label-text}

> getByText, getAllByText, queryByText, queryAllByText, findByText, findAllByText
> getByLabelText, getAllByLabelText, queryByLabelText, queryAllByLabelText, findByLabelText, findAllByLabelText

```ts
getByText(
getByLabelText(
text: TextMatch,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Returns a `ReactTestInstance` with matching text – may be a string or regular expression.
Returns a `ReactTestInstance` with matching label:

This method will join `<Text>` siblings to find matches, similarly to [how React Native handles these components](https://reactnative.dev/docs/text#containers). This will allow for querying for strings that will be visually rendered together, but may be semantically separate React components.
- either by matching [`aria-label`](https://reactnative.dev/docs/accessibility#aria-label)/[`accessibilityLabel`](https://reactnative.dev/docs/accessibility#accessibilitylabel) prop
- or by matching text content of view referenced by [`aria-labelledby`](https://reactnative.dev/docs/accessibility#aria-labelledby-android)/[`accessibilityLabelledBy`](https://reactnative.dev/docs/accessibility#accessibilitylabelledby-android) prop

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

render(<MyComponent />);
const element = screen.getByText('banana');
const element = screen.getByLabelText('my-label');
```

### `ByPlaceholderText` {#by-placeholder-text}
### `*ByPlaceholderText` {#by-placeholder-text}

> getByPlaceholderText, getAllByPlaceholderText, queryByPlaceholderText, queryAllByPlaceholderText, findByPlaceholderText, findAllByPlaceholderText

@@ -194,7 +278,7 @@ render(<MyComponent />);
const element = screen.getByPlaceholderText('username');
```

### `ByDisplayValue` {#by-display-value}
### `*ByDisplayValue` {#by-display-value}

> getByDisplayValue, getAllByDisplayValue, queryByDisplayValue, queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue

@@ -218,41 +302,41 @@ render(<MyComponent />);
const element = screen.getByDisplayValue('username');
```

### `ByTestId` {#by-test-id}
### `*ByText` {#by-text}

> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId, findByTestId, findAllByTestId
> getByText, getAllByText, queryByText, queryAllByText, findByText, findAllByText

```ts
getByTestId(
testId: TextMatch,
getByText(
text: TextMatch,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
},
}
): ReactTestInstance;
```

Returns a `ReactTestInstance` with matching `testID` prop. `testID` – may be a string or a regular expression.
Returns a `ReactTestInstance` with matching text – may be a string or regular expression.

This method will join `<Text>` siblings to find matches, similarly to [how React Native handles these components](https://reactnative.dev/docs/text#containers). This will allow for querying for strings that will be visually rendered together, but may be semantically separate React components.

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

render(<MyComponent />);
const element = screen.getByTestId('unique-id');
const element = screen.getByText('banana');
```

:::info
In the spirit of [the guiding principles](https://testing-library.com/docs/guiding-principles), it is recommended to use this only after the other queries don't work for your use case. Using `testID` attributes do not resemble how your software is used and should be avoided if possible. However, they are particularly useful for end-to-end testing on real devices, e.g. using Detox and it's an encouraged technique to use there. Learn more from the blog post ["Making your UI tests resilient to change"](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change).
:::
### `*ByHintText` {#by-hint-text}

### `ByLabelText` {#by-label-text}

> getByLabelText, getAllByLabelText, queryByLabelText, queryAllByLabelText, findByLabelText, findAllByLabelText
> getByA11yHint, getAllByA11yHint, queryByA11yHint, queryAllByA11yHint, findByA11yHint, findAllByA11yHint
> getByAccessibilityHint, getAllByAccessibilityHint, queryByAccessibilityHint, queryAllByAccessibilityHint, findByAccessibilityHint, findAllByAccessibilityHint
> getByHintText, getAllByHintText, queryByHintText, queryAllByHintText, findByHintText, findAllByHintText

```ts
getByLabelText(
text: TextMatch,
getByHintText(
hint: TextMatch,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
@@ -261,27 +345,26 @@ getByLabelText(
): ReactTestInstance;
```

Returns a `ReactTestInstance` with matching label:

- either by matching [`aria-label`](https://reactnative.dev/docs/accessibility#aria-label)/[`accessibilityLabel`](https://reactnative.dev/docs/accessibility#accessibilitylabel) prop
- or by matching text content of view referenced by [`aria-labelledby`](https://reactnative.dev/docs/accessibility#aria-labelledby-android)/[`accessibilityLabelledBy`](https://reactnative.dev/docs/accessibility#accessibilitylabelledby-android) prop
Returns a `ReactTestInstance` with matching `accessibilityHint` prop.

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

render(<MyComponent />);
const element = screen.getByLabelText('my-label');
const element = screen.getByHintText('Plays a song');
```

### `ByHintText`, `ByA11yHint`, `ByAccessibilityHint` {#by-hint-text}
:::info
Please consult [Apple guidelines on how `accessibilityHint` should be used](https://developer.apple.com/documentation/objectivec/nsobject/1615093-accessibilityhint).
:::

> getByA11yHint, getAllByA11yHint, queryByA11yHint, queryAllByA11yHint, findByA11yHint, findAllByA11yHint
> getByAccessibilityHint, getAllByAccessibilityHint, queryByAccessibilityHint, queryAllByAccessibilityHint, findByAccessibilityHint, findAllByAccessibilityHint
> getByHintText, getAllByHintText, queryByHintText, queryAllByHintText, findByHintText, findAllByHintText
### `*ByTestId` {#by-test-id}

> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId, findByTestId, findAllByTestId

```ts
getByHintText(
hint: TextMatch,
getByTestId(
testId: TextMatch,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
@@ -290,20 +373,20 @@ getByHintText(
): ReactTestInstance;
```

Returns a `ReactTestInstance` with matching `accessibilityHint` prop.
Returns a `ReactTestInstance` with matching `testID` prop. `testID` – may be a string or a regular expression.

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

render(<MyComponent />);
const element = screen.getByHintText('Plays a song');
const element = screen.getByTestId('unique-id');
```

:::info
Please consult [Apple guidelines on how `accessibilityHint` should be used](https://developer.apple.com/documentation/objectivec/nsobject/1615093-accessibilityhint).
In the spirit of [the guiding principles](https://testing-library.com/docs/guiding-principles), it is recommended to use this only after the other queries don't work for your use case. Using `testID` attributes do not resemble how your software is used and should be avoided if possible. However, they are particularly useful for end-to-end testing on real devices, e.g. using Detox and it's an encouraged technique to use there. Learn more from the blog post ["Making your UI tests resilient to change"](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change).
:::

### `ByA11yState`, `ByAccessibilityState` (deprecated) {#by-accessibility-state}
### `*ByA11yState`, `ByAccessibilityState` (deprecated) {#by-accessibility-state}

:::caution
This query has been marked deprecated, as is typically too general to give meaningful results. Therefore, it's better to use one of following options:
@@ -373,14 +456,15 @@ but will not match elements with following props:
The difference in handling default values is made to reflect observed accessibility behaviour on iOS and Android platforms.
:::

### `ByA11yValue`, `ByAccessibilityValue` (deprecated) {#by-accessibility-value}
### `*ByA11yValue`, `*ByAccessibilityValue` (deprecated) {#by-accessibility-value}

:::caution
This query has been marked deprecated, as is typically too general to give meaningful results. Therefore, it's better to use one of following options:

- [`toHaveAccessibilityValue()`](jest-matchers#tohaveaccessibilityvalue) Jest matcher to check the state of element found using some other query
- [`*ByRole`](#by-role) query with `value` option
:::

:::

> getByA11yValue, getAllByA11yValue, queryByA11yValue, queryAllByA11yValue, findByA11yValue, findAllByA11yValue
> getByAccessibilityValue, getAllByAccessibilityValue, queryByAccessibilityValue, queryAllByAccessibilityValue, findByAccessibilityValue, findAllByAccessibilityValue
@@ -543,12 +627,7 @@ screen.getByText(node, 'text', {

## Legacy unit testing helpers

> Use sparingly and responsibly, escape hatches here

`render` from `@testing-library/react-native` exposes additional queries that **should not be used in component integration testing**, but some users (like component library creators) interested in unit testing some components may find helpful.

<details>
<summary>Queries helpful in unit testing</summary>
`render` from `@testing-library/react-native` exposes additional queries that **should not be used in integration or component testing**, but some users (like component library creators) interested in unit testing some components may find helpful.

The interface is the same as for other queries, but we won't provide full names so that they're harder to find by search engines.

@@ -571,5 +650,3 @@ Returns a `ReactTestInstance` with matching props object.
:::caution
This query has been marked unsafe, since it requires knowledge about implementation details of the component. Use responsibly.
:::

</details>