Skip to content

docs: Jest matchers docs #1506

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

Merged
merged 18 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
218 changes: 218 additions & 0 deletions website/docs/JestMatchers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
---
id: jest-matchers
title: Jest Matchers
---

:::note
Built-in Jest matchers require RNTL v12.4.0 or later.
:::

This guide describes built-in Jest matchers, we recommend using these matchers as they provide more readable tests, better accessibility support and better developer experience.

If you are already using legacy Jest Native matchers we have a [migration guide](migration-jest-native) for moving to the built-in matchers.

import TOCInline from '@theme/TOCInline';

<TOCInline toc={toc} />

## Element Existence

### `toBeOnTheScreen()`

```ts
expect(element).toBeOnTheScreen()
```

This allows you to assert whether element is attached to the element tree or not. If you hold a reference to an element and it gets unmounted during the test it will no longer pass this assertion.

## Element Content

### `toHaveTextContent()`

```ts
expect(element).toHaveTextContent(t
text: string | RegExp,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
},
)
```

This allows you to assert whether the given element has a text content or not. It accepts either `string` or `RegExp` matchers, as well as [text match options](Queries.md#text-match-options) of `exact` and `normalizer`.

When `text` parameter is `undefined` it will only check for existence of text content, and when `text` is defined it will check if the actual text content matches passed value.

### `toContainElement()`

```ts
expect(container).toContainElement(
element: ReactTestInstance | null,
)
```

This allows you to assert whether the given container element does contain another host element.

### `toBeEmptyElement()`

```ts
expect(element).toBeEmptyElement()
```

This allows you to assert whether the given element does not have any host child elements nor text content.





## Element State

### `toHaveDisplayValue()`

```ts
expect(element).toHaveDisplayValue(
value: string | RegExp,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
},
)
```

This allows you to assert whether the given `TextInput` element has specified display value. It accepts either `string` or `RegExp` matchers, as well as [text match options](Queries.md#text-match-options) of `exact` and `normalizer`.

### `toHaveAccessibilityValue()`

```ts
expect(element).toHaveAccessibilityValue(
value: {
min?: number;
max?: number;
now?: number;
text?: string | RegExp;
},
)
```

This allows you to assert whether the given element has specified accessible value.

This matcher will assert accessibility value based on `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` and `accessibilityValue` props. Only defined value entires will be used in the assertion, the element might have additional accessibility value entries and still be matched.

When querying by `text` entry a string or `RegExp` might be used.


### `toBeEnabled()` / `toBeDisabled` {#tobeenabled}

```ts
expect(element).toBeEnabled()
expect(element).toBeDisabled()
```

These allows you to assert whether the given element is enabled or disabled from user's perspective. It relies on accessibility disabled state as set by `aria-disabled` or `accessibilityState.disabled` props. It will considers given element disabled when it or any of its ancestors is disabled.

:::note
This matchers are negation of each other, and both are probivided to avoid double negations in your assertions.
:::


### `toBeSelected()`

```ts
expect(element).toBeSelected()
```

This allows you to assert whether the given element is selected from user's perspective. It relies on accessibility selected state as set by `aria-selected` or `accessibilityState.selected` props.


### `toBeChecked()` / `toBePartiallyChecked()` {#tobechecked}

```ts
expect(element).toBeChecked()
expect(element).toBePartiallyChecked()
```

These allows you to assert whether the given element is checked or partially checked from user's perspective. It relies on accessibility checked state as set by `aria-checked` or `accessibilityState.checked` props.

:::note
* `toBeChecked()` matcher works only on elements with `checkbox` or `radio` role.
* `toBePartiallyChecked()` matchers works only on elements with `checkbox` role.
:::

### `toBeExpanded()` / `toBeCollapsed()` {#tobeexpanded}

```ts
expect(element).toBeExpanded()
expect(element).toBeCollapsed()
```

These allows you to assert whether the given element is expanded or collapsed from user's perspective. It relies on accessibility disabled state as set by `aria-expanded` or `accessibilityState.expanded` props.

:::note
This matchers are negation of each other for expandable elements (elements with explicit `aria-expanded` or `accessibilityState.expanded` props). However, both won't pass for non-expandable elements (ones without explicit `aria-expanded` or `accessibilityState.expanded` props).
:::


### `toBeBusy()`

```ts
expect(element).toBeBusy()
```

This allows you to assert whether the given element is busy from user's perspective. It relies on accessibility selected state as set by `aria-busy` or `accessibilityState.busy` props.

## Element Styles

### `toBeVisible()`

```ts
expect(element).toBeVisible()
```

This allows you to assert whether the given element is visible from user's perspective.

The element is considered invisibile when it or any of its ancestors has `display: none` or `opacity: 0` styles, as well as when it's hidden from accessbility.

### `toHaveStyle()`

```ts
expect(element).toHaveStyle(
style: StyleProp<Style>,
)
```

This allows you to assert whether the given element has given styles.

## Other

### `toHaveAccessibleName()`

```ts
expect(element).toHaveAccessibleName(
name?: string | RegExp,
options?: {
exact?: boolean;
normalizer?: (text: string) => string;
},
)
```

This allows you to assert whether the given element has specified accessible name. It accepts either `string` or `RegExp` matchers, as well as [text match options](Queries.md#text-match-options) of `exact` and `normalizer`.

Accessible name will be computed based on `aria-labelledby`, `accessibilityLabelledBy`, `aria-label`, `accessibilityLabel` props, in the absence of these props, element text content will be used.

When `name` parameter is `undefined` it will only check if element has any accessible name.

### `toHaveProp()`

```ts
expect(element).toHaveProp(
name: string,
value?: unknown,
)
```

This allows you to assert whether the given element has a given prop. When `value` parameter is `undefined` it will only check for existence of prop, and when `value` is defined it will check if the actual value matches passed value.

:::note
This matchers should be treated as escape hatch to be used when all other matchers are not suitable.
:::
79 changes: 79 additions & 0 deletions website/docs/MigrationJestMatchers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
id: migration-jest-native
title: Migration from Jest Native matchers
---

This guide describes steps necessary to migrate from [legacy Jest Native matchers v5](https://github.com/testing-library/jest-native) to [built-in Jest matchers](jest-matchers).

import TOCInline from '@theme/TOCInline';

<TOCInline toc={toc} />

## General notes

All of built-in Jest matchers provided by React Native Testing Library are supporting only host elements. This should not be an issue, as all RNTL v12 queries already return only host elements. When this guide states that given matcher should work the same it assumes behavior only only host elements. If you need to assert status of composite elements use Jest Native matchers in [legacy mode](#gradual-migration).

## Usage

You can use the built-in matchers by adding following line to your `jest-setup.ts` file:

```ts
import '@testing-library/react-native/extend-expect';
```

### Gradual migration

You can use the built-in matchers alongside with legacy Jest Native matchers by changing their import in your `jest-setup.ts` file:

```ts
// Replace this:
// import '@testing-library/jest-native/extend-expect';

// With this:
import '@testing-library/react-native/extend-expect';
import '@testing-library/jest-native/legacy-extend-expect';
```

In such case legacy matchers will be available using `legacy_` prefix, e.g.:

```ts
expect(element).legacy_toHaveAccessibilityState({ busy: true });
```

## Migration details

### Matchers not requiring changes

Following matchers should work the same:
* [`toBeEmptyElement()`](jest-matchers#tobeemptyelement)
* [`toBeEnabled()` / `toBeDisabled()`](jest-matchers#tobeenabled)
* [`toBeOnTheScreen()`](jest-matchers#tobeonthescreen)
* [`toBeVisible()`](jest-matchers#tobevisible)
* [`toContainElement()`](jest-matchers#tocontainelement)
* [`toHaveAccessibilityValue()`](jest-matchers#tohaveaccessibilityvalue)
* [`toHaveDisplayValue()`](jest-matchers#tohavedisplayvalue)
* [`toHaveProp()`](jest-matchers#tohaveprop)
* [`toHaveStyle()`](jest-matchers#tohavestyle)
* [`toHaveTextContent()`](jest-matchers#tohavetextcontent)

### Replaced matchers

`toHaveAccessibilityState()` matcher has been replaced by following matchers:
* enabled state: [`toBeEnabled()` / `toBeDisabled()`](jest-matchers#tobeenabled)
* checked state: [`toBeChecked()` / `toBePartiallyChecked()`](jest-matchers#tobechecked)
* selected state: [`toBeSelected()`](jest-matchers#tobeselected)
* expanded state: [`toBeExpanded()` / `toBeCollapsed()`](jest-matchers#tobeexpanded)
* busy state: [`toBeBusy()`](jest-matchers#tobebusy)

The new matchers support both `accessbililityState` and `aria-*` props.

### Added matchers

New [`toHaveAccessibleName()`](jest-matchers#tohaveaccessiblename) has been added.

### Noteworthy details

You should be aware of following changes:
* [`toBeEnabled()` / `toBeDisabled()`](jest-matchers#tobeenabled) matchers also check the disabled state for element ancestors and not only the element itself
* [`toBeChecked()`](jest-matchers#tobechecked) matcher supports only elements with `checkbox` or `radio` role
* [`toBePartiallyChecked()`](jest-matchers#tobechecked) matchers supports only elements with `checkbox` role
19 changes: 10 additions & 9 deletions website/docs/Queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ getByDisplayValue(
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand All @@ -208,7 +208,7 @@ getByTestId(
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand Down Expand Up @@ -236,7 +236,7 @@ getByLabelText(
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand Down Expand Up @@ -264,7 +264,7 @@ getByHintText(
exact?: boolean;
normalizer?: (text: string) => string;
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand Down Expand Up @@ -305,7 +305,7 @@ getByA11yState(
},
options?: {
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand Down Expand Up @@ -367,7 +367,7 @@ getByA11yValue(
},
options?: {
includeHiddenElements?: boolean;
}
},
): ReactTestInstance;
```

Expand Down Expand Up @@ -457,7 +457,8 @@ screen.getByText('Goodbye World');
screen.getByText(/hello world/);
```

### Precision
### Options {#text-match-options}
#### Precision

```typescript
type TextMatchOptions = {
Expand All @@ -475,7 +476,7 @@ Queries that take a `TextMatch` also accept an object as the second argument tha

`exact` option defaults to `true` but if you want to search for a text slice or make text matching case-insensitive you can override it. That being said we advise you to use regex in more complex scenarios.

### Normalization
#### Normalization

Before running any matching logic against text, it is automatically normalized. By default, normalization consists of trimming whitespace from the start and end of text, and collapsing multiple adjacent whitespace characters into a single space.

Expand All @@ -490,7 +491,7 @@ Specifying a value for `normalizer` replaces the built-in normalization, but you
- `trim`: Defaults to `true`. Trims leading and trailing whitespace.
- `collapseWhitespace`: Defaults to `true`. Collapses inner whitespace (newlines, tabs repeated spaces) into a single space.

#### Normalization Examples
##### Normalization Examples

To perform a match against text without trimming:

Expand Down