@testing-library/react-native 14.0.0-rc.0 → 14.0.0-rc.1
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/helpers/accessibility.js +24 -7
- package/build/helpers/accessibility.js.map +1 -1
- package/docs/README.md +31 -0
- package/docs/agents/architecture.md +21 -0
- package/docs/agents/build-and-validation.md +27 -0
- package/docs/agents/code-style.md +12 -0
- package/docs/agents/example-apps.md +56 -0
- package/docs/agents/git-workflow.md +13 -0
- package/docs/agents/testing.md +20 -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/package.json +9 -7
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({
|
|
@@ -1 +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","labelElementId","accessibilityLabelledBy","container","getContainerInstance","labelInstance","findAll","node","isTestInstance","nativeID","includeHiddenElements","length","getTextContent","explicitLabel","accessibilityLabel","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","join","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 labelElementId =\n instance.props['aria-labelledby'] ?? instance.props.accessibilityLabelledBy;\n if (labelElementId) {\n const container = getContainerInstance(instance);\n const labelInstance = findAll(\n container,\n (node) => isTestInstance(node) && node.props.nativeID === labelElementId,\n { includeHiddenElements: true },\n );\n if (labelInstance.length > 0) {\n return getTextContent(labelInstance[0]);\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\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":"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","ignoreList":[]}
|
|
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,21 @@
|
|
|
1
|
+
# Architecture And API Design
|
|
2
|
+
|
|
3
|
+
## Project overview
|
|
4
|
+
|
|
5
|
+
`@testing-library/react-native` provides utilities for testing React Native components in ways that resemble real usage and avoid implementation details.
|
|
6
|
+
|
|
7
|
+
- Tech stack: TypeScript, React Native, Jest
|
|
8
|
+
- Core principle: the closer tests are to real usage, the more confidence they provide
|
|
9
|
+
- Runtime model: the library simulates the React Native runtime on top of `test-renderer`
|
|
10
|
+
|
|
11
|
+
## API design principles
|
|
12
|
+
|
|
13
|
+
- Prefer a small public API surface.
|
|
14
|
+
- Expose underlying React and `react-reconciler` capabilities when Testing Libraries need them.
|
|
15
|
+
- Render host elements only.
|
|
16
|
+
- Provide escape hatches to fibers when needed.
|
|
17
|
+
|
|
18
|
+
## Key entry points
|
|
19
|
+
|
|
20
|
+
- `src/pure.ts`: side-effect-free core logic without auto-cleanup
|
|
21
|
+
- `src/index.ts`: main entry point that re-exports `pure` and adds auto-cleanup side effects
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Build, Validation, And Repo Layout
|
|
2
|
+
|
|
3
|
+
## Common commands
|
|
4
|
+
|
|
5
|
+
- Install dependencies: `yarn install`
|
|
6
|
+
- Run tests: `yarn test`
|
|
7
|
+
- Run tests in CI mode: `yarn test:ci`
|
|
8
|
+
- Type check: `yarn typecheck`
|
|
9
|
+
- Lint source files: `yarn lint`
|
|
10
|
+
- Check formatting: `yarn prettier`
|
|
11
|
+
- Validate the main package: `yarn validate`
|
|
12
|
+
- Build the package: `yarn build`
|
|
13
|
+
|
|
14
|
+
## Command notes
|
|
15
|
+
|
|
16
|
+
- `yarn lint` runs ESLint on `src`.
|
|
17
|
+
- `yarn validate` runs typecheck, tests, lint, and Prettier checks for the main package.
|
|
18
|
+
- `yarn build` cleans `build/`, transpiles source with Babel, and emits TypeScript declarations.
|
|
19
|
+
|
|
20
|
+
## Repo layout
|
|
21
|
+
|
|
22
|
+
- `src/`: source code
|
|
23
|
+
- `src/pure.ts`: core logic without side effects
|
|
24
|
+
- `src/index.ts`: main entry point with auto-cleanup side effects
|
|
25
|
+
- `examples/`: example React Native apps
|
|
26
|
+
- `website/`: documentation site
|
|
27
|
+
- `codemods/`: codemod implementations and tests
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# TypeScript And Code Style
|
|
2
|
+
|
|
3
|
+
## Linting and formatting
|
|
4
|
+
|
|
5
|
+
- ESLint uses `@callstack/eslint-config` together with `typescript-eslint`.
|
|
6
|
+
- Important enforced rules include `no-console` and consistent type imports.
|
|
7
|
+
- Prettier is the formatter.
|
|
8
|
+
- Formatting defaults include single quotes and trailing commas.
|
|
9
|
+
|
|
10
|
+
## Imports
|
|
11
|
+
|
|
12
|
+
- Keep imports sorted with `eslint-plugin-simple-import-sort`.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Example App Regeneration
|
|
2
|
+
|
|
3
|
+
## General workflow
|
|
4
|
+
|
|
5
|
+
- Regenerate Expo example apps in a temporary directory, then copy the fresh scaffold into place.
|
|
6
|
+
- Before replacing an example app, move the current app directory to `/tmp` so repo-specific files can be restored selectively.
|
|
7
|
+
- After copying a generated app into `examples/`, remove the generated `.git` directory and `node_modules`, then reinstall from inside the repo workspace.
|
|
8
|
+
|
|
9
|
+
## `examples/basic`
|
|
10
|
+
|
|
11
|
+
- Generate from the Expo blank TypeScript template:
|
|
12
|
+
- `yarn create expo-app /tmp/rntl-basic-fresh --template blank-typescript --yes`
|
|
13
|
+
- Restore these repo-specific files on top of the fresh scaffold:
|
|
14
|
+
- `App.tsx`
|
|
15
|
+
- `components/`
|
|
16
|
+
- `__tests__/`
|
|
17
|
+
- `theme.ts`
|
|
18
|
+
- `jest.config.js`
|
|
19
|
+
- `jest-setup.ts`
|
|
20
|
+
- `babel.config.js`
|
|
21
|
+
- `eslint.config.mjs`
|
|
22
|
+
- `README.md`
|
|
23
|
+
- `.expo-shared/assets.json` if it existed before
|
|
24
|
+
- Keep the fresh Expo entrypoint `index.ts`.
|
|
25
|
+
- Update `package.json` and `app.json` to match repo naming and scripts.
|
|
26
|
+
|
|
27
|
+
## `examples/cookbook`
|
|
28
|
+
|
|
29
|
+
- Generate from a router-enabled Expo scaffold:
|
|
30
|
+
- `yarn create expo-app /tmp/rntl-cookbook-fresh --example with-router --yes`
|
|
31
|
+
- Restore these repo-specific files on top of the fresh scaffold:
|
|
32
|
+
- `app/`
|
|
33
|
+
- tutorial test directories such as `basics-tutorial/` and `basics-tutorial-react-strict-dom/`
|
|
34
|
+
- `theme.ts`
|
|
35
|
+
- `jest.config.js`
|
|
36
|
+
- `jest-setup.ts`
|
|
37
|
+
- `babel.config.js`
|
|
38
|
+
- `.eslintrc`
|
|
39
|
+
- `.eslintignore`
|
|
40
|
+
- `README.md`
|
|
41
|
+
- custom assets not present in the scaffold, such as `assets/gradientRNBanner.png`
|
|
42
|
+
- `.expo-shared/assets.json` if it existed before
|
|
43
|
+
- Keep the fresh Expo Router entry setup.
|
|
44
|
+
- Reapply the cookbook-specific dependency set in `package.json` and `app.json`.
|
|
45
|
+
|
|
46
|
+
## Validation after regeneration
|
|
47
|
+
|
|
48
|
+
- Run these commands from inside the regenerated app directory:
|
|
49
|
+
- `yarn expo install --check`
|
|
50
|
+
- `yarn lint`
|
|
51
|
+
- `yarn typecheck`
|
|
52
|
+
- `yarn test --watchman=false`
|
|
53
|
+
|
|
54
|
+
## Lockfile guidance
|
|
55
|
+
|
|
56
|
+
- If the fresh scaffold causes dependency-resolution churn, restore the previous `yarn.lock` first and then run `yarn install` instead of re-resolving the full tree.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Git, Releases, And PR Workflow
|
|
2
|
+
|
|
3
|
+
## Commits and releases
|
|
4
|
+
|
|
5
|
+
- Use Conventional Commits such as `fix:`, `feat:`, and `chore:`.
|
|
6
|
+
- Releases are managed with `release-it`.
|
|
7
|
+
|
|
8
|
+
## PR draft workflow
|
|
9
|
+
|
|
10
|
+
- Maintain `PR.txt` at the repository root using the structure from `.github/pull_request_template.md`.
|
|
11
|
+
- Keep `PR.txt` aligned with the current branch diff relative to `origin/main`.
|
|
12
|
+
- Include tests actually run and any known validation gaps in `PR.txt`.
|
|
13
|
+
- Do not commit `PR.txt`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Testing Conventions
|
|
2
|
+
|
|
3
|
+
## Test stack
|
|
4
|
+
|
|
5
|
+
- Test framework: Jest with the `react-native` preset
|
|
6
|
+
- Test environment setup: `jest-setup.ts`
|
|
7
|
+
- Auto-cleanup is configured from `src/index.ts` unless explicitly skipped
|
|
8
|
+
|
|
9
|
+
## Test location and coverage
|
|
10
|
+
|
|
11
|
+
- Library tests are typically colocated under `src/**/__tests__`
|
|
12
|
+
- Coverage is collected from `src` and excludes test files
|
|
13
|
+
|
|
14
|
+
## Test organization
|
|
15
|
+
|
|
16
|
+
- Use `describe` blocks to group tests by theme.
|
|
17
|
+
- Do not put all tests into a single `describe`.
|
|
18
|
+
- Avoid nested `describe` blocks.
|
|
19
|
+
- If a `describe` would contain only one test, make that test top-level instead.
|
|
20
|
+
- Prefer `test` over `it`.
|
|
@@ -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
|
+
```
|