diff --git a/examples/basic/jest-setup.js b/examples/basic/jest-setup.js
index 708f3fc4a..af6ed0772 100644
--- a/examples/basic/jest-setup.js
+++ b/examples/basic/jest-setup.js
@@ -1,13 +1,7 @@
/* eslint-disable no-undef, import/no-extraneous-dependencies */
-import { configure } from '@testing-library/react-native';
// Import Jest Native matchers
import '@testing-library/jest-native/extend-expect';
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
-
-// Enable excluding hidden elements from the queries by default
-configure({
- defaultIncludeHiddenElements: false,
-});
diff --git a/examples/react-navigation/README.md b/examples/react-navigation/README.md
index 89c573e18..3e39f9641 100644
--- a/examples/react-navigation/README.md
+++ b/examples/react-navigation/README.md
@@ -8,8 +8,6 @@ There are two types of recommeded tests:
1. Tests operating on navigator level - these use `renderNavigator` helper to render a navigator component used in the app. It is useful when you want to test a scenario that includes multiple screens.
2. Tests operating on single screen level - these use regular `render` helper but require refactoring screen components into `Screen` and `ScreenContent` components. Where `Screen` receives React Navigation props and/or uses hooks like `useNavigation` while `ScreenContent` does not have a direct relation to React Navigation API but gets props from `Screen` and calls relevant callbacks to trigger navigation.
-> Note that this example applies `includeHiddenElements: false` by default, so all queries will ignore elements on the hidden screens, e.g. inactive tabs or screens present in stack navigators. This option is enabled in `jest-setup.js` file, using `defaultIncludeHiddenElements: false` option to `configure` function.
-
## Non-recommended tests
There also exists another popular type of screen level tests, where users mock React Navigation objects like `navigation`, `route` and/or hooks like `useNavigation`, etc. We don't recommend this way of testing. **Mocking internal parts of the libraries is effectively testing implementation details, which goes against the Testing Library's [Guiding Principles](https://testing-library.com/docs/guiding-principles/)**.
diff --git a/examples/react-navigation/jest-setup.js b/examples/react-navigation/jest-setup.js
index e8edcee19..914a6bbc5 100644
--- a/examples/react-navigation/jest-setup.js
+++ b/examples/react-navigation/jest-setup.js
@@ -1,5 +1,4 @@
/* eslint-disable no-undef, import/no-extraneous-dependencies */
-import { configure } from '@testing-library/react-native';
// Import Jest Native matchers
import '@testing-library/jest-native/extend-expect';
@@ -10,8 +9,3 @@ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
// Setup Reanimated mocking for Drawer navigation
global.ReanimatedDataMock = { now: () => Date.now() };
require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests();
-
-// Enable excluding hidden elements from the queries by default
-configure({
- defaultIncludeHiddenElements: false,
-});
diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts
index f8f5c9f42..e1798a84c 100644
--- a/src/__tests__/config.test.ts
+++ b/src/__tests__/config.test.ts
@@ -7,7 +7,7 @@ import {
test('getConfig() returns existing configuration', () => {
expect(getConfig().asyncUtilTimeout).toEqual(1000);
- expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
+ expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
});
test('configure() overrides existing config values', () => {
@@ -16,21 +16,21 @@ test('configure() overrides existing config values', () => {
expect(getConfig()).toEqual({
asyncUtilTimeout: 5000,
defaultDebugOptions: { message: 'debug message' },
- defaultIncludeHiddenElements: true,
+ defaultIncludeHiddenElements: false,
});
});
test('resetToDefaults() resets config to defaults', () => {
configure({
asyncUtilTimeout: 5000,
- defaultIncludeHiddenElements: false,
+ defaultIncludeHiddenElements: true,
});
expect(getConfig().asyncUtilTimeout).toEqual(5000);
- expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
+ expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
resetToDefaults();
expect(getConfig().asyncUtilTimeout).toEqual(1000);
- expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
+ expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
});
test('resetToDefaults() resets internal config to defaults', () => {
@@ -44,8 +44,8 @@ test('resetToDefaults() resets internal config to defaults', () => {
});
test('configure handles alias option defaultHidden', () => {
- expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
-
- configure({ defaultHidden: false });
expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
+
+ configure({ defaultHidden: true });
+ expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
});
diff --git a/src/config.ts b/src/config.ts
index 6b215b872..3528391e3 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -32,7 +32,7 @@ export type InternalConfig = Config & {
const defaultConfig: InternalConfig = {
asyncUtilTimeout: 1000,
- defaultIncludeHiddenElements: true,
+ defaultIncludeHiddenElements: false,
};
let config = { ...defaultConfig };
diff --git a/src/helpers/__tests__/accessiblity.test.tsx b/src/helpers/__tests__/accessiblity.test.tsx
index eeee51610..d47ebd6a2 100644
--- a/src/helpers/__tests__/accessiblity.test.tsx
+++ b/src/helpers/__tests__/accessiblity.test.tsx
@@ -14,19 +14,25 @@ describe('isHiddenFromAccessibility', () => {
test('returns false for accessible elements', () => {
expect(
isHiddenFromAccessibility(
- render().getByTestId('subject')
+ render().getByTestId('subject', {
+ includeHiddenElements: true,
+ })
)
).toBe(false);
expect(
isHiddenFromAccessibility(
- render(Hello).getByTestId('subject')
+ render(Hello).getByTestId('subject', {
+ includeHiddenElements: true,
+ })
)
).toBe(false);
expect(
isHiddenFromAccessibility(
- render().getByTestId('subject')
+ render().getByTestId('subject', {
+ includeHiddenElements: true,
+ })
)
).toBe(false);
});
@@ -37,7 +43,13 @@ describe('isHiddenFromAccessibility', () => {
test('detects elements with accessibilityElementsHidden prop', () => {
const view = render();
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects nested elements with accessibilityElementsHidden prop', () => {
@@ -46,7 +58,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects deeply nested elements with accessibilityElementsHidden prop', () => {
@@ -59,14 +77,26 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects elements with importantForAccessibility="no-hide-descendants" prop', () => {
const view = render(
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects nested elements with importantForAccessibility="no-hide-descendants" prop', () => {
@@ -75,12 +105,24 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects elements with display=none', () => {
const view = render();
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects nested elements with display=none', () => {
@@ -89,7 +131,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects deeply nested elements with display=none', () => {
@@ -102,7 +150,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects elements with display=none with complex style', () => {
@@ -116,12 +170,24 @@ describe('isHiddenFromAccessibility', () => {
]}
/>
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('is not trigged by opacity = 0', () => {
const view = render();
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(false);
});
test('detects siblings of element with accessibilityViewIsModal prop', () => {
@@ -131,7 +197,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('detects deeply nested siblings of element with accessibilityViewIsModal prop', () => {
@@ -145,7 +217,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(true);
});
test('is not triggered for element with accessibilityViewIsModal prop', () => {
@@ -154,7 +232,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(false);
});
test('is not triggered for child of element with accessibilityViewIsModal prop', () => {
@@ -165,7 +249,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(false);
});
test('is not triggered for descendent of element with accessibilityViewIsModal prop', () => {
@@ -180,7 +270,13 @@ describe('isHiddenFromAccessibility', () => {
);
- expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false);
+ expect(
+ isHiddenFromAccessibility(
+ view.getByTestId('subject', {
+ includeHiddenElements: true,
+ })
+ )
+ ).toBe(false);
});
test('has isInaccessible alias', () => {
diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts
index 86a99ae70..91a9bc246 100644
--- a/src/helpers/accessiblity.ts
+++ b/src/helpers/accessiblity.ts
@@ -59,6 +59,11 @@ export function isHiddenFromAccessibility(
export const isInaccessible = isHiddenFromAccessibility;
function isSubtreeInaccessible(element: ReactTestInstance): boolean {
+ // Null props can happen for React.Fragments
+ if (element.props == null) {
+ return false;
+ }
+
// iOS: accessibilityElementsHidden
// See: https://reactnative.dev/docs/accessibility#accessibilityelementshidden-ios
if (element.props.accessibilityElementsHidden) {
diff --git a/src/queries/__tests__/a11yState.test.tsx b/src/queries/__tests__/a11yState.test.tsx
index bb50ae2fa..41ed4b7c0 100644
--- a/src/queries/__tests__/a11yState.test.tsx
+++ b/src/queries/__tests__/a11yState.test.tsx
@@ -244,11 +244,11 @@ test('byA11yState queries support hidden option', () => {
);
- expect(getByA11yState({ expanded: false })).toBeTruthy();
expect(
getByA11yState({ expanded: false }, { includeHiddenElements: true })
).toBeTruthy();
+ expect(queryByA11yState({ expanded: false })).toBeFalsy();
expect(
queryByA11yState({ expanded: false }, { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/a11yValue.test.tsx b/src/queries/__tests__/a11yValue.test.tsx
index c713fc38d..a3f6fb74e 100644
--- a/src/queries/__tests__/a11yValue.test.tsx
+++ b/src/queries/__tests__/a11yValue.test.tsx
@@ -99,11 +99,11 @@ test('byA11yValue queries support hidden option', () => {
);
- expect(getByA11yValue({ max: 10 })).toBeTruthy();
expect(
getByA11yValue({ max: 10 }, { includeHiddenElements: true })
).toBeTruthy();
+ expect(queryByA11yValue({ max: 10 })).toBeFalsy();
expect(
queryByA11yValue({ max: 10 }, { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/displayValue.test.tsx b/src/queries/__tests__/displayValue.test.tsx
index db8a7147b..c1c7f3713 100644
--- a/src/queries/__tests__/displayValue.test.tsx
+++ b/src/queries/__tests__/displayValue.test.tsx
@@ -111,11 +111,11 @@ test('byDisplayValue queries support hidden option', () => {
);
- expect(getByDisplayValue('hidden')).toBeTruthy();
expect(
getByDisplayValue('hidden', { includeHiddenElements: true })
).toBeTruthy();
+ expect(queryByDisplayValue('hidden')).toBeFalsy();
expect(
queryByDisplayValue('hidden', { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/hintText.test.tsx b/src/queries/__tests__/hintText.test.tsx
index 4adc5bb33..d97427e3c 100644
--- a/src/queries/__tests__/hintText.test.tsx
+++ b/src/queries/__tests__/hintText.test.tsx
@@ -114,9 +114,9 @@ test('byHintText queries support hidden option', () => {
);
- expect(getByHintText('hidden')).toBeTruthy();
expect(getByHintText('hidden', { includeHiddenElements: true })).toBeTruthy();
+ expect(queryByHintText('hidden')).toBeFalsy();
expect(
queryByHintText('hidden', { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/labelText.test.tsx b/src/queries/__tests__/labelText.test.tsx
index 07aa3fccc..5da2fe57b 100644
--- a/src/queries/__tests__/labelText.test.tsx
+++ b/src/queries/__tests__/labelText.test.tsx
@@ -151,11 +151,11 @@ test('byLabelText queries support hidden option', () => {
);
- expect(getByLabelText('hidden')).toBeTruthy();
expect(
getByLabelText('hidden', { includeHiddenElements: true })
).toBeTruthy();
+ expect(queryByLabelText('hidden')).toBeFalsy();
expect(
queryByLabelText('hidden', { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/placeholderText.test.tsx b/src/queries/__tests__/placeholderText.test.tsx
index b62d058f3..276a301bb 100644
--- a/src/queries/__tests__/placeholderText.test.tsx
+++ b/src/queries/__tests__/placeholderText.test.tsx
@@ -64,11 +64,11 @@ test('byPlaceholderText queries support hidden option', () => {
);
- expect(getByPlaceholderText('hidden')).toBeTruthy();
expect(
getByPlaceholderText('hidden', { includeHiddenElements: true })
).toBeTruthy();
+ expect(queryByPlaceholderText('hidden')).toBeFalsy();
expect(
queryByPlaceholderText('hidden', { includeHiddenElements: false })
).toBeFalsy();
diff --git a/src/queries/__tests__/role.test.tsx b/src/queries/__tests__/role.test.tsx
index 81d6884f2..f953061d6 100644
--- a/src/queries/__tests__/role.test.tsx
+++ b/src/queries/__tests__/role.test.tsx
@@ -723,9 +723,9 @@ test('byRole queries support hidden option', () => {
);
- expect(getByRole('button')).toBeTruthy();
expect(getByRole('button', { includeHiddenElements: true })).toBeTruthy();
+ expect(queryByRole('button')).toBeFalsy();
expect(queryByRole('button', { includeHiddenElements: false })).toBeFalsy();
expect(() =>
getByRole('button', { includeHiddenElements: false })
diff --git a/src/queries/__tests__/testId.test.tsx b/src/queries/__tests__/testId.test.tsx
index f064d411c..ae615ea86 100644
--- a/src/queries/__tests__/testId.test.tsx
+++ b/src/queries/__tests__/testId.test.tsx
@@ -140,9 +140,9 @@ test('byTestId queries support hidden option', () => {
);
- expect(getByTestId('hidden')).toBeTruthy();
expect(getByTestId('hidden', { includeHiddenElements: true })).toBeTruthy();
+ expect(queryByTestId('hidden')).toBeFalsy();
expect(queryByTestId('hidden', { includeHiddenElements: false })).toBeFalsy();
expect(() =>
getByTestId('hidden', { includeHiddenElements: false })
diff --git a/src/queries/__tests__/text.test.tsx b/src/queries/__tests__/text.test.tsx
index 06b02fa43..0f42daff7 100644
--- a/src/queries/__tests__/text.test.tsx
+++ b/src/queries/__tests__/text.test.tsx
@@ -492,9 +492,9 @@ test('byText support hidden option', () => {
Hidden from accessibility
);
- expect(getByText(/hidden/i)).toBeTruthy();
expect(getByText(/hidden/i, { includeHiddenElements: true })).toBeTruthy();
+ expect(queryByText(/hidden/i)).toBeFalsy();
expect(queryByText(/hidden/i, { includeHiddenElements: false })).toBeFalsy();
expect(() =>
getByText(/hidden/i, { includeHiddenElements: false })
diff --git a/website/docs/API.md b/website/docs/API.md
index 3f395fa23..3f0b8dc9b 100644
--- a/website/docs/API.md
+++ b/website/docs/API.md
@@ -884,9 +884,7 @@ Default timeout, in ms, for async helper functions (`waitFor`, `waitForElementTo
#### `defaultIncludeHiddenElements` option
-Default value for [includeHiddenElements](Queries.md#includehiddenelements-option) query option for all queries. Defaults to `true`, which means that queries will match [elements hidden from accessibility](#ishiddenfromaccessibility) by default.
-
-Currently this option is set to `true` which means that queries will also match hidden elements. This is done to avoid breaking changes. However, we plan to change the default behavior to exclude hidden elements in the next major release.
+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.
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).
diff --git a/website/docs/MigrationV12.md b/website/docs/MigrationV12.md
index c8119ae51..6e2c24042 100644
--- a/website/docs/MigrationV12.md
+++ b/website/docs/MigrationV12.md
@@ -7,7 +7,12 @@ React Native Testing Library 12 introduces a handful of breaking changes compare
# Breaking changes
-## 1. `*ByRole` queries now return only accessibility elements
+## 1. All queries exclude elements hidden from accessibility by default
+Elements that are hidden from accessiblity, e.g. elements on non-active screen when using React Navigation, now will not be matched by default by all queries. This is the effect of switching the default value for global config option `defaultIncludeHiddenElements`(api#defaultincludehiddenelements-option) to `false`.
+
+Previous behaviour of matching hidden elements can be enabled on query level using [includeHiddenElements](api-queries#includehiddenelements-option) query options or globally using `defaultIncludeHiddenElements`(api#defaultincludehiddenelements-option) configuration option.
+
+## 2. `*ByRole` queries now return only accessibility elements
`*ByRole` queries now return only accessibility elements, either explicitly marked with `accessible` prop or implicit ones where this status is derrived from component type itself (e.g `Text`, `TextInput`, `Switch`, but not `View`).
You may need to adjust relevant components under test to make sure they pass `isAccessibilityElement` check.
@@ -38,14 +43,14 @@ While following elements will not match:
Button
```
-## 2. `*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return host elements
+## 3. `*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return host elements
`*ByText`, `*ByDisplayValue`, `*ByPlaceholderText` queries now return [host elements](testing-env#host-and-composite-components), which is consistent with other queries.
While potentially breaking, this should not cause issues in tests if you are using recommended queries and Jest Matchers from Jest Native package.
Problematic cases may include: directly checking some prop values (without using Jest Native matchers), referencing other nodes using `parent` or `children` props, examining `type` property of `ReactTestInstance`, etc.
-## 3. `container` API has been renamed to `UNSAFE_root`.
+## 4. `container` API has been renamed to `UNSAFE_root`.
Historically `container` was supposed to mimic the [RTL's container](https://testing-library.com/docs/react-testing-library/api/#container). However it turned out not so relevant in RNTL's environment, where we actually used it to return React Test Renderer's root instance.
diff --git a/website/docs/Queries.md b/website/docs/Queries.md
index e40adb46b..bce270c58 100644
--- a/website/docs/Queries.md
+++ b/website/docs/Queries.md
@@ -415,7 +415,7 @@ const element2 = screen.getByA11yValue({ text: /25/ });
### `includeHiddenElements` option
-All queries have the `includeHiddenElements` option which affects whether [elements hidden from accessibility](./API.md#ishiddenfromaccessibility) are matched by the query.
+All queries have the `includeHiddenElements` option which affects whether [elements hidden from accessibility](./API.md#ishiddenfromaccessibility) are matched by the query. By default queries will not match hidden elements, because the users of the app would not be able to see such elements.
You can configure the default value with the [`configure` function](API.md#configure).