@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
package/README.md
CHANGED
|
@@ -12,11 +12,6 @@
|
|
|
12
12
|
[![MIT License][license-badge]][license]
|
|
13
13
|
[![Sponsored by Callstack][callstack-badge]][callstack]
|
|
14
14
|
|
|
15
|
-
> [!WARNING]
|
|
16
|
-
> **Beta Version:** This version (v14) is currently in beta. APIs and behavior may change before the stable release. Please report any issues you encounter.
|
|
17
|
-
>
|
|
18
|
-
> For stable version (v13) see [README-v13.md](./README-v13.md).
|
|
19
|
-
|
|
20
15
|
## The problem
|
|
21
16
|
|
|
22
17
|
You want to write maintainable tests for your React Native components. Your tests should avoid implementation details and focus on giving you confidence. They should remain maintainable so refactors (changes to implementation but not functionality) don't break your tests and slow you and your team down.
|
|
@@ -34,7 +29,7 @@ This project is inspired by [React Testing Library](https://github.com/testing-l
|
|
|
34
29
|
Open a Terminal in your project's folder and run:
|
|
35
30
|
|
|
36
31
|
```sh
|
|
37
|
-
npm install --save-dev @testing-library/react-native
|
|
32
|
+
npm install --save-dev @testing-library/react-native
|
|
38
33
|
```
|
|
39
34
|
|
|
40
35
|
This library has a `peerDependencies` listing for [Test Renderer](https://github.com/mdjastrzebski/test-renderer). Make sure to install it as a dev dependency:
|
|
@@ -140,14 +140,17 @@ function computeAriaModal(instance) {
|
|
|
140
140
|
return instance.props['aria-modal'] ?? instance.props.accessibilityViewIsModal;
|
|
141
141
|
}
|
|
142
142
|
function computeAriaLabel(instance) {
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
143
|
+
const labelElementIds = getAriaLabelledByIds(instance);
|
|
144
|
+
if (labelElementIds.length > 0) {
|
|
145
145
|
const container = (0, _componentTree.getContainerInstance)(instance);
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return (0, _textContent.getTextContent)(labelInstance[0]);
|
|
146
|
+
const labelTexts = labelElementIds.map(labelElementId => {
|
|
147
|
+
const labelInstance = (0, _findAll.findAll)(container, node => (0, _componentTree.isTestInstance)(node) && node.props.nativeID === labelElementId, {
|
|
148
|
+
includeHiddenElements: true
|
|
149
|
+
});
|
|
150
|
+
return labelInstance.length > 0 ? (0, _textContent.getTextContent)(labelInstance[0]) : undefined;
|
|
151
|
+
}).filter(labelText => labelText !== undefined);
|
|
152
|
+
if (labelTexts.length > 0) {
|
|
153
|
+
return labelTexts.join(' ').trim().replace(/\s+/g, ' ');
|
|
151
154
|
}
|
|
152
155
|
}
|
|
153
156
|
const explicitLabel = instance.props['aria-label'] ?? instance.props.accessibilityLabel;
|
|
@@ -161,6 +164,20 @@ function computeAriaLabel(instance) {
|
|
|
161
164
|
}
|
|
162
165
|
return undefined;
|
|
163
166
|
}
|
|
167
|
+
function getAriaLabelledByIds(instance) {
|
|
168
|
+
const ariaLabelledBy = instance.props['aria-labelledby'];
|
|
169
|
+
if (typeof ariaLabelledBy === 'string') {
|
|
170
|
+
return [ariaLabelledBy];
|
|
171
|
+
}
|
|
172
|
+
const accessibilityLabelledBy = instance.props.accessibilityLabelledBy;
|
|
173
|
+
if (Array.isArray(accessibilityLabelledBy)) {
|
|
174
|
+
return accessibilityLabelledBy;
|
|
175
|
+
}
|
|
176
|
+
if (typeof accessibilityLabelledBy === 'string') {
|
|
177
|
+
return [accessibilityLabelledBy];
|
|
178
|
+
}
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
164
181
|
|
|
165
182
|
// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#busy-state
|
|
166
183
|
function computeAriaBusy({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.js","names":["_reactNative","require","_componentTree","_findAll","_hostComponentNames","_textContent","_textInput","accessibilityStateKeys","exports","accessibilityValueKeys","isHiddenFromAccessibility","instance","cache","current","isCurrentSubtreeInaccessible","get","undefined","isSubtreeInaccessible","set","parent","isInaccessible","props","accessibilityElementsHidden","importantForAccessibility","flatStyle","StyleSheet","flatten","style","display","hostSiblings","getInstanceSiblings","some","sibling","computeAriaModal","isAccessibilityElement","isHostImage","alt","accessible","isHostText","isHostTextInput","isHostSwitch","getRole","explicitRole","role","accessibilityRole","normalizeRole","accessibilityViewIsModal","computeAriaLabel","labelElementIds","getAriaLabelledByIds","length","container","getContainerInstance","labelTexts","map","labelElementId","labelInstance","findAll","node","isTestInstance","nativeID","includeHiddenElements","getTextContent","filter","labelText","join","trim","replace","explicitLabel","accessibilityLabel","ariaLabelledBy","accessibilityLabelledBy","Array","isArray","computeAriaBusy","accessibilityState","busy","computeAriaChecked","value","rolesSupportingCheckedState","checked","computeAriaDisabled","isEditableTextInput","disabled","computeAriaExpanded","expanded","computeAriaSelected","selected","computeAriaValue","accessibilityValue","ariaValueMax","ariaValueMin","ariaValueNow","ariaValueText","max","min","now","text","computeAccessibleName","options","label","placeholder","root","parts","child","children","push","childLabel","checkbox","radio","switch"],"sources":["../../src/helpers/accessibility.ts"],"sourcesContent":["import type { AccessibilityRole, AccessibilityState, AccessibilityValue, Role } from 'react-native';\nimport { StyleSheet } from 'react-native';\nimport type { TestInstance } from 'test-renderer';\n\nimport { getContainerInstance, getInstanceSiblings, isTestInstance } from './component-tree';\nimport { findAll } from './find-all';\nimport { isHostImage, isHostSwitch, isHostText, isHostTextInput } from './host-component-names';\nimport { getTextContent } from './text-content';\nimport { isEditableTextInput } from './text-input';\n\ntype IsInaccessibleOptions = {\n cache?: WeakMap<TestInstance, boolean>;\n};\n\nexport const accessibilityStateKeys: (keyof AccessibilityState)[] = [\n 'disabled',\n 'selected',\n 'checked',\n 'busy',\n 'expanded',\n];\n\nexport const accessibilityValueKeys: (keyof AccessibilityValue)[] = ['min', 'max', 'now', 'text'];\n\nexport function isHiddenFromAccessibility(\n instance: TestInstance | null,\n { cache }: IsInaccessibleOptions = {},\n): boolean {\n if (instance == null) {\n return true;\n }\n\n let current: TestInstance | null = instance;\n while (current) {\n let isCurrentSubtreeInaccessible = cache?.get(current);\n\n if (isCurrentSubtreeInaccessible === undefined) {\n isCurrentSubtreeInaccessible = isSubtreeInaccessible(current);\n cache?.set(current, isCurrentSubtreeInaccessible);\n }\n\n if (isCurrentSubtreeInaccessible) {\n return true;\n }\n\n current = current.parent;\n }\n\n return false;\n}\n\n/** RTL-compatibility alias for `isHiddenFromAccessibility` */\nexport const isInaccessible = isHiddenFromAccessibility;\n\nfunction isSubtreeInaccessible(instance: TestInstance): boolean {\n // See: https://reactnative.dev/docs/accessibility#aria-hidden\n if (instance.props['aria-hidden']) {\n return true;\n }\n\n // iOS: accessibilityElementsHidden\n // See: https://reactnative.dev/docs/accessibility#accessibilityelementshidden-ios\n if (instance.props.accessibilityElementsHidden) {\n return true;\n }\n\n // Android: importantForAccessibility\n // See: https://reactnative.dev/docs/accessibility#importantforaccessibility-android\n if (instance.props.importantForAccessibility === 'no-hide-descendants') {\n return true;\n }\n\n // Note that `opacity: 0` is not treated as inaccessible on iOS\n const flatStyle = StyleSheet.flatten(instance.props.style) ?? {};\n if (flatStyle.display === 'none') return true;\n\n // iOS: accessibilityViewIsModal or aria-modal\n // See: https://reactnative.dev/docs/accessibility#accessibilityviewismodal-ios\n const hostSiblings = getInstanceSiblings(instance);\n if (hostSiblings.some((sibling) => computeAriaModal(sibling))) {\n return true;\n }\n\n return false;\n}\n\nexport function isAccessibilityElement(instance: TestInstance | null): boolean {\n if (instance == null) {\n return false;\n }\n\n // https://github.com/facebook/react-native/blob/8dabed60f456e76a9e53273b601446f34de41fb5/packages/react-native/Libraries/Image/Image.ios.js#L172\n if (isHostImage(instance) && instance.props.alt !== undefined) {\n return true;\n }\n\n if (instance.props.accessible !== undefined) {\n return instance.props.accessible;\n }\n\n return isHostText(instance) || isHostTextInput(instance) || isHostSwitch(instance);\n}\n\n/**\n * Returns the accessibility role for given element. It will return explicit\n * role from either `role` or `accessibilityRole` props if set.\n *\n * If explicit role is not available, it would try to return default element\n * role:\n * - `text` for `Text` elements\n *\n * In all other cases this functions returns `none`.\n *\n * @param instance\n * @returns\n */\nexport function getRole(instance: TestInstance): Role | AccessibilityRole {\n const explicitRole = instance.props.role ?? instance.props.accessibilityRole;\n if (explicitRole) {\n return normalizeRole(explicitRole);\n }\n\n if (isHostText(instance)) {\n return 'text';\n }\n\n // Note: host Image elements report \"image\" role in screen reader only on Android, but not on iOS.\n // It's better to require explicit role for Image elements.\n\n return 'none';\n}\n\n/**\n * There are some duplications between (ARIA) `Role` and `AccessibilityRole` types.\n * Resolve them by using ARIA `Role` type where possible.\n *\n * @param role Role to normalize\n * @returns Normalized role\n */\nexport function normalizeRole(role: string): Role | AccessibilityRole {\n if (role === 'image') {\n return 'img';\n }\n\n return role as Role | AccessibilityRole;\n}\n\nexport function computeAriaModal(instance: TestInstance): boolean | undefined {\n return instance.props['aria-modal'] ?? instance.props.accessibilityViewIsModal;\n}\n\nexport function computeAriaLabel(instance: TestInstance): string | undefined {\n const labelElementIds = getAriaLabelledByIds(instance);\n if (labelElementIds.length > 0) {\n const container = getContainerInstance(instance);\n const labelTexts = labelElementIds\n .map((labelElementId) => {\n const labelInstance = findAll(\n container,\n (node) => isTestInstance(node) && node.props.nativeID === labelElementId,\n { includeHiddenElements: true },\n );\n\n return labelInstance.length > 0 ? getTextContent(labelInstance[0]) : undefined;\n })\n .filter((labelText): labelText is string => labelText !== undefined);\n\n if (labelTexts.length > 0) {\n return labelTexts.join(' ').trim().replace(/\\s+/g, ' ');\n }\n }\n\n const explicitLabel = instance.props['aria-label'] ?? instance.props.accessibilityLabel;\n if (explicitLabel) {\n return explicitLabel;\n }\n\n //https://github.com/facebook/react-native/blob/8dabed60f456e76a9e53273b601446f34de41fb5/packages/react-native/Libraries/Image/Image.ios.js#L173\n if (isHostImage(instance) && instance.props.alt) {\n return instance.props.alt;\n }\n\n return undefined;\n}\n\nfunction getAriaLabelledByIds(instance: TestInstance): string[] {\n const ariaLabelledBy = instance.props['aria-labelledby'];\n if (typeof ariaLabelledBy === 'string') {\n return [ariaLabelledBy];\n }\n\n const accessibilityLabelledBy = instance.props.accessibilityLabelledBy;\n if (Array.isArray(accessibilityLabelledBy)) {\n return accessibilityLabelledBy;\n }\n\n if (typeof accessibilityLabelledBy === 'string') {\n return [accessibilityLabelledBy];\n }\n\n return [];\n}\n\n// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#busy-state\nexport function computeAriaBusy({ props }: TestInstance): boolean {\n return props['aria-busy'] ?? props.accessibilityState?.busy ?? false;\n}\n\n// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#checked-state\nexport function computeAriaChecked(instance: TestInstance): AccessibilityState['checked'] {\n const { props } = instance;\n\n if (isHostSwitch(instance)) {\n return props.value;\n }\n\n const role = getRole(instance);\n if (!rolesSupportingCheckedState[role]) {\n return undefined;\n }\n\n return props['aria-checked'] ?? props.accessibilityState?.checked;\n}\n\n// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#disabled-state\nexport function computeAriaDisabled(instance: TestInstance): boolean {\n if (isHostTextInput(instance) && !isEditableTextInput(instance)) {\n return true;\n }\n\n const { props } = instance;\n\n if (isHostText(instance) && props.disabled) {\n return true;\n }\n\n return props['aria-disabled'] ?? props.accessibilityState?.disabled ?? false;\n}\n\n// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#expanded-state\nexport function computeAriaExpanded({ props }: TestInstance): boolean | undefined {\n return props['aria-expanded'] ?? props.accessibilityState?.expanded;\n}\n\n// See: https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State#selected-state\nexport function computeAriaSelected({ props }: TestInstance): boolean {\n return props['aria-selected'] ?? props.accessibilityState?.selected ?? false;\n}\n\nexport function computeAriaValue(instance: TestInstance): AccessibilityValue {\n const {\n accessibilityValue,\n 'aria-valuemax': ariaValueMax,\n 'aria-valuemin': ariaValueMin,\n 'aria-valuenow': ariaValueNow,\n 'aria-valuetext': ariaValueText,\n } = instance.props;\n\n return {\n max: ariaValueMax ?? accessibilityValue?.max,\n min: ariaValueMin ?? accessibilityValue?.min,\n now: ariaValueNow ?? accessibilityValue?.now,\n text: ariaValueText ?? accessibilityValue?.text,\n };\n}\n\ntype ComputeAccessibleNameOptions = {\n root?: boolean;\n};\n\nexport function computeAccessibleName(\n instance: TestInstance,\n options?: ComputeAccessibleNameOptions,\n): string | undefined {\n const label = computeAriaLabel(instance);\n if (label) {\n return label;\n }\n\n if (isHostTextInput(instance) && instance.props.placeholder && options?.root !== false) {\n return instance.props.placeholder;\n }\n\n const parts = [];\n for (const child of instance.children) {\n if (typeof child === 'string') {\n if (child) {\n parts.push(child);\n }\n } else {\n const childLabel = computeAccessibleName(child, { root: false });\n if (childLabel) {\n parts.push(childLabel);\n }\n }\n }\n\n return parts.join(' ');\n}\n\ntype RoleSupportMap = Partial<Record<Role | AccessibilityRole, true>>;\n\nexport const rolesSupportingCheckedState: RoleSupportMap = {\n checkbox: true,\n radio: true,\n switch: true,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAGA,IAAAC,cAAA,GAAAD,OAAA;AACA,IAAAE,QAAA,GAAAF,OAAA;AACA,IAAAG,mBAAA,GAAAH,OAAA;AACA,IAAAI,YAAA,GAAAJ,OAAA;AACA,IAAAK,UAAA,GAAAL,OAAA;AAMO,MAAMM,sBAAoD,GAAAC,OAAA,CAAAD,sBAAA,GAAG,CAClE,UAAU,EACV,UAAU,EACV,SAAS,EACT,MAAM,EACN,UAAU,CACX;AAEM,MAAME,sBAAoD,GAAAD,OAAA,CAAAC,sBAAA,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC;AAE1F,SAASC,yBAAyBA,CACvCC,QAA6B,EAC7B;EAAEC;AAA6B,CAAC,GAAG,CAAC,CAAC,EAC5B;EACT,IAAID,QAAQ,IAAI,IAAI,EAAE;IACpB,OAAO,IAAI;EACb;EAEA,IAAIE,OAA4B,GAAGF,QAAQ;EAC3C,OAAOE,OAAO,EAAE;IACd,IAAIC,4BAA4B,GAAGF,KAAK,EAAEG,GAAG,CAACF,OAAO,CAAC;IAEtD,IAAIC,4BAA4B,KAAKE,SAAS,EAAE;MAC9CF,4BAA4B,GAAGG,qBAAqB,CAACJ,OAAO,CAAC;MAC7DD,KAAK,EAAEM,GAAG,CAACL,OAAO,EAAEC,4BAA4B,CAAC;IACnD;IAEA,IAAIA,4BAA4B,EAAE;MAChC,OAAO,IAAI;IACb;IAEAD,OAAO,GAAGA,OAAO,CAACM,MAAM;EAC1B;EAEA,OAAO,KAAK;AACd;;AAEA;AACO,MAAMC,cAAc,GAAAZ,OAAA,CAAAY,cAAA,GAAGV,yBAAyB;AAEvD,SAASO,qBAAqBA,CAACN,QAAsB,EAAW;EAC9D;EACA,IAAIA,QAAQ,CAACU,KAAK,CAAC,aAAa,CAAC,EAAE;IACjC,OAAO,IAAI;EACb;;EAEA;EACA;EACA,IAAIV,QAAQ,CAACU,KAAK,CAACC,2BAA2B,EAAE;IAC9C,OAAO,IAAI;EACb;;EAEA;EACA;EACA,IAAIX,QAAQ,CAACU,KAAK,CAACE,yBAAyB,KAAK,qBAAqB,EAAE;IACtE,OAAO,IAAI;EACb;;EAEA;EACA,MAAMC,SAAS,GAAGC,uBAAU,CAACC,OAAO,CAACf,QAAQ,CAACU,KAAK,CAACM,KAAK,CAAC,IAAI,CAAC,CAAC;EAChE,IAAIH,SAAS,CAACI,OAAO,KAAK,MAAM,EAAE,OAAO,IAAI;;EAE7C;EACA;EACA,MAAMC,YAAY,GAAG,IAAAC,kCAAmB,EAACnB,QAAQ,CAAC;EAClD,IAAIkB,YAAY,CAACE,IAAI,CAAEC,OAAO,IAAKC,gBAAgB,CAACD,OAAO,CAAC,CAAC,EAAE;IAC7D,OAAO,IAAI;EACb;EAEA,OAAO,KAAK;AACd;AAEO,SAASE,sBAAsBA,CAACvB,QAA6B,EAAW;EAC7E,IAAIA,QAAQ,IAAI,IAAI,EAAE;IACpB,OAAO,KAAK;EACd;;EAEA;EACA,IAAI,IAAAwB,+BAAW,EAACxB,QAAQ,CAAC,IAAIA,QAAQ,CAACU,KAAK,CAACe,GAAG,KAAKpB,SAAS,EAAE;IAC7D,OAAO,IAAI;EACb;EAEA,IAAIL,QAAQ,CAACU,KAAK,CAACgB,UAAU,KAAKrB,SAAS,EAAE;IAC3C,OAAOL,QAAQ,CAACU,KAAK,CAACgB,UAAU;EAClC;EAEA,OAAO,IAAAC,8BAAU,EAAC3B,QAAQ,CAAC,IAAI,IAAA4B,mCAAe,EAAC5B,QAAQ,CAAC,IAAI,IAAA6B,gCAAY,EAAC7B,QAAQ,CAAC;AACpF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS8B,OAAOA,CAAC9B,QAAsB,EAA4B;EACxE,MAAM+B,YAAY,GAAG/B,QAAQ,CAACU,KAAK,CAACsB,IAAI,IAAIhC,QAAQ,CAACU,KAAK,CAACuB,iBAAiB;EAC5E,IAAIF,YAAY,EAAE;IAChB,OAAOG,aAAa,CAACH,YAAY,CAAC;EACpC;EAEA,IAAI,IAAAJ,8BAAU,EAAC3B,QAAQ,CAAC,EAAE;IACxB,OAAO,MAAM;EACf;;EAEA;EACA;;EAEA,OAAO,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASkC,aAAaA,CAACF,IAAY,EAA4B;EACpE,IAAIA,IAAI,KAAK,OAAO,EAAE;IACpB,OAAO,KAAK;EACd;EAEA,OAAOA,IAAI;AACb;AAEO,SAASV,gBAAgBA,CAACtB,QAAsB,EAAuB;EAC5E,OAAOA,QAAQ,CAACU,KAAK,CAAC,YAAY,CAAC,IAAIV,QAAQ,CAACU,KAAK,CAACyB,wBAAwB;AAChF;AAEO,SAASC,gBAAgBA,CAACpC,QAAsB,EAAsB;EAC3E,MAAMqC,eAAe,GAAGC,oBAAoB,CAACtC,QAAQ,CAAC;EACtD,IAAIqC,eAAe,CAACE,MAAM,GAAG,CAAC,EAAE;IAC9B,MAAMC,SAAS,GAAG,IAAAC,mCAAoB,EAACzC,QAAQ,CAAC;IAChD,MAAM0C,UAAU,GAAGL,eAAe,CAC/BM,GAAG,CAAEC,cAAc,IAAK;MACvB,MAAMC,aAAa,GAAG,IAAAC,gBAAO,EAC3BN,SAAS,EACRO,IAAI,IAAK,IAAAC,6BAAc,EAACD,IAAI,CAAC,IAAIA,IAAI,CAACrC,KAAK,CAACuC,QAAQ,KAAKL,cAAc,EACxE;QAAEM,qBAAqB,EAAE;MAAK,CAChC,CAAC;MAED,OAAOL,aAAa,CAACN,MAAM,GAAG,CAAC,GAAG,IAAAY,2BAAc,EAACN,aAAa,CAAC,CAAC,CAAC,CAAC,GAAGxC,SAAS;IAChF,CAAC,CAAC,CACD+C,MAAM,CAAEC,SAAS,IAA0BA,SAAS,KAAKhD,SAAS,CAAC;IAEtE,IAAIqC,UAAU,CAACH,MAAM,GAAG,CAAC,EAAE;MACzB,OAAOG,UAAU,CAACY,IAAI,CAAC,GAAG,CAAC,CAACC,IAAI,CAAC,CAAC,CAACC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;IACzD;EACF;EAEA,MAAMC,aAAa,GAAGzD,QAAQ,CAACU,KAAK,CAAC,YAAY,CAAC,IAAIV,QAAQ,CAACU,KAAK,CAACgD,kBAAkB;EACvF,IAAID,aAAa,EAAE;IACjB,OAAOA,aAAa;EACtB;;EAEA;EACA,IAAI,IAAAjC,+BAAW,EAACxB,QAAQ,CAAC,IAAIA,QAAQ,CAACU,KAAK,CAACe,GAAG,EAAE;IAC/C,OAAOzB,QAAQ,CAACU,KAAK,CAACe,GAAG;EAC3B;EAEA,OAAOpB,SAAS;AAClB;AAEA,SAASiC,oBAAoBA,CAACtC,QAAsB,EAAY;EAC9D,MAAM2D,cAAc,GAAG3D,QAAQ,CAACU,KAAK,CAAC,iBAAiB,CAAC;EACxD,IAAI,OAAOiD,cAAc,KAAK,QAAQ,EAAE;IACtC,OAAO,CAACA,cAAc,CAAC;EACzB;EAEA,MAAMC,uBAAuB,GAAG5D,QAAQ,CAACU,KAAK,CAACkD,uBAAuB;EACtE,IAAIC,KAAK,CAACC,OAAO,CAACF,uBAAuB,CAAC,EAAE;IAC1C,OAAOA,uBAAuB;EAChC;EAEA,IAAI,OAAOA,uBAAuB,KAAK,QAAQ,EAAE;IAC/C,OAAO,CAACA,uBAAuB,CAAC;EAClC;EAEA,OAAO,EAAE;AACX;;AAEA;AACO,SAASG,eAAeA,CAAC;EAAErD;AAAoB,CAAC,EAAW;EAChE,OAAOA,KAAK,CAAC,WAAW,CAAC,IAAIA,KAAK,CAACsD,kBAAkB,EAAEC,IAAI,IAAI,KAAK;AACtE;;AAEA;AACO,SAASC,kBAAkBA,CAAClE,QAAsB,EAAiC;EACxF,MAAM;IAAEU;EAAM,CAAC,GAAGV,QAAQ;EAE1B,IAAI,IAAA6B,gCAAY,EAAC7B,QAAQ,CAAC,EAAE;IAC1B,OAAOU,KAAK,CAACyD,KAAK;EACpB;EAEA,MAAMnC,IAAI,GAAGF,OAAO,CAAC9B,QAAQ,CAAC;EAC9B,IAAI,CAACoE,2BAA2B,CAACpC,IAAI,CAAC,EAAE;IACtC,OAAO3B,SAAS;EAClB;EAEA,OAAOK,KAAK,CAAC,cAAc,CAAC,IAAIA,KAAK,CAACsD,kBAAkB,EAAEK,OAAO;AACnE;;AAEA;AACO,SAASC,mBAAmBA,CAACtE,QAAsB,EAAW;EACnE,IAAI,IAAA4B,mCAAe,EAAC5B,QAAQ,CAAC,IAAI,CAAC,IAAAuE,8BAAmB,EAACvE,QAAQ,CAAC,EAAE;IAC/D,OAAO,IAAI;EACb;EAEA,MAAM;IAAEU;EAAM,CAAC,GAAGV,QAAQ;EAE1B,IAAI,IAAA2B,8BAAU,EAAC3B,QAAQ,CAAC,IAAIU,KAAK,CAAC8D,QAAQ,EAAE;IAC1C,OAAO,IAAI;EACb;EAEA,OAAO9D,KAAK,CAAC,eAAe,CAAC,IAAIA,KAAK,CAACsD,kBAAkB,EAAEQ,QAAQ,IAAI,KAAK;AAC9E;;AAEA;AACO,SAASC,mBAAmBA,CAAC;EAAE/D;AAAoB,CAAC,EAAuB;EAChF,OAAOA,KAAK,CAAC,eAAe,CAAC,IAAIA,KAAK,CAACsD,kBAAkB,EAAEU,QAAQ;AACrE;;AAEA;AACO,SAASC,mBAAmBA,CAAC;EAAEjE;AAAoB,CAAC,EAAW;EACpE,OAAOA,KAAK,CAAC,eAAe,CAAC,IAAIA,KAAK,CAACsD,kBAAkB,EAAEY,QAAQ,IAAI,KAAK;AAC9E;AAEO,SAASC,gBAAgBA,CAAC7E,QAAsB,EAAsB;EAC3E,MAAM;IACJ8E,kBAAkB;IAClB,eAAe,EAAEC,YAAY;IAC7B,eAAe,EAAEC,YAAY;IAC7B,eAAe,EAAEC,YAAY;IAC7B,gBAAgB,EAAEC;EACpB,CAAC,GAAGlF,QAAQ,CAACU,KAAK;EAElB,OAAO;IACLyE,GAAG,EAAEJ,YAAY,IAAID,kBAAkB,EAAEK,GAAG;IAC5CC,GAAG,EAAEJ,YAAY,IAAIF,kBAAkB,EAAEM,GAAG;IAC5CC,GAAG,EAAEJ,YAAY,IAAIH,kBAAkB,EAAEO,GAAG;IAC5CC,IAAI,EAAEJ,aAAa,IAAIJ,kBAAkB,EAAEQ;EAC7C,CAAC;AACH;AAMO,SAASC,qBAAqBA,CACnCvF,QAAsB,EACtBwF,OAAsC,EAClB;EACpB,MAAMC,KAAK,GAAGrD,gBAAgB,CAACpC,QAAQ,CAAC;EACxC,IAAIyF,KAAK,EAAE;IACT,OAAOA,KAAK;EACd;EAEA,IAAI,IAAA7D,mCAAe,EAAC5B,QAAQ,CAAC,IAAIA,QAAQ,CAACU,KAAK,CAACgF,WAAW,IAAIF,OAAO,EAAEG,IAAI,KAAK,KAAK,EAAE;IACtF,OAAO3F,QAAQ,CAACU,KAAK,CAACgF,WAAW;EACnC;EAEA,MAAME,KAAK,GAAG,EAAE;EAChB,KAAK,MAAMC,KAAK,IAAI7F,QAAQ,CAAC8F,QAAQ,EAAE;IACrC,IAAI,OAAOD,KAAK,KAAK,QAAQ,EAAE;MAC7B,IAAIA,KAAK,EAAE;QACTD,KAAK,CAACG,IAAI,CAACF,KAAK,CAAC;MACnB;IACF,CAAC,MAAM;MACL,MAAMG,UAAU,GAAGT,qBAAqB,CAACM,KAAK,EAAE;QAAEF,IAAI,EAAE;MAAM,CAAC,CAAC;MAChE,IAAIK,UAAU,EAAE;QACdJ,KAAK,CAACG,IAAI,CAACC,UAAU,CAAC;MACxB;IACF;EACF;EAEA,OAAOJ,KAAK,CAACtC,IAAI,CAAC,GAAG,CAAC;AACxB;AAIO,MAAMc,2BAA2C,GAAAvE,OAAA,CAAAuE,2BAAA,GAAG;EACzD6B,QAAQ,EAAE,IAAI;EACdC,KAAK,EAAE,IAAI;EACXC,MAAM,EAAE;AACV,CAAC","ignoreList":[]}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# React Native Testing Library package docs
|
|
2
|
+
|
|
3
|
+
These markdown files are bundled with the npm package for coding agents. They describe the installed package version.
|
|
4
|
+
|
|
5
|
+
Start with [LLM Guidelines](./guides/llm-guidelines.md) for rules overview, or use the included page list below to load specific references.
|
|
6
|
+
|
|
7
|
+
## Included pages
|
|
8
|
+
|
|
9
|
+
- [LLM Guidelines](./guides/llm-guidelines.md) - Quick rules for agents writing RNTL tests.
|
|
10
|
+
- [Quick Start](./guides/quick-start.md) - Installation and setup basics.
|
|
11
|
+
- [How to Query](./guides/how-to-query.md) - Query priority and selection guidance.
|
|
12
|
+
- [Common Mistakes](./guides/common-mistakes.md) - Common anti-patterns and preferred alternatives.
|
|
13
|
+
- [Troubleshooting](./guides/troubleshooting.md) - Common integration and runtime issues.
|
|
14
|
+
- [Testing Environment](./guides/testing-environment.md) - How RNTL simulates React Native under tests.
|
|
15
|
+
- [Understanding act](./guides/understanding-act.md) - How act warnings happen and how to resolve them.
|
|
16
|
+
- [Migration to 14.x](./guides/migration-v14.md) - Breaking changes and upgrade steps for RNTL v14.
|
|
17
|
+
- [API Overview](./api/overview.md) - Top-level API map.
|
|
18
|
+
- [render](./api/render.md) - Rendering components in tests.
|
|
19
|
+
- [screen](./api/screen.md) - Recommended global query surface.
|
|
20
|
+
- [Queries](./api/queries.md) - Query variants and predicates.
|
|
21
|
+
- [Jest Matchers](./api/jest-matchers.md) - Built-in RNTL assertions.
|
|
22
|
+
- [User Event](./api/user-event.md) - Realistic user interactions.
|
|
23
|
+
- [Fire Event](./api/fire-event.md) - Low-level event triggering.
|
|
24
|
+
- [Async Utilities](./api/async-utilities.md) - Async queries and wait helpers.
|
|
25
|
+
- [renderHook](./api/render-hook.md) - Testing custom hooks.
|
|
26
|
+
- [Configuration](./api/configuration.md) - Runtime configuration options.
|
|
27
|
+
- [Accessibility](./api/accessibility.md) - Accessibility helpers and hidden elements.
|
|
28
|
+
- [Other Helpers](./api/other-helpers.md) - within, act, cleanup, and related helpers.
|
|
29
|
+
- [Custom Render](./cookbook/custom-render.md) - Reusable render wrappers.
|
|
30
|
+
- [Async Events](./cookbook/async-events.md) - Testing async interactions.
|
|
31
|
+
- [Network Requests](./cookbook/network-requests.md) - Testing components that make network requests.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Accessibility
|
|
2
|
+
|
|
3
|
+
## `isHiddenFromAccessibility`
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
function isHiddenFromAccessibility(instance: TestInstance | null): boolean {}
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Also available as `isInaccessible()` alias for React Testing Library compatibility.
|
|
10
|
+
|
|
11
|
+
Checks if given element is hidden from assistive technology, e.g. screen readers.
|
|
12
|
+
|
|
13
|
+
> [!NOTE]
|
|
14
|
+
> Like [`isInaccessible`](https://testing-library.com/docs/dom-testing-library/api-accessibility/#isinaccessible) function from DOM Testing Library this function considers both accessibility elements and presentational elements (regular `View`s) to be accessible, unless they are hidden in terms of host platform.
|
|
15
|
+
>
|
|
16
|
+
> This covers only part of [ARIA notion of Accessibility Tree](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), as ARIA excludes both hidden and presentational elements from the Accessibility Tree.
|
|
17
|
+
|
|
18
|
+
For the scope of this function, element is inaccessible when it, or any of its ancestors, meets any of the following conditions:
|
|
19
|
+
|
|
20
|
+
- it has `display: none` style
|
|
21
|
+
- it has [`aria-hidden`](https://reactnative.dev/docs/accessibility#aria-hidden) prop set to `true`
|
|
22
|
+
- it has [`accessibilityElementsHidden`](https://reactnative.dev/docs/accessibility#accessibilityelementshidden-ios) prop set to `true`
|
|
23
|
+
- it has [`importantForAccessibility`](https://reactnative.dev/docs/accessibility#importantforaccessibility-android) prop set to `no-hide-descendants`
|
|
24
|
+
- it has sibling host element with either [`aria-modal`](https://reactnative.dev/docs/accessibility#aria-modal-ios) or [`accessibilityViewIsModal`](https://reactnative.dev/docs/accessibility#accessibilityviewismodal-ios) prop set to `true`
|
|
25
|
+
|
|
26
|
+
Specifying `accessible={false}`, `role="none"`, `accessibilityRole="none"`, or `importantForAccessibility="no"` props does not cause the element to become inaccessible.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Async utilities
|
|
2
|
+
|
|
3
|
+
## `findBy*` queries
|
|
4
|
+
|
|
5
|
+
The `findBy*` queries are used to find elements that are not instantly available but will be added as a result of some asynchronous action. Learn more details [here](./queries.md#find-by).
|
|
6
|
+
|
|
7
|
+
## `waitFor`
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
function waitFor<T>(
|
|
11
|
+
expectation: () => T,
|
|
12
|
+
options?: {
|
|
13
|
+
timeout?: number;
|
|
14
|
+
interval?: number;
|
|
15
|
+
onTimeout?: (error: Error) => Error;
|
|
16
|
+
},
|
|
17
|
+
): Promise<T>;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Waits for the `expectation` callback to pass. `waitFor` runs the callback multiple times until timeout is reached, as specified by the `timeout` and `interval` options. The callback must throw an error when the expectation is not met. Returning any value, including a falsy one, is treated as meeting the expectation, and the callback result is returned to the caller.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
await waitFor(() => expect(mockFunction).toHaveBeenCalledWith());
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`waitFor` executes the `expectation` callback every `interval` (default: 50 ms) until `timeout` (default: 1000 ms) is reached. Execution stops as soon as the callback doesn't throw an error, and the callback's return value is returned to the caller. If timeout is reached, `waitFor` re-throws the final error thrown by `expectation`.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// ❌ `waitFor` will return immediately because callback does not throw
|
|
30
|
+
await waitFor(() => false);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`waitFor` is an async function so you need to `await` the result to pause test execution.
|
|
34
|
+
|
|
35
|
+
```jsx
|
|
36
|
+
// ❌ missing `await`: `waitFor` will just return Promise that will be rejected when the timeout is reached
|
|
37
|
+
waitFor(() => expect(1).toBe(2));
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> You can enforce awaiting `waitFor` by using the [await-async-utils](https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/await-async-utils.md) rule from [eslint-plugin-testing-library](https://github.com/testing-library/eslint-plugin-testing-library).
|
|
42
|
+
|
|
43
|
+
Since `waitFor` runs the `expectation` callback multiple times, [avoid performing side effects](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#performing-side-effects-in-waitfor) in `waitFor`.
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
await waitFor(async () => {
|
|
47
|
+
// ❌ button will be pressed on each waitFor iteration
|
|
48
|
+
await fireEvent.press(screen.getByText('press me'));
|
|
49
|
+
expect(mockOnPress).toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> [!NOTE]
|
|
54
|
+
> Avoiding side effects in `expectation` callback can be partially enforced with the [`no-wait-for-side-effects` rule](https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-wait-for-side-effects.md).
|
|
55
|
+
|
|
56
|
+
Use a [single assertion per `waitFor`](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback) for consistency and faster failing tests. For multiple assertions, use separate `waitFor` calls. Often you won't need to wrap the second assertion in `waitFor` since the first one waits for the asynchronous change.
|
|
57
|
+
|
|
58
|
+
`waitFor` checks whether Jest fake timers are enabled and adapts its behavior in such case. The following snippet is a simplified version of how it behaves when fake timers are enabled:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
let fakeTimeRemaining = timeout;
|
|
62
|
+
let lastError;
|
|
63
|
+
|
|
64
|
+
while (fakeTimeRemaining > 0) {
|
|
65
|
+
fakeTimeRemaining = fakeTimeRemaining - interval;
|
|
66
|
+
jest.advanceTimersByTime(interval);
|
|
67
|
+
try {
|
|
68
|
+
// resolve
|
|
69
|
+
return expectation();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
lastError = error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// reject
|
|
76
|
+
throw lastError;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
In the following example we test that a function is called after 10 seconds using fake timers. With fake timers, the test doesn't depend on real time passing, making it faster and more reliable. We don't need to advance fake timers through Jest's API because `waitFor` handles this.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// in component
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
someFunction();
|
|
85
|
+
}, 10000);
|
|
86
|
+
|
|
87
|
+
// in test
|
|
88
|
+
jest.useFakeTimers();
|
|
89
|
+
|
|
90
|
+
await waitFor(
|
|
91
|
+
() => {
|
|
92
|
+
expect(someFunction).toHaveBeenCalledWith();
|
|
93
|
+
},
|
|
94
|
+
{ timeout: 10000 },
|
|
95
|
+
);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> [!NOTE]
|
|
99
|
+
> If you receive warnings related to `act()` function consult our [Understanding Act](../guides/understanding-act.md) function document.
|
|
100
|
+
|
|
101
|
+
### Options
|
|
102
|
+
|
|
103
|
+
- `timeout`: How long to wait for, in ms. Defaults to 1000 ms (configured by `asyncUtilTimeout` option).
|
|
104
|
+
- `interval`: How often to check, in ms. Defaults to 50 ms.
|
|
105
|
+
- `onTimeout`: Callback to transform the error before it's thrown. Useful for debugging, e.g., `onTimeout: () => { screen.debug(); }`.
|
|
106
|
+
|
|
107
|
+
## `waitForElementToBeRemoved`
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
function waitForElementToBeRemoved<T>(
|
|
111
|
+
expectation: () => T,
|
|
112
|
+
options?: {
|
|
113
|
+
timeout?: number;
|
|
114
|
+
interval?: number;
|
|
115
|
+
onTimeout?: (error: Error) => Error;
|
|
116
|
+
},
|
|
117
|
+
): Promise<T>;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Waits for non-deterministic periods of time until queried element is removed or times out. `waitForElementToBeRemoved` periodically calls `expectation` every `interval` milliseconds to determine whether the element has been removed or not.
|
|
121
|
+
|
|
122
|
+
```jsx
|
|
123
|
+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';
|
|
124
|
+
|
|
125
|
+
test('waiting for an Banana to be removed', async () => {
|
|
126
|
+
await render(<Banana />);
|
|
127
|
+
|
|
128
|
+
await waitForElementToBeRemoved(() => screen.getByText('Banana ready'));
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This method expects that the element is initially present in the render tree and then is removed from it. If the element is not present when you call this method it throws an error.
|
|
133
|
+
|
|
134
|
+
You can use any of `getBy`, `getAllBy`, `queryBy` and `queryAllBy` queries for `expectation` parameter.
|
|
135
|
+
|
|
136
|
+
> [!NOTE]
|
|
137
|
+
> If you receive warnings related to `act()` function consult our [Understanding Act](../guides/understanding-act.md) function document.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## `configure`
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
type Config = {
|
|
7
|
+
/** Default timeout, in ms, for `waitFor` and `findBy*` queries. */
|
|
8
|
+
asyncUtilTimeout: number;
|
|
9
|
+
|
|
10
|
+
/** Default value for `includeHiddenElements` query option. */
|
|
11
|
+
defaultIncludeHiddenElements: boolean;
|
|
12
|
+
|
|
13
|
+
/** Default options for `debug` helper. */
|
|
14
|
+
defaultDebugOptions?: Partial<DebugOptions>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ConfigAliasOptions = {
|
|
18
|
+
/** RTL-compatibility alias for `defaultIncludeHiddenElements`. */
|
|
19
|
+
defaultHidden: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function configure(options: Partial<Config & ConfigAliasOptions>) {}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### `asyncUtilTimeout` option
|
|
26
|
+
|
|
27
|
+
Default timeout, in ms, for async helper functions (`waitFor`, `waitForElementToBeRemoved`) and `findBy*` queries. Defaults to 1000 ms.
|
|
28
|
+
|
|
29
|
+
### `defaultIncludeHiddenElements` option
|
|
30
|
+
|
|
31
|
+
Default value for [includeHiddenElements](./queries.md#includehiddenelements-option) query option for all queries. The default value is set to `false`, so all queries will not match [elements hidden from accessibility](#ishiddenfromaccessibility). This is because the users of the app would not be able to see such elements.
|
|
32
|
+
|
|
33
|
+
This option is also available as `defaultHidden` alias for compatibility with [React Testing Library](https://testing-library.com/docs/dom-testing-library/api-configuration/#defaulthidden).
|
|
34
|
+
|
|
35
|
+
### `defaultDebugOptions` option
|
|
36
|
+
|
|
37
|
+
Default [debug options](#debug) to be used when calling `debug()`. These default options will be overridden by the ones you specify directly when calling `debug()`.
|
|
38
|
+
|
|
39
|
+
## `resetToDefaults()`
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
function resetToDefaults() {}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Environment variables
|
|
46
|
+
|
|
47
|
+
### `RNTL_SKIP_AUTO_CLEANUP`
|
|
48
|
+
|
|
49
|
+
Set to `true` to disable automatic `cleanup()` after each test. It works the same as importing `react-native-testing-library/dont-cleanup-after-each` or using `react-native-testing-library/pure`.
|
|
50
|
+
|
|
51
|
+
```shell
|
|
52
|
+
$ RNTL_SKIP_AUTO_CLEANUP=true jest
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS`
|
|
56
|
+
|
|
57
|
+
Set to `true` to disable auto-detection of fake timers. This might be useful in rare cases when you want to use non-Jest fake timers. See [issue #886](https://github.com/callstack/react-native-testing-library/issues/886) for more details.
|
|
58
|
+
|
|
59
|
+
```shell
|
|
60
|
+
$ RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true jest
|
|
61
|
+
```
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Fire Event API
|
|
2
|
+
|
|
3
|
+
## `fireEvent`
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> For common events like `press` or `type`, use the [User Event API](./user-event.md). It simulates events more realistically by emitting a sequence of events with proper event objects that mimic React Native runtime behavior.
|
|
7
|
+
>
|
|
8
|
+
> Use Fire Event for cases not supported by User Event and for triggering event handlers on composite components.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
function fireEvent(instance: TestInstance, eventName: string, ...data: unknown[]): Promise<unknown>;
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The `fireEvent` API triggers event handlers on both host and composite components. It traverses the component tree bottom-up from the passed element to find an enabled event handler named `onXxx` where `xxx` is the event name.
|
|
15
|
+
|
|
16
|
+
Unlike User Event, this API does not automatically pass event object to event handler, this is responsibility of the user to construct such object.
|
|
17
|
+
|
|
18
|
+
The base `fireEvent(instance, eventName, ...data)` API can pass multiple custom arguments to the handler. Convenience helpers such as `fireEvent.press` and `fireEvent.scroll` are different: they create a default event object and accept one optional object to merge into it.
|
|
19
|
+
|
|
20
|
+
This function uses async `act` internally to execute all pending React updates during event handling.
|
|
21
|
+
|
|
22
|
+
```jsx
|
|
23
|
+
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
24
|
+
|
|
25
|
+
test('fire changeText event', async () => {
|
|
26
|
+
const onEventMock = jest.fn();
|
|
27
|
+
await render(
|
|
28
|
+
// MyComponent renders TextInput which has a placeholder 'Enter details'
|
|
29
|
+
// and with `onChangeText` bound to handleChangeText
|
|
30
|
+
<MyComponent handleChangeText={onEventMock} />,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
await fireEvent(screen.getByPlaceholderText('change'), 'onChangeText', 'ab');
|
|
34
|
+
expect(onEventMock).toHaveBeenCalledWith('ab');
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> [!NOTE]
|
|
39
|
+
> `fireEvent` performs checks that should prevent events firing on disabled elements.
|
|
40
|
+
|
|
41
|
+
An example using `fireEvent` with native events that aren't already aliased by the `fireEvent` api.
|
|
42
|
+
|
|
43
|
+
```jsx
|
|
44
|
+
import { TextInput, View } from 'react-native';
|
|
45
|
+
import { fireEvent, render, screen } from '@testing-library/react-native';
|
|
46
|
+
|
|
47
|
+
const onBlurMock = jest.fn();
|
|
48
|
+
|
|
49
|
+
await render(
|
|
50
|
+
<View>
|
|
51
|
+
<TextInput placeholder="my placeholder" onBlur={onBlurMock} />
|
|
52
|
+
</View>,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// you can omit the `on` prefix
|
|
56
|
+
await fireEvent(screen.getByPlaceholderText('my placeholder'), 'blur');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
FireEvent exposes convenience methods for common events like: `press`, `changeText`, `scroll`.
|
|
60
|
+
|
|
61
|
+
### `fireEvent.press`
|
|
62
|
+
|
|
63
|
+
> [!NOTE]
|
|
64
|
+
> Use the User Event [`press()`](./user-event.md#press) helper instead. It simulates press interactions more realistically, including pressable support.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
fireEvent.press: (
|
|
68
|
+
instance: TestInstance,
|
|
69
|
+
eventProps?: Record<string, unknown>,
|
|
70
|
+
) => Promise<void>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Builds a press event object, merges `eventProps` into it, and invokes the `press` handler on the element or nearest eligible parent.
|
|
74
|
+
|
|
75
|
+
```jsx
|
|
76
|
+
import { View, Text, TouchableOpacity } from 'react-native';
|
|
77
|
+
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
78
|
+
|
|
79
|
+
const onPressMock = jest.fn();
|
|
80
|
+
const eventData = {
|
|
81
|
+
nativeEvent: {
|
|
82
|
+
pageX: 20,
|
|
83
|
+
pageY: 30,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
await render(
|
|
88
|
+
<View>
|
|
89
|
+
<TouchableOpacity onPress={onPressMock}>
|
|
90
|
+
<Text>Press me</Text>
|
|
91
|
+
</TouchableOpacity>
|
|
92
|
+
</View>,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
await fireEvent.press(screen.getByText('Press me'), eventData);
|
|
96
|
+
expect(onPressMock).toHaveBeenCalledWith(expect.objectContaining(eventData));
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `fireEvent.changeText`
|
|
100
|
+
|
|
101
|
+
> [!NOTE]
|
|
102
|
+
> Use the User Event [`type()`](./user-event.md#type) helper instead. It simulates text change interactions more realistically, including key-by-key typing, element focus, and other editing events.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
fireEvent.changeText: (
|
|
106
|
+
instance: TestInstance,
|
|
107
|
+
text: string,
|
|
108
|
+
) => Promise<void>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Invokes `changeText` event handler on the element or parent element in the tree.
|
|
112
|
+
|
|
113
|
+
```jsx
|
|
114
|
+
import { View, TextInput } from 'react-native';
|
|
115
|
+
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
116
|
+
|
|
117
|
+
const onChangeTextMock = jest.fn();
|
|
118
|
+
const CHANGE_TEXT = 'content';
|
|
119
|
+
|
|
120
|
+
await render(
|
|
121
|
+
<View>
|
|
122
|
+
<TextInput placeholder="Enter data" onChangeText={onChangeTextMock} />
|
|
123
|
+
</View>,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
await fireEvent.changeText(screen.getByPlaceholderText('Enter data'), CHANGE_TEXT);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `fireEvent.scroll`
|
|
130
|
+
|
|
131
|
+
> [!NOTE]
|
|
132
|
+
> Prefer [`user.scrollTo`](./user-event.md#scrollto) over `fireEvent.scroll` for `ScrollView`, `FlatList`, and `SectionList` components. User Event simulates events more realistically based on React Native runtime behavior.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
fireEvent.scroll: (
|
|
136
|
+
instance: TestInstance,
|
|
137
|
+
eventProps?: Record<string, unknown>,
|
|
138
|
+
) => Promise<void>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Builds a scroll event object, merges `eventProps` into it, and invokes the `scroll` handler on the element or nearest eligible parent.
|
|
142
|
+
|
|
143
|
+
#### On a `ScrollView`
|
|
144
|
+
|
|
145
|
+
```jsx
|
|
146
|
+
import { ScrollView, Text } from 'react-native';
|
|
147
|
+
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
148
|
+
|
|
149
|
+
const onScrollMock = jest.fn();
|
|
150
|
+
const eventData = {
|
|
151
|
+
nativeEvent: {
|
|
152
|
+
contentOffset: {
|
|
153
|
+
y: 200,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
await render(
|
|
159
|
+
<ScrollView testID="scroll-view" onScroll={onScrollMock}>
|
|
160
|
+
<Text>Content</Text>
|
|
161
|
+
</ScrollView>,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
await fireEvent.scroll(screen.getByTestId('scroll-view'), eventData);
|
|
165
|
+
```
|