@testing-library/react-native 14.0.0-rc.0 → 14.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -6
- package/{build → dist}/helpers/accessibility.js +24 -7
- package/dist/helpers/accessibility.js.map +1 -0
- package/docs/README.md +31 -0
- package/docs/api/accessibility.md +26 -0
- package/docs/api/async-utilities.md +137 -0
- package/docs/api/configuration.md +61 -0
- package/docs/api/fire-event.md +165 -0
- package/docs/api/jest-matchers.md +198 -0
- package/docs/api/other-helpers.md +94 -0
- package/docs/api/overview.md +18 -0
- package/docs/api/queries.md +500 -0
- package/docs/api/render-hook.md +176 -0
- package/docs/api/render.md +49 -0
- package/docs/api/screen.md +188 -0
- package/docs/api/user-event.md +295 -0
- package/docs/cookbook/async-events.md +147 -0
- package/docs/cookbook/custom-render.md +83 -0
- package/docs/cookbook/network-requests.md +375 -0
- package/docs/guides/common-mistakes.md +587 -0
- package/docs/guides/how-to-query.md +125 -0
- package/docs/guides/llm-guidelines.md +228 -0
- package/docs/guides/migration-v14.md +553 -0
- package/docs/guides/quick-start.md +77 -0
- package/docs/guides/testing-environment.md +123 -0
- package/docs/guides/troubleshooting.md +61 -0
- package/docs/guides/understanding-act.md +207 -0
- package/matchers.d.ts +1 -1
- package/matchers.js +2 -2
- package/package.json +14 -12
- package/pure.d.ts +1 -1
- package/pure.js +1 -1
- package/build/helpers/accessibility.js.map +0 -1
- /package/{build → dist}/act.d.ts +0 -0
- /package/{build → dist}/act.js +0 -0
- /package/{build → dist}/act.js.map +0 -0
- /package/{build → dist}/cleanup.d.ts +0 -0
- /package/{build → dist}/cleanup.js +0 -0
- /package/{build → dist}/cleanup.js.map +0 -0
- /package/{build → dist}/config.d.ts +0 -0
- /package/{build → dist}/config.js +0 -0
- /package/{build → dist}/config.js.map +0 -0
- /package/{build → dist}/event-builder/base.d.ts +0 -0
- /package/{build → dist}/event-builder/base.js +0 -0
- /package/{build → dist}/event-builder/base.js.map +0 -0
- /package/{build → dist}/event-builder/common.d.ts +0 -0
- /package/{build → dist}/event-builder/common.js +0 -0
- /package/{build → dist}/event-builder/common.js.map +0 -0
- /package/{build → dist}/event-builder/index.d.ts +0 -0
- /package/{build → dist}/event-builder/index.js +0 -0
- /package/{build → dist}/event-builder/index.js.map +0 -0
- /package/{build → dist}/event-builder/scroll.d.ts +0 -0
- /package/{build → dist}/event-builder/scroll.js +0 -0
- /package/{build → dist}/event-builder/scroll.js.map +0 -0
- /package/{build → dist}/event-builder/text.d.ts +0 -0
- /package/{build → dist}/event-builder/text.js +0 -0
- /package/{build → dist}/event-builder/text.js.map +0 -0
- /package/{build → dist}/event-handler.d.ts +0 -0
- /package/{build → dist}/event-handler.js +0 -0
- /package/{build → dist}/event-handler.js.map +0 -0
- /package/{build → dist}/fire-event.d.ts +0 -0
- /package/{build → dist}/fire-event.js +0 -0
- /package/{build → dist}/fire-event.js.map +0 -0
- /package/{build → dist}/flush-micro-tasks.d.ts +0 -0
- /package/{build → dist}/flush-micro-tasks.js +0 -0
- /package/{build → dist}/flush-micro-tasks.js.map +0 -0
- /package/{build → dist}/helpers/accessibility.d.ts +0 -0
- /package/{build → dist}/helpers/component-tree.d.ts +0 -0
- /package/{build → dist}/helpers/component-tree.js +0 -0
- /package/{build → dist}/helpers/component-tree.js.map +0 -0
- /package/{build → dist}/helpers/debug.d.ts +0 -0
- /package/{build → dist}/helpers/debug.js +0 -0
- /package/{build → dist}/helpers/debug.js.map +0 -0
- /package/{build → dist}/helpers/errors.d.ts +0 -0
- /package/{build → dist}/helpers/errors.js +0 -0
- /package/{build → dist}/helpers/errors.js.map +0 -0
- /package/{build → dist}/helpers/find-all.d.ts +0 -0
- /package/{build → dist}/helpers/find-all.js +0 -0
- /package/{build → dist}/helpers/find-all.js.map +0 -0
- /package/{build → dist}/helpers/format-element.d.ts +0 -0
- /package/{build → dist}/helpers/format-element.js +0 -0
- /package/{build → dist}/helpers/format-element.js.map +0 -0
- /package/{build → dist}/helpers/host-component-names.d.ts +0 -0
- /package/{build → dist}/helpers/host-component-names.js +0 -0
- /package/{build → dist}/helpers/host-component-names.js.map +0 -0
- /package/{build → dist}/helpers/logger.d.ts +0 -0
- /package/{build → dist}/helpers/logger.js +0 -0
- /package/{build → dist}/helpers/logger.js.map +0 -0
- /package/{build → dist}/helpers/map-props.d.ts +0 -0
- /package/{build → dist}/helpers/map-props.js +0 -0
- /package/{build → dist}/helpers/map-props.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-state.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-state.js +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-state.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-value.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-value.js +0 -0
- /package/{build → dist}/helpers/matchers/match-accessibility-value.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-array-prop.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-array-prop.js +0 -0
- /package/{build → dist}/helpers/matchers/match-array-prop.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-label-text.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-label-text.js +0 -0
- /package/{build → dist}/helpers/matchers/match-label-text.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-object-prop.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-object-prop.js +0 -0
- /package/{build → dist}/helpers/matchers/match-object-prop.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-string-prop.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-string-prop.js +0 -0
- /package/{build → dist}/helpers/matchers/match-string-prop.js.map +0 -0
- /package/{build → dist}/helpers/matchers/match-text-content.d.ts +0 -0
- /package/{build → dist}/helpers/matchers/match-text-content.js +0 -0
- /package/{build → dist}/helpers/matchers/match-text-content.js.map +0 -0
- /package/{build → dist}/helpers/object.d.ts +0 -0
- /package/{build → dist}/helpers/object.js +0 -0
- /package/{build → dist}/helpers/object.js.map +0 -0
- /package/{build → dist}/helpers/pointer-events.d.ts +0 -0
- /package/{build → dist}/helpers/pointer-events.js +0 -0
- /package/{build → dist}/helpers/pointer-events.js.map +0 -0
- /package/{build → dist}/helpers/text-content.d.ts +0 -0
- /package/{build → dist}/helpers/text-content.js +0 -0
- /package/{build → dist}/helpers/text-content.js.map +0 -0
- /package/{build → dist}/helpers/text-input.d.ts +0 -0
- /package/{build → dist}/helpers/text-input.js +0 -0
- /package/{build → dist}/helpers/text-input.js.map +0 -0
- /package/{build → dist}/helpers/timers.d.ts +0 -0
- /package/{build → dist}/helpers/timers.js +0 -0
- /package/{build → dist}/helpers/timers.js.map +0 -0
- /package/{build → dist}/helpers/validate-options.d.ts +0 -0
- /package/{build → dist}/helpers/validate-options.js +0 -0
- /package/{build → dist}/helpers/validate-options.js.map +0 -0
- /package/{build → dist}/helpers/wrap-async.d.ts +0 -0
- /package/{build → dist}/helpers/wrap-async.js +0 -0
- /package/{build → dist}/helpers/wrap-async.js.map +0 -0
- /package/{build → dist}/index.d.ts +0 -0
- /package/{build → dist}/index.js +0 -0
- /package/{build → dist}/index.js.map +0 -0
- /package/{build → dist}/matchers/extend-expect.d.ts +0 -0
- /package/{build → dist}/matchers/extend-expect.js +0 -0
- /package/{build → dist}/matchers/extend-expect.js.map +0 -0
- /package/{build → dist}/matchers/index.d.ts +0 -0
- /package/{build → dist}/matchers/index.js +0 -0
- /package/{build → dist}/matchers/index.js.map +0 -0
- /package/{build → dist}/matchers/to-be-busy.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-busy.js +0 -0
- /package/{build → dist}/matchers/to-be-busy.js.map +0 -0
- /package/{build → dist}/matchers/to-be-checked.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-checked.js +0 -0
- /package/{build → dist}/matchers/to-be-checked.js.map +0 -0
- /package/{build → dist}/matchers/to-be-disabled.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-disabled.js +0 -0
- /package/{build → dist}/matchers/to-be-disabled.js.map +0 -0
- /package/{build → dist}/matchers/to-be-empty-element.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-empty-element.js +0 -0
- /package/{build → dist}/matchers/to-be-empty-element.js.map +0 -0
- /package/{build → dist}/matchers/to-be-expanded.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-expanded.js +0 -0
- /package/{build → dist}/matchers/to-be-expanded.js.map +0 -0
- /package/{build → dist}/matchers/to-be-on-the-screen.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-on-the-screen.js +0 -0
- /package/{build → dist}/matchers/to-be-on-the-screen.js.map +0 -0
- /package/{build → dist}/matchers/to-be-partially-checked.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-partially-checked.js +0 -0
- /package/{build → dist}/matchers/to-be-partially-checked.js.map +0 -0
- /package/{build → dist}/matchers/to-be-selected.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-selected.js +0 -0
- /package/{build → dist}/matchers/to-be-selected.js.map +0 -0
- /package/{build → dist}/matchers/to-be-visible.d.ts +0 -0
- /package/{build → dist}/matchers/to-be-visible.js +0 -0
- /package/{build → dist}/matchers/to-be-visible.js.map +0 -0
- /package/{build → dist}/matchers/to-contain-element.d.ts +0 -0
- /package/{build → dist}/matchers/to-contain-element.js +0 -0
- /package/{build → dist}/matchers/to-contain-element.js.map +0 -0
- /package/{build → dist}/matchers/to-have-accessibility-value.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-accessibility-value.js +0 -0
- /package/{build → dist}/matchers/to-have-accessibility-value.js.map +0 -0
- /package/{build → dist}/matchers/to-have-accessible-name.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-accessible-name.js +0 -0
- /package/{build → dist}/matchers/to-have-accessible-name.js.map +0 -0
- /package/{build → dist}/matchers/to-have-display-value.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-display-value.js +0 -0
- /package/{build → dist}/matchers/to-have-display-value.js.map +0 -0
- /package/{build → dist}/matchers/to-have-prop.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-prop.js +0 -0
- /package/{build → dist}/matchers/to-have-prop.js.map +0 -0
- /package/{build → dist}/matchers/to-have-style.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-style.js +0 -0
- /package/{build → dist}/matchers/to-have-style.js.map +0 -0
- /package/{build → dist}/matchers/to-have-text-content.d.ts +0 -0
- /package/{build → dist}/matchers/to-have-text-content.js +0 -0
- /package/{build → dist}/matchers/to-have-text-content.js.map +0 -0
- /package/{build → dist}/matchers/types.d.ts +0 -0
- /package/{build → dist}/matchers/types.js +0 -0
- /package/{build → dist}/matchers/types.js.map +0 -0
- /package/{build → dist}/matchers/utils.d.ts +0 -0
- /package/{build → dist}/matchers/utils.js +0 -0
- /package/{build → dist}/matchers/utils.js.map +0 -0
- /package/{build → dist}/matches.d.ts +0 -0
- /package/{build → dist}/matches.js +0 -0
- /package/{build → dist}/matches.js.map +0 -0
- /package/{build → dist}/native-state.d.ts +0 -0
- /package/{build → dist}/native-state.js +0 -0
- /package/{build → dist}/native-state.js.map +0 -0
- /package/{build → dist}/pure.d.ts +0 -0
- /package/{build → dist}/pure.js +0 -0
- /package/{build → dist}/pure.js.map +0 -0
- /package/{build → dist}/queries/display-value.d.ts +0 -0
- /package/{build → dist}/queries/display-value.js +0 -0
- /package/{build → dist}/queries/display-value.js.map +0 -0
- /package/{build → dist}/queries/hint-text.d.ts +0 -0
- /package/{build → dist}/queries/hint-text.js +0 -0
- /package/{build → dist}/queries/hint-text.js.map +0 -0
- /package/{build → dist}/queries/label-text.d.ts +0 -0
- /package/{build → dist}/queries/label-text.js +0 -0
- /package/{build → dist}/queries/label-text.js.map +0 -0
- /package/{build → dist}/queries/make-queries.d.ts +0 -0
- /package/{build → dist}/queries/make-queries.js +0 -0
- /package/{build → dist}/queries/make-queries.js.map +0 -0
- /package/{build → dist}/queries/options.d.ts +0 -0
- /package/{build → dist}/queries/options.js +0 -0
- /package/{build → dist}/queries/options.js.map +0 -0
- /package/{build → dist}/queries/placeholder-text.d.ts +0 -0
- /package/{build → dist}/queries/placeholder-text.js +0 -0
- /package/{build → dist}/queries/placeholder-text.js.map +0 -0
- /package/{build → dist}/queries/role.d.ts +0 -0
- /package/{build → dist}/queries/role.js +0 -0
- /package/{build → dist}/queries/role.js.map +0 -0
- /package/{build → dist}/queries/test-id.d.ts +0 -0
- /package/{build → dist}/queries/test-id.js +0 -0
- /package/{build → dist}/queries/test-id.js.map +0 -0
- /package/{build → dist}/queries/text.d.ts +0 -0
- /package/{build → dist}/queries/text.js +0 -0
- /package/{build → dist}/queries/text.js.map +0 -0
- /package/{build → dist}/render-hook.d.ts +0 -0
- /package/{build → dist}/render-hook.js +0 -0
- /package/{build → dist}/render-hook.js.map +0 -0
- /package/{build → dist}/render.d.ts +0 -0
- /package/{build → dist}/render.js +0 -0
- /package/{build → dist}/render.js.map +0 -0
- /package/{build → dist}/screen.d.ts +0 -0
- /package/{build → dist}/screen.js +0 -0
- /package/{build → dist}/screen.js.map +0 -0
- /package/{build → dist}/test-utils/console.d.ts +0 -0
- /package/{build → dist}/test-utils/console.js +0 -0
- /package/{build → dist}/test-utils/console.js.map +0 -0
- /package/{build → dist}/test-utils/events.d.ts +0 -0
- /package/{build → dist}/test-utils/events.js +0 -0
- /package/{build → dist}/test-utils/events.js.map +0 -0
- /package/{build → dist}/test-utils/json.d.ts +0 -0
- /package/{build → dist}/test-utils/json.js +0 -0
- /package/{build → dist}/test-utils/json.js.map +0 -0
- /package/{build → dist}/test-utils/react-version-gates.d.ts +0 -0
- /package/{build → dist}/test-utils/react-version-gates.js +0 -0
- /package/{build → dist}/test-utils/react-version-gates.js.map +0 -0
- /package/{build → dist}/test-utils/timers.d.ts +0 -0
- /package/{build → dist}/test-utils/timers.js +0 -0
- /package/{build → dist}/test-utils/timers.js.map +0 -0
- /package/{build → dist}/test-utils/version.d.ts +0 -0
- /package/{build → dist}/test-utils/version.js +0 -0
- /package/{build → dist}/test-utils/version.js.map +0 -0
- /package/{build → dist}/types.d.ts +0 -0
- /package/{build → dist}/types.js +0 -0
- /package/{build → dist}/types.js.map +0 -0
- /package/{build → dist}/user-event/clear.d.ts +0 -0
- /package/{build → dist}/user-event/clear.js +0 -0
- /package/{build → dist}/user-event/clear.js.map +0 -0
- /package/{build → dist}/user-event/index.d.ts +0 -0
- /package/{build → dist}/user-event/index.js +0 -0
- /package/{build → dist}/user-event/index.js.map +0 -0
- /package/{build → dist}/user-event/paste.d.ts +0 -0
- /package/{build → dist}/user-event/paste.js +0 -0
- /package/{build → dist}/user-event/paste.js.map +0 -0
- /package/{build → dist}/user-event/press/index.d.ts +0 -0
- /package/{build → dist}/user-event/press/index.js +0 -0
- /package/{build → dist}/user-event/press/index.js.map +0 -0
- /package/{build → dist}/user-event/press/press.d.ts +0 -0
- /package/{build → dist}/user-event/press/press.js +0 -0
- /package/{build → dist}/user-event/press/press.js.map +0 -0
- /package/{build → dist}/user-event/scroll/index.d.ts +0 -0
- /package/{build → dist}/user-event/scroll/index.js +0 -0
- /package/{build → dist}/user-event/scroll/index.js.map +0 -0
- /package/{build → dist}/user-event/scroll/scroll-to.d.ts +0 -0
- /package/{build → dist}/user-event/scroll/scroll-to.js +0 -0
- /package/{build → dist}/user-event/scroll/scroll-to.js.map +0 -0
- /package/{build → dist}/user-event/scroll/utils.d.ts +0 -0
- /package/{build → dist}/user-event/scroll/utils.js +0 -0
- /package/{build → dist}/user-event/scroll/utils.js.map +0 -0
- /package/{build → dist}/user-event/setup/index.d.ts +0 -0
- /package/{build → dist}/user-event/setup/index.js +0 -0
- /package/{build → dist}/user-event/setup/index.js.map +0 -0
- /package/{build → dist}/user-event/setup/setup.d.ts +0 -0
- /package/{build → dist}/user-event/setup/setup.js +0 -0
- /package/{build → dist}/user-event/setup/setup.js.map +0 -0
- /package/{build → dist}/user-event/type/index.d.ts +0 -0
- /package/{build → dist}/user-event/type/index.js +0 -0
- /package/{build → dist}/user-event/type/index.js.map +0 -0
- /package/{build → dist}/user-event/type/parse-keys.d.ts +0 -0
- /package/{build → dist}/user-event/type/parse-keys.js +0 -0
- /package/{build → dist}/user-event/type/parse-keys.js.map +0 -0
- /package/{build → dist}/user-event/type/type.d.ts +0 -0
- /package/{build → dist}/user-event/type/type.js +0 -0
- /package/{build → dist}/user-event/type/type.js.map +0 -0
- /package/{build → dist}/user-event/utils/content-size.d.ts +0 -0
- /package/{build → dist}/user-event/utils/content-size.js +0 -0
- /package/{build → dist}/user-event/utils/content-size.js.map +0 -0
- /package/{build → dist}/user-event/utils/dispatch-event.d.ts +0 -0
- /package/{build → dist}/user-event/utils/dispatch-event.js +0 -0
- /package/{build → dist}/user-event/utils/dispatch-event.js.map +0 -0
- /package/{build → dist}/user-event/utils/index.d.ts +0 -0
- /package/{build → dist}/user-event/utils/index.js +0 -0
- /package/{build → dist}/user-event/utils/index.js.map +0 -0
- /package/{build → dist}/user-event/utils/wait.d.ts +0 -0
- /package/{build → dist}/user-event/utils/wait.js +0 -0
- /package/{build → dist}/user-event/utils/wait.js.map +0 -0
- /package/{build → dist}/wait-for-element-to-be-removed.d.ts +0 -0
- /package/{build → dist}/wait-for-element-to-be-removed.js +0 -0
- /package/{build → dist}/wait-for-element-to-be-removed.js.map +0 -0
- /package/{build → dist}/wait-for.d.ts +0 -0
- /package/{build → dist}/wait-for.js +0 -0
- /package/{build → dist}/wait-for.js.map +0 -0
- /package/{build → dist}/within.d.ts +0 -0
- /package/{build → dist}/within.js +0 -0
- /package/{build → dist}/within.js.map +0 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
# Common Mistakes with React Native Testing Library
|
|
2
|
+
|
|
3
|
+
> **Note:** This guide is adapted from Kent C. Dodds' article ["Common mistakes with React Testing Library"](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library) for React Native Testing Library v14. The original article focuses on web React, but the principles apply to React Native as well. This adaptation includes React Native-specific examples, async API usage (v14), and ARIA-compatible accessibility attributes.
|
|
4
|
+
|
|
5
|
+
React Native Testing Library guiding principle is:
|
|
6
|
+
|
|
7
|
+
> "The more your tests resemble the way your software is used, the more confidence they can give you."
|
|
8
|
+
|
|
9
|
+
This guide outlines some common mistakes people make when using React Native Testing Library and how to avoid them.
|
|
10
|
+
|
|
11
|
+
## Using the wrong query
|
|
12
|
+
|
|
13
|
+
**Importance: high**
|
|
14
|
+
|
|
15
|
+
React Native Testing Library provides several query types. Here's the priority order:
|
|
16
|
+
|
|
17
|
+
1. **Queries that reflect user experience:**
|
|
18
|
+
- `getByRole` - most accessible
|
|
19
|
+
- `getByLabelText` - accessible label
|
|
20
|
+
- `getByPlaceholderText` - `TextInput` placeholder text
|
|
21
|
+
- `getByText` - text content
|
|
22
|
+
- `getByDisplayValue` - `TextInput` input value
|
|
23
|
+
|
|
24
|
+
2. **Semantic queries:**
|
|
25
|
+
- `getByTestId` - only if nothing else works
|
|
26
|
+
|
|
27
|
+
Here's an example of using the right query:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { TextInput, View } from 'react-native';
|
|
31
|
+
import { render, screen } from '@testing-library/react-native';
|
|
32
|
+
|
|
33
|
+
test('finds input by label', async () => {
|
|
34
|
+
await render(
|
|
35
|
+
<View>
|
|
36
|
+
<TextInput aria-label="Username" placeholder="Enter username" value="" />
|
|
37
|
+
</View>,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// ✅ Good - uses accessible label
|
|
41
|
+
const input = screen.getByLabelText('Username');
|
|
42
|
+
|
|
43
|
+
// ✅ Also good - uses placeholder
|
|
44
|
+
const inputByPlaceholder = screen.getByPlaceholderText('Enter username');
|
|
45
|
+
|
|
46
|
+
// ❌ Bad - uses testID when accessible queries work
|
|
47
|
+
// const input = screen.getByTestId('username-input');
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Not using `*ByRole` query most of the time
|
|
52
|
+
|
|
53
|
+
**Importance: high**
|
|
54
|
+
|
|
55
|
+
`getByRole` is the most accessible query and should be your first choice. It queries elements by their semantic role:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { Pressable, Text, TextInput, View } from 'react-native';
|
|
59
|
+
import { render, screen } from '@testing-library/react-native';
|
|
60
|
+
|
|
61
|
+
test('uses role queries', async () => {
|
|
62
|
+
await render(
|
|
63
|
+
<View>
|
|
64
|
+
<Pressable role="button">
|
|
65
|
+
<Text>Submit</Text>
|
|
66
|
+
</Pressable>
|
|
67
|
+
<TextInput role="searchbox" aria-label="Search" placeholder="Search..." />
|
|
68
|
+
</View>,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// ✅ Good - uses role query
|
|
72
|
+
const button = screen.getByRole('button', { name: 'Submit' });
|
|
73
|
+
const searchbox = screen.getByRole('searchbox', { name: 'Search' });
|
|
74
|
+
|
|
75
|
+
expect(button).toBeOnTheScreen();
|
|
76
|
+
expect(searchbox).toBeOnTheScreen();
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Common roles in React Native include:
|
|
81
|
+
|
|
82
|
+
- `button` - pressable elements
|
|
83
|
+
- `text` - static text
|
|
84
|
+
- `header` / `heading` - headers
|
|
85
|
+
- `searchbox` - search inputs
|
|
86
|
+
- `switch` - toggle switches
|
|
87
|
+
- `checkbox` - checkboxes
|
|
88
|
+
- `radio` - radio buttons
|
|
89
|
+
- And more...
|
|
90
|
+
|
|
91
|
+
Note: React Native supports both ARIA-compatible (`role`) and legacy (`accessibilityRole`) props. Prefer `role` for consistency with web standards.
|
|
92
|
+
|
|
93
|
+
## Using the wrong assertion
|
|
94
|
+
|
|
95
|
+
**Importance: high**
|
|
96
|
+
|
|
97
|
+
React Native Testing Library provides built-in Jest matchers. Make sure you're using the right ones:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { Pressable, Text } from 'react-native';
|
|
101
|
+
import { render, screen } from '@testing-library/react-native';
|
|
102
|
+
|
|
103
|
+
test('button is disabled', async () => {
|
|
104
|
+
await render(
|
|
105
|
+
<Pressable role="button" aria-disabled>
|
|
106
|
+
<Text>Submit</Text>
|
|
107
|
+
</Pressable>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const button = screen.getByRole('button', { name: 'Submit' });
|
|
111
|
+
|
|
112
|
+
// ✅ Good - uses RNTL matcher
|
|
113
|
+
expect(button).toBeDisabled();
|
|
114
|
+
|
|
115
|
+
// ❌ Bad - doesn't use RNTL matcher
|
|
116
|
+
expect(button.props['aria-disabled']).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Common matchers include:
|
|
121
|
+
|
|
122
|
+
- `toBeOnTheScreen()` - checks if element is rendered (replaces `toBeInTheDocument()`)
|
|
123
|
+
- `toBeDisabled()` - checks if element is disabled
|
|
124
|
+
- `toHaveTextContent()` - checks text content
|
|
125
|
+
- `toHaveAccessibleName()` - checks accessible name
|
|
126
|
+
- And more...
|
|
127
|
+
|
|
128
|
+
## Using `query*` variants for anything except checking for non-existence
|
|
129
|
+
|
|
130
|
+
**Importance: high**
|
|
131
|
+
|
|
132
|
+
Use `queryBy*` only when checking that an element doesn't exist:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { View, Text } from 'react-native';
|
|
136
|
+
import { render, screen } from '@testing-library/react-native';
|
|
137
|
+
|
|
138
|
+
test('checks non-existence', async () => {
|
|
139
|
+
await render(
|
|
140
|
+
<View>
|
|
141
|
+
<Text>Hello</Text>
|
|
142
|
+
</View>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// ✅ Good - uses queryBy for non-existence check
|
|
146
|
+
expect(screen.queryByText('Goodbye')).not.toBeOnTheScreen();
|
|
147
|
+
|
|
148
|
+
// ❌ Bad - uses queryBy when element should exist
|
|
149
|
+
// const element = screen.queryByText('Hello');
|
|
150
|
+
// expect(element).toBeOnTheScreen();
|
|
151
|
+
|
|
152
|
+
// ✅ Good - uses getBy when element should exist
|
|
153
|
+
expect(screen.getByText('Hello')).toBeOnTheScreen();
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Using `waitFor` to wait for elements that can be queried with `find*`
|
|
158
|
+
|
|
159
|
+
**Importance: high**
|
|
160
|
+
|
|
161
|
+
Use `findBy*` queries instead of `waitFor` + `getBy*`:
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
import { View, Text } from 'react-native';
|
|
165
|
+
import { render, screen, waitFor } from '@testing-library/react-native';
|
|
166
|
+
|
|
167
|
+
test('waits for element', async () => {
|
|
168
|
+
const Component = () => {
|
|
169
|
+
const [show, setShow] = React.useState(false);
|
|
170
|
+
|
|
171
|
+
React.useEffect(() => {
|
|
172
|
+
setTimeout(() => setShow(true), 100);
|
|
173
|
+
}, []);
|
|
174
|
+
|
|
175
|
+
return <View>{show && <Text>Loaded</Text>}</View>;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
await render(<Component />);
|
|
179
|
+
|
|
180
|
+
// ✅ Good - uses findBy query
|
|
181
|
+
const element = await screen.findByText('Loaded');
|
|
182
|
+
expect(element).toBeOnTheScreen();
|
|
183
|
+
|
|
184
|
+
// ❌ Bad - uses waitFor + getBy
|
|
185
|
+
// await waitFor(() => {
|
|
186
|
+
// expect(screen.getByText('Loaded')).toBeOnTheScreen();
|
|
187
|
+
// });
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Performing side-effects in `waitFor`
|
|
192
|
+
|
|
193
|
+
**Importance: high**
|
|
194
|
+
|
|
195
|
+
Don't perform side-effects in `waitFor` callbacks:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import { Pressable, Text, View } from 'react-native';
|
|
199
|
+
import { render, screen, waitFor, fireEvent } from '@testing-library/react-native';
|
|
200
|
+
|
|
201
|
+
test('avoids side effects in waitFor', async () => {
|
|
202
|
+
const Component = () => {
|
|
203
|
+
const [count, setCount] = React.useState(0);
|
|
204
|
+
return (
|
|
205
|
+
<View>
|
|
206
|
+
<Pressable role="button" onPress={() => setCount(count + 1)}>
|
|
207
|
+
<Text>Increment</Text>
|
|
208
|
+
</Pressable>
|
|
209
|
+
<Text>Count: {count}</Text>
|
|
210
|
+
</View>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
await render(<Component />);
|
|
215
|
+
|
|
216
|
+
const button = screen.getByRole('button');
|
|
217
|
+
|
|
218
|
+
// ❌ Bad - side effect in waitFor
|
|
219
|
+
// await waitFor(async () => {
|
|
220
|
+
// await fireEvent.press(button);
|
|
221
|
+
// expect(screen.getByText('Count: 1')).toBeOnTheScreen();
|
|
222
|
+
// });
|
|
223
|
+
|
|
224
|
+
// ✅ Good - side effect outside waitFor
|
|
225
|
+
await fireEvent.press(button);
|
|
226
|
+
await waitFor(() => {
|
|
227
|
+
expect(screen.getByText('Count: 1')).toBeOnTheScreen();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Using `container` to query for elements
|
|
233
|
+
|
|
234
|
+
**Importance: high**
|
|
235
|
+
|
|
236
|
+
React Native Testing Library provides a `container` object that has a `queryAll` method, but you should avoid using it directly:
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { View, Text } from 'react-native';
|
|
240
|
+
import { render } from '@testing-library/react-native';
|
|
241
|
+
|
|
242
|
+
test('finds element incorrectly', async () => {
|
|
243
|
+
const { container } = await render(
|
|
244
|
+
<View>
|
|
245
|
+
<Text testID="message">Hello</Text>
|
|
246
|
+
</View>,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// ❌ Bad - using container.queryAll directly
|
|
250
|
+
const element = container.queryAll((node) => node.props.testID === 'message')[0];
|
|
251
|
+
|
|
252
|
+
// ✅ Good - use proper queries
|
|
253
|
+
// const element = screen.getByTestId('message');
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Instead, use the proper query methods from `screen` or the `render` result. The `container` is a low-level API that you rarely need.
|
|
258
|
+
|
|
259
|
+
## Passing an empty callback to `waitFor`
|
|
260
|
+
|
|
261
|
+
**Importance: high**
|
|
262
|
+
|
|
263
|
+
Don't pass an empty callback to `waitFor`:
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { View } from 'react-native';
|
|
267
|
+
import { render, waitFor } from '@testing-library/react-native';
|
|
268
|
+
|
|
269
|
+
test('waits correctly', async () => {
|
|
270
|
+
await render(<View testID="test" />);
|
|
271
|
+
|
|
272
|
+
// ❌ Bad - empty callback
|
|
273
|
+
// await waitFor(() => {});
|
|
274
|
+
|
|
275
|
+
// ✅ Good - meaningful assertion
|
|
276
|
+
await waitFor(() => {
|
|
277
|
+
expect(screen.getByTestId('test')).toBeOnTheScreen();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Not using `screen`
|
|
283
|
+
|
|
284
|
+
**Importance: medium**
|
|
285
|
+
|
|
286
|
+
You can get all the queries from the `render` result:
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import { View, Text } from 'react-native';
|
|
290
|
+
import { render } from '@testing-library/react-native';
|
|
291
|
+
|
|
292
|
+
test('renders component', async () => {
|
|
293
|
+
const { getByText } = await render(
|
|
294
|
+
<View>
|
|
295
|
+
<Text>Hello</Text>
|
|
296
|
+
</View>,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
expect(getByText('Hello')).toBeOnTheScreen();
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
But you can also get them from the `screen` object:
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
import { View, Text } from 'react-native';
|
|
307
|
+
import { render, screen } from '@testing-library/react-native';
|
|
308
|
+
|
|
309
|
+
test('renders component', async () => {
|
|
310
|
+
await render(
|
|
311
|
+
<View>
|
|
312
|
+
<Text>Hello</Text>
|
|
313
|
+
</View>,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(screen.getByText('Hello')).toBeOnTheScreen();
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Using `screen` has several benefits:
|
|
321
|
+
|
|
322
|
+
1. You don't need to destructure `getByText` from `render`
|
|
323
|
+
2. It's more consistent with the Testing Library ecosystem
|
|
324
|
+
|
|
325
|
+
## Wrapping things in `act` unnecessarily
|
|
326
|
+
|
|
327
|
+
**Importance: medium**
|
|
328
|
+
|
|
329
|
+
React Native Testing Library's `render`, `renderHook`, `userEvent`, and `fireEvent` are already wrapped in `act`, so you don't need to wrap them yourself:
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
import { Pressable, Text, View } from 'react-native';
|
|
333
|
+
import { render, fireEvent, screen } from '@testing-library/react-native';
|
|
334
|
+
|
|
335
|
+
test('updates on press', async () => {
|
|
336
|
+
const Component = () => {
|
|
337
|
+
const [count, setCount] = React.useState(0);
|
|
338
|
+
return (
|
|
339
|
+
<View>
|
|
340
|
+
<Pressable role="button" onPress={() => setCount(count + 1)}>
|
|
341
|
+
<Text>Count: {count}</Text>
|
|
342
|
+
</Pressable>
|
|
343
|
+
</View>
|
|
344
|
+
);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
await render(<Component />);
|
|
348
|
+
|
|
349
|
+
const button = screen.getByRole('button');
|
|
350
|
+
|
|
351
|
+
// ✅ Good - fireEvent is already wrapped in act
|
|
352
|
+
await fireEvent.press(button);
|
|
353
|
+
|
|
354
|
+
expect(screen.getByText('Count: 1')).toBeOnTheScreen();
|
|
355
|
+
|
|
356
|
+
// ❌ Bad - unnecessary act wrapper
|
|
357
|
+
// await act(async () => {
|
|
358
|
+
// await fireEvent.press(button);
|
|
359
|
+
// });
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Not using User Event API
|
|
364
|
+
|
|
365
|
+
**Importance: medium**
|
|
366
|
+
|
|
367
|
+
`userEvent` provides a more realistic way to simulate user interactions:
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
import { Pressable, Text, TextInput, View } from 'react-native';
|
|
371
|
+
import { render, screen, userEvent } from '@testing-library/react-native';
|
|
372
|
+
|
|
373
|
+
test('uses userEvent', async () => {
|
|
374
|
+
const user = userEvent.setup();
|
|
375
|
+
|
|
376
|
+
const Component = () => {
|
|
377
|
+
const [value, setValue] = React.useState('');
|
|
378
|
+
return (
|
|
379
|
+
<View>
|
|
380
|
+
<TextInput aria-label="Name" value={value} onChangeText={setValue} />
|
|
381
|
+
<Pressable role="button" onPress={() => setValue('')}>
|
|
382
|
+
<Text>Clear</Text>
|
|
383
|
+
</Pressable>
|
|
384
|
+
</View>
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
await render(<Component />);
|
|
389
|
+
|
|
390
|
+
const input = screen.getByLabelText('Name');
|
|
391
|
+
const button = screen.getByRole('button', { name: 'Clear' });
|
|
392
|
+
|
|
393
|
+
// ✅ Good - uses userEvent for realistic interactions
|
|
394
|
+
await user.type(input, 'John');
|
|
395
|
+
expect(input).toHaveValue('John');
|
|
396
|
+
|
|
397
|
+
await user.press(button);
|
|
398
|
+
expect(input).toHaveValue('');
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
`userEvent` methods are async and must be awaited. Available methods include:
|
|
403
|
+
|
|
404
|
+
- `press()` - simulates a press
|
|
405
|
+
- `longPress()` - simulates long press
|
|
406
|
+
- `type()` - simulates typing
|
|
407
|
+
- `clear()` - clears text input
|
|
408
|
+
- `paste()` - simulates pasting
|
|
409
|
+
- `scrollTo()` - simulates scrolling
|
|
410
|
+
|
|
411
|
+
## Not querying by text
|
|
412
|
+
|
|
413
|
+
**Importance: medium**
|
|
414
|
+
|
|
415
|
+
In React Native, text is rendered in `<Text>` components. You should query by the text content that users see:
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
import { Text, View } from 'react-native';
|
|
419
|
+
import { render, screen } from '@testing-library/react-native';
|
|
420
|
+
|
|
421
|
+
test('finds text correctly', async () => {
|
|
422
|
+
await render(
|
|
423
|
+
<View>
|
|
424
|
+
<Text>Hello World</Text>
|
|
425
|
+
</View>,
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// ✅ Good - queries by visible text
|
|
429
|
+
expect(screen.getByText('Hello World')).toBeOnTheScreen();
|
|
430
|
+
|
|
431
|
+
// ❌ Bad - queries by testID when text is available
|
|
432
|
+
// expect(screen.getByTestId('greeting')).toBeOnTheScreen();
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Not using Testing Library ESLint plugins
|
|
437
|
+
|
|
438
|
+
**Importance: medium**
|
|
439
|
+
|
|
440
|
+
There's an ESLint plugin for Testing Library: [`eslint-plugin-testing-library`](https://github.com/testing-library/eslint-plugin-testing-library). This plugin can help you avoid common mistakes and will automatically fix your code in many cases.
|
|
441
|
+
|
|
442
|
+
You can install it with:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
yarn add --dev eslint-plugin-testing-library
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
And configure it in your `eslint.config.js` (flat config):
|
|
449
|
+
|
|
450
|
+
```js
|
|
451
|
+
import testingLibrary from 'eslint-plugin-testing-library';
|
|
452
|
+
|
|
453
|
+
export default [testingLibrary.configs['flat/react']];
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Note: Unlike React Testing Library, React Native Testing Library has built-in Jest matchers, so you don't need `eslint-plugin-jest-dom`.
|
|
457
|
+
|
|
458
|
+
## Using `cleanup`
|
|
459
|
+
|
|
460
|
+
**Importance: medium**
|
|
461
|
+
|
|
462
|
+
React Native Testing Library automatically cleans up after each test. You don't need to call `cleanup()` manually unless you're using the `pure` export (which doesn't include automatic cleanup).
|
|
463
|
+
|
|
464
|
+
If you want to disable automatic cleanup for a specific test, you can use:
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
import { render } from '@testing-library/react-native/pure';
|
|
468
|
+
|
|
469
|
+
test('does not cleanup', async () => {
|
|
470
|
+
// This test won't cleanup automatically
|
|
471
|
+
await render(<MyComponent />);
|
|
472
|
+
// ... your test
|
|
473
|
+
});
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
But in most cases, you don't need to worry about cleanup at all - it's handled automatically.
|
|
477
|
+
|
|
478
|
+
## Using `get*` variants as assertions
|
|
479
|
+
|
|
480
|
+
**Importance: low**
|
|
481
|
+
|
|
482
|
+
`getBy*` queries throw errors when elements aren't found, so they work as assertions. However, for better error messages, you might want to combine them with explicit matchers:
|
|
483
|
+
|
|
484
|
+
```tsx
|
|
485
|
+
import { View, Text } from 'react-native';
|
|
486
|
+
import { render, screen } from '@testing-library/react-native';
|
|
487
|
+
|
|
488
|
+
test('uses getBy as assertion', async () => {
|
|
489
|
+
await render(
|
|
490
|
+
<View>
|
|
491
|
+
<Text>Hello</Text>
|
|
492
|
+
</View>,
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
// ✅ Good - getBy throws if not found, so it's an assertion
|
|
496
|
+
const element = screen.getByText('Hello');
|
|
497
|
+
expect(element).toBeOnTheScreen();
|
|
498
|
+
|
|
499
|
+
// ✅ Also good - more explicit
|
|
500
|
+
expect(screen.getByText('Hello')).toBeOnTheScreen();
|
|
501
|
+
|
|
502
|
+
// ❌ Bad - redundant assertion
|
|
503
|
+
// const element = screen.getByText('Hello');
|
|
504
|
+
// expect(element).not.toBeNull(); // getBy already throws if null
|
|
505
|
+
});
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## Having multiple assertions in a single `waitFor` callback
|
|
509
|
+
|
|
510
|
+
**Importance: low**
|
|
511
|
+
|
|
512
|
+
Keep `waitFor` callbacks focused on a single assertion:
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
import { View, Text } from 'react-native';
|
|
516
|
+
import { render, screen, waitFor } from '@testing-library/react-native';
|
|
517
|
+
|
|
518
|
+
test('waits with single assertion', async () => {
|
|
519
|
+
const Component = () => {
|
|
520
|
+
const [count, setCount] = React.useState(0);
|
|
521
|
+
|
|
522
|
+
React.useEffect(() => {
|
|
523
|
+
setTimeout(() => setCount(1), 100);
|
|
524
|
+
}, []);
|
|
525
|
+
|
|
526
|
+
return (
|
|
527
|
+
<View>
|
|
528
|
+
<Text>Count: {count}</Text>
|
|
529
|
+
</View>
|
|
530
|
+
);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
await render(<Component />);
|
|
534
|
+
|
|
535
|
+
// ✅ Good - single assertion per waitFor
|
|
536
|
+
await waitFor(() => {
|
|
537
|
+
expect(screen.getByText('Count: 1')).toBeOnTheScreen();
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// If you need multiple assertions, do them after waitFor
|
|
541
|
+
expect(screen.getByText('Count: 1')).toHaveTextContent('Count: 1');
|
|
542
|
+
|
|
543
|
+
// ❌ Bad - multiple assertions in waitFor
|
|
544
|
+
// await waitFor(() => {
|
|
545
|
+
// expect(screen.getByText('Count: 1')).toBeOnTheScreen();
|
|
546
|
+
// expect(screen.getByText('Count: 1')).toHaveTextContent('Count: 1');
|
|
547
|
+
// });
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
## Using `wrapper` as the variable name
|
|
552
|
+
|
|
553
|
+
**Importance: low**
|
|
554
|
+
|
|
555
|
+
This is not really a "mistake" per se, but it's a common pattern that can be improved. When you use the `wrapper` option in `render`, you might be tempted to name your wrapper component `Wrapper`:
|
|
556
|
+
|
|
557
|
+
```tsx
|
|
558
|
+
import { View } from 'react-native';
|
|
559
|
+
import { render, screen } from '@testing-library/react-native';
|
|
560
|
+
|
|
561
|
+
test('renders with wrapper', async () => {
|
|
562
|
+
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
563
|
+
<View testID="wrapper">{children}</View>
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
await render(<View testID="content">Content</View>, {
|
|
567
|
+
wrapper: Wrapper,
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
expect(screen.getByTestId('content')).toBeOnTheScreen();
|
|
571
|
+
});
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
This works fine, but it's more conventional to name it something more descriptive like `ThemeProvider` or `AllTheProviders` (if you're wrapping with multiple providers). This makes it clearer what the wrapper is doing.
|
|
575
|
+
|
|
576
|
+
## Summary
|
|
577
|
+
|
|
578
|
+
The key principles to remember:
|
|
579
|
+
|
|
580
|
+
1. **Use the right query** - Prefer `getByRole` as your first choice, use `findBy*` for async elements, and `queryBy*` only for checking non-existence
|
|
581
|
+
2. **Use proper assertions** - Use RNTL's built-in matchers (`toBeOnTheScreen()`, `toBeDisabled()`, etc.) instead of asserting on props directly
|
|
582
|
+
3. **Handle async operations correctly** - Always `await` `render()`, `renderHook`, `fireEvent`,and `userEvent` methods
|
|
583
|
+
4. **Use `waitFor` correctly** - Avoid side-effects in callbacks, use `findBy*` instead when possible, and keep callbacks focused
|
|
584
|
+
5. **Follow accessibility best practices** - Prefer ARIA attributes (`role`, `aria-label`) over `accessibility*` props
|
|
585
|
+
6. **Organize code well** - Use `screen` over destructuring, prefer `userEvent` over `fireEvent`, and don't use `cleanup()`
|
|
586
|
+
|
|
587
|
+
By following these principles, your tests will be more maintainable, accessible, and reliable.
|