diff --git a/src/__tests__/__snapshots__/render.breaking.test.tsx.snap b/src/__tests__/__snapshots__/render.breaking.test.tsx.snap
deleted file mode 100644
index 018ae7d63..000000000
--- a/src/__tests__/__snapshots__/render.breaking.test.tsx.snap
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`toJSON renders host output 1`] = `
-
-
- press me
-
-
-`;
diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts
index 03de47adc..f8f5c9f42 100644
--- a/src/__tests__/config.test.ts
+++ b/src/__tests__/config.test.ts
@@ -6,7 +6,6 @@ import {
} from '../config';
test('getConfig() returns existing configuration', () => {
- expect(getConfig().useBreakingChanges).toEqual(false);
expect(getConfig().asyncUtilTimeout).toEqual(1000);
expect(getConfig().defaultIncludeHiddenElements).toEqual(true);
});
@@ -18,7 +17,6 @@ test('configure() overrides existing config values', () => {
asyncUtilTimeout: 5000,
defaultDebugOptions: { message: 'debug message' },
defaultIncludeHiddenElements: true,
- useBreakingChanges: false,
});
});
@@ -37,12 +35,12 @@ test('resetToDefaults() resets config to defaults', () => {
test('resetToDefaults() resets internal config to defaults', () => {
configureInternal({
- useBreakingChanges: true,
+ hostComponentNames: { text: 'A', textInput: 'A' },
});
- expect(getConfig().useBreakingChanges).toEqual(true);
+ expect(getConfig().hostComponentNames).toEqual({ text: 'A', textInput: 'A' });
resetToDefaults();
- expect(getConfig().useBreakingChanges).toEqual(false);
+ expect(getConfig().hostComponentNames).toBe(undefined);
});
test('configure handles alias option defaultHidden', () => {
diff --git a/src/__tests__/render.breaking.test.tsx b/src/__tests__/render.breaking.test.tsx
deleted file mode 100644
index ac6492664..000000000
--- a/src/__tests__/render.breaking.test.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */
-
-/* eslint-disable no-console */
-import * as React from 'react';
-import { View, Text, TextInput, Pressable } from 'react-native';
-import { render, screen, fireEvent, RenderAPI } from '..';
-import { configureInternal } from '../config';
-
-beforeEach(() => {
- configureInternal({ useBreakingChanges: true });
-});
-
-const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
-const PLACEHOLDER_CHEF = 'Who inspected freshness?';
-const INPUT_FRESHNESS = 'Custom Freshie';
-const INPUT_CHEF = 'I inspected freshie';
-const DEFAULT_INPUT_CHEF = 'What did you inspect?';
-const DEFAULT_INPUT_CUSTOMER = 'What banana?';
-
-class MyButton extends React.Component {
- render() {
- return (
-
- {this.props.children}
-
- );
- }
-}
-
-class Banana extends React.Component {
- state = {
- fresh: false,
- };
-
- componentDidUpdate() {
- if (this.props.onUpdate) {
- this.props.onUpdate();
- }
- }
-
- componentWillUnmount() {
- if (this.props.onUnmount) {
- this.props.onUnmount();
- }
- }
-
- changeFresh = () => {
- this.setState((state) => ({
- fresh: !state.fresh,
- }));
- };
-
- render() {
- const test = 0;
- return (
-
- Is the banana fresh?
-
- {this.state.fresh ? 'fresh' : 'not fresh'}
-
-
-
-
-
-
- Change freshness!
-
- First Text
- Second Text
- {test}
-
- );
- }
-}
-
-test('UNSAFE_getAllByType, UNSAFE_queryAllByType', () => {
- const { UNSAFE_getAllByType, UNSAFE_queryAllByType } = render();
- const [text, status, button] = UNSAFE_getAllByType(Text);
- const InExistent = () => null;
-
- expect(text.props.children).toBe('Is the banana fresh?');
- expect(status.props.children).toBe('not fresh');
- expect(button.props.children).toBe('Change freshness!');
- expect(() => UNSAFE_getAllByType(InExistent)).toThrow('No instances found');
-
- expect(UNSAFE_queryAllByType(Text)[1]).toBe(status);
- expect(UNSAFE_queryAllByType(InExistent)).toHaveLength(0);
-});
-
-test('UNSAFE_getByProps, UNSAFE_queryByProps', () => {
- const { UNSAFE_getByProps, UNSAFE_queryByProps } = render();
- const primaryType = UNSAFE_getByProps({ type: 'primary' });
-
- expect(primaryType.props.children).toBe('Change freshness!');
- expect(() => UNSAFE_getByProps({ type: 'inexistent' })).toThrow(
- 'No instances found'
- );
-
- expect(UNSAFE_queryByProps({ type: 'primary' })).toBe(primaryType);
- expect(UNSAFE_queryByProps({ type: 'inexistent' })).toBeNull();
-});
-
-test('UNSAFE_getAllByProp, UNSAFE_queryAllByProps', () => {
- const { UNSAFE_getAllByProps, UNSAFE_queryAllByProps } = render();
- const primaryTypes = UNSAFE_getAllByProps({ type: 'primary' });
-
- expect(primaryTypes).toHaveLength(1);
- expect(() => UNSAFE_getAllByProps({ type: 'inexistent' })).toThrow(
- 'No instances found'
- );
-
- expect(UNSAFE_queryAllByProps({ type: 'primary' })).toEqual(primaryTypes);
- expect(UNSAFE_queryAllByProps({ type: 'inexistent' })).toHaveLength(0);
-});
-
-test('update', () => {
- const fn = jest.fn();
- const { getByText, update, rerender } = render();
-
- fireEvent.press(getByText('Change freshness!'));
-
- update();
- rerender();
-
- expect(fn).toHaveBeenCalledTimes(3);
-});
-
-test('unmount', () => {
- const fn = jest.fn();
- const { unmount } = render();
- unmount();
- expect(fn).toHaveBeenCalled();
-});
-
-test('unmount should handle cleanup functions', () => {
- const cleanup = jest.fn();
- const Component = () => {
- React.useEffect(() => cleanup);
- return null;
- };
-
- const { unmount } = render();
-
- unmount();
-
- expect(cleanup).toHaveBeenCalledTimes(1);
-});
-
-test('toJSON renders host output', () => {
- const { toJSON } = render(press me);
- expect(toJSON()).toMatchSnapshot();
-});
-
-test('renders options.wrapper around node', () => {
- type WrapperComponentProps = { children: React.ReactNode };
- const WrapperComponent = ({ children }: WrapperComponentProps) => (
- {children}
- );
-
- const { toJSON, getByTestId } = render(, {
- wrapper: WrapperComponent,
- });
-
- expect(getByTestId('wrapper')).toBeTruthy();
- expect(toJSON()).toMatchInlineSnapshot(`
-
-
-
- `);
-});
-
-test('renders options.wrapper around updated node', () => {
- type WrapperComponentProps = { children: React.ReactNode };
- const WrapperComponent = ({ children }: WrapperComponentProps) => (
- {children}
- );
-
- const { toJSON, getByTestId, rerender } = render(, {
- wrapper: WrapperComponent,
- });
-
- rerender(
-
- );
-
- expect(getByTestId('wrapper')).toBeTruthy();
- expect(toJSON()).toMatchInlineSnapshot(`
-
-
-
- `);
-});
-
-test('returns host root', () => {
- const { root } = render();
-
- expect(root).toBeDefined();
- expect(root.type).toBe('View');
- expect(root.props.testID).toBe('inner');
-});
-
-test('returns composite UNSAFE_root', () => {
- const { UNSAFE_root } = render();
-
- expect(UNSAFE_root).toBeDefined();
- expect(UNSAFE_root.type).toBe(View);
- expect(UNSAFE_root.props.testID).toBe('inner');
-});
-
-test('container displays deprecation', () => {
- const view = render();
-
- expect(() => view.container).toThrowErrorMatchingInlineSnapshot(`
- "'container' property has been renamed to 'UNSAFE_root'.
-
- Consider using 'root' property which returns root host element."
- `);
- expect(() => screen.container).toThrowErrorMatchingInlineSnapshot(`
- "'container' property has been renamed to 'UNSAFE_root'.
-
- Consider using 'root' property which returns root host element."
- `);
-});
-
-test('RenderAPI type', () => {
- render() as RenderAPI;
- expect(true).toBeTruthy();
-});
diff --git a/src/__tests__/render.test.tsx b/src/__tests__/render.test.tsx
index 145fc6248..fd2fe4c05 100644
--- a/src/__tests__/render.test.tsx
+++ b/src/__tests__/render.test.tsx
@@ -1,13 +1,7 @@
/* eslint-disable no-console */
import * as React from 'react';
-import { View, Text, TextInput, Pressable, SafeAreaView } from 'react-native';
-import { render, fireEvent, RenderAPI } from '..';
-
-type ConsoleLogMock = jest.Mock;
-
-beforeEach(() => {
- jest.spyOn(console, 'warn').mockImplementation(() => {});
-});
+import { View, Text, TextInput, Pressable } from 'react-native';
+import { render, screen, fireEvent, RenderAPI } from '..';
const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
const PLACEHOLDER_CHEF = 'Who inspected freshness?';
@@ -156,7 +150,6 @@ test('unmount should handle cleanup functions', () => {
test('toJSON renders host output', () => {
const { toJSON } = render(press me);
-
expect(toJSON()).toMatchSnapshot();
});
@@ -226,38 +219,19 @@ test('returns composite UNSAFE_root', () => {
expect(UNSAFE_root.props.testID).toBe('inner');
});
-test('returns container', () => {
- const { container } = render();
+test('container displays deprecation', () => {
+ const view = render();
- const mockCalls = (console.warn as any as ConsoleLogMock).mock.calls;
- expect(mockCalls[0][0]).toMatchInlineSnapshot(`
- "'container' property is deprecated and has been renamed to 'UNSAFE_root'.
+ expect(() => view.container).toThrowErrorMatchingInlineSnapshot(`
+ "'container' property has been renamed to 'UNSAFE_root'.
Consider using 'root' property which returns root host element."
`);
+ expect(() => screen.container).toThrowErrorMatchingInlineSnapshot(`
+ "'container' property has been renamed to 'UNSAFE_root'.
- expect(container).toBeDefined();
- // `View` composite component is returned. This behavior will break if we
- // start returning only host components.
- expect(container.type).toBe(View);
- expect(container.props.testID).toBe('inner');
-});
-
-test('returns wrapper component as container', () => {
- type WrapperComponentProps = { children: React.ReactNode };
- const WrapperComponent = ({ children }: WrapperComponentProps) => (
- {children}
- );
-
- const { container } = render(, {
- wrapper: WrapperComponent,
- });
-
- expect(container).toBeDefined();
- // `WrapperComponent` composite component is returned with no testID passed to
- // it. This behavior will break if we start returning only host components.
- expect(container.type).toBe(WrapperComponent);
- expect(container.props.testID).not.toBeDefined();
+ Consider using 'root' property which returns root host element."
+ `);
});
test('RenderAPI type', () => {
diff --git a/src/config.ts b/src/config.ts
index 732206e38..6b215b872 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -26,15 +26,11 @@ export type HostComponentNames = {
};
export type InternalConfig = Config & {
- /** Whether to use breaking changes intended for next major version release. */
- useBreakingChanges: boolean;
-
/** Names for key React Native host components. */
hostComponentNames?: HostComponentNames;
};
const defaultConfig: InternalConfig = {
- useBreakingChanges: false,
asyncUtilTimeout: 1000,
defaultIncludeHiddenElements: true,
};
diff --git a/src/helpers/__tests__/getTextContent.test.tsx b/src/helpers/__tests__/getTextContent.test.tsx
index c7e4bb243..e2f937c7d 100644
--- a/src/helpers/__tests__/getTextContent.test.tsx
+++ b/src/helpers/__tests__/getTextContent.test.tsx
@@ -5,7 +5,7 @@ import { getTextContent } from '../getTextContent';
test('getTextContent with simple content', () => {
const view = render(Hello world);
- expect(getTextContent(view.container)).toBe('Hello world');
+ expect(getTextContent(view.root)).toBe('Hello world');
});
test('getTextContent with null element', () => {
@@ -18,7 +18,7 @@ test('getTextContent with single nested content', () => {
Hello world
);
- expect(getTextContent(view.container)).toBe('Hello world');
+ expect(getTextContent(view.root)).toBe('Hello world');
});
test('getTextContent with multiple nested content', () => {
@@ -27,7 +27,7 @@ test('getTextContent with multiple nested content', () => {
Hello world
);
- expect(getTextContent(view.container)).toBe('Hello world');
+ expect(getTextContent(view.root)).toBe('Hello world');
});
test('getTextContent with multiple number content', () => {
@@ -36,7 +36,7 @@ test('getTextContent with multiple number content', () => {
Hello world {100}
);
- expect(getTextContent(view.container)).toBe('Hello world 100');
+ expect(getTextContent(view.root)).toBe('Hello world 100');
});
test('getTextContent with multiple boolean content', () => {
@@ -45,5 +45,5 @@ test('getTextContent with multiple boolean content', () => {
Hello{false} {true}world
);
- expect(getTextContent(view.container)).toBe('Hello world');
+ expect(getTextContent(view.root)).toBe('Hello world');
});
diff --git a/src/queries/__tests__/displayValue.breaking.test.tsx b/src/queries/__tests__/displayValue.breaking.test.tsx
deleted file mode 100644
index 01949d375..000000000
--- a/src/queries/__tests__/displayValue.breaking.test.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */
-
-import * as React from 'react';
-import { View, TextInput } from 'react-native';
-
-import { render } from '../..';
-import { configureInternal } from '../../config';
-
-const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
-const PLACEHOLDER_CHEF = 'Who inspected freshness?';
-const INPUT_FRESHNESS = 'Custom Freshie';
-const INPUT_CHEF = 'I inspected freshie';
-const DEFAULT_INPUT_CHEF = 'What did you inspect?';
-const DEFAULT_INPUT_CUSTOMER = 'What banana?';
-
-beforeEach(() => configureInternal({ useBreakingChanges: true }));
-
-const Banana = () => (
-
-
-
-
-
-
-);
-
-test('getByDisplayValue, queryByDisplayValue', () => {
- const { getByDisplayValue, queryByDisplayValue } = render();
- const input = getByDisplayValue(/custom/i);
-
- expect(input.props.value).toBe(INPUT_FRESHNESS);
-
- const sameInput = getByDisplayValue(INPUT_FRESHNESS);
-
- expect(sameInput.props.value).toBe(INPUT_FRESHNESS);
- expect(() => getByDisplayValue('no value')).toThrow(
- 'Unable to find an element with displayValue: no value'
- );
-
- expect(queryByDisplayValue(/custom/i)).toBe(input);
- expect(queryByDisplayValue('no value')).toBeNull();
- expect(() => queryByDisplayValue(/fresh/i)).toThrow(
- 'Found multiple elements with display value: /fresh/i'
- );
-});
-
-test('getByDisplayValue, queryByDisplayValue get element by default value only when value is undefined', () => {
- const { getByDisplayValue, queryByDisplayValue } = render();
- expect(() =>
- getByDisplayValue(DEFAULT_INPUT_CHEF)
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with displayValue: What did you inspect?"`
- );
- expect(queryByDisplayValue(DEFAULT_INPUT_CHEF)).toBeNull();
-
- expect(() => getByDisplayValue('hello')).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with displayValue: hello"`
- );
- expect(queryByDisplayValue('hello')).toBeNull();
-
- expect(getByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy();
- expect(queryByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy();
-});
-
-test('getAllByDisplayValue, queryAllByDisplayValue', () => {
- const { getAllByDisplayValue, queryAllByDisplayValue } = render();
- const inputs = getAllByDisplayValue(/fresh/i);
-
- expect(inputs).toHaveLength(2);
- expect(() => getAllByDisplayValue('no value')).toThrow(
- 'Unable to find an element with displayValue: no value'
- );
-
- expect(queryAllByDisplayValue(/fresh/i)).toEqual(inputs);
- expect(queryAllByDisplayValue('no value')).toHaveLength(0);
-});
-
-test('findBy queries work asynchronously', async () => {
- const options = { timeout: 10 }; // Short timeout so that this test runs quickly
- const { rerender, findByDisplayValue, findAllByDisplayValue } = render(
-
- );
-
- await expect(
- findByDisplayValue('Display Value', {}, options)
- ).rejects.toBeTruthy();
- await expect(
- findAllByDisplayValue('Display Value', {}, options)
- ).rejects.toBeTruthy();
-
- setTimeout(
- () =>
- rerender(
-
-
-
- ),
- 20
- );
-
- await expect(findByDisplayValue('Display Value')).resolves.toBeTruthy();
- await expect(findAllByDisplayValue('Display Value')).resolves.toHaveLength(1);
-}, 20000);
-
-test('byDisplayValue queries support hidden option', () => {
- const { getByDisplayValue, queryByDisplayValue } = render(
-
- );
-
- expect(getByDisplayValue('hidden')).toBeTruthy();
- expect(
- getByDisplayValue('hidden', { includeHiddenElements: true })
- ).toBeTruthy();
-
- expect(
- queryByDisplayValue('hidden', { includeHiddenElements: false })
- ).toBeFalsy();
- expect(() =>
- getByDisplayValue('hidden', { includeHiddenElements: false })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with displayValue: hidden"`
- );
-});
-
-test('byDisplayValue should return host component', () => {
- const { getByDisplayValue } = render();
-
- expect(getByDisplayValue('value').type).toBe('TextInput');
-});
diff --git a/src/queries/__tests__/displayValue.test.tsx b/src/queries/__tests__/displayValue.test.tsx
index e8f351c2b..db8a7147b 100644
--- a/src/queries/__tests__/displayValue.test.tsx
+++ b/src/queries/__tests__/displayValue.test.tsx
@@ -50,10 +50,16 @@ test('getByDisplayValue, queryByDisplayValue', () => {
test('getByDisplayValue, queryByDisplayValue get element by default value only when value is undefined', () => {
const { getByDisplayValue, queryByDisplayValue } = render();
- expect(() => getByDisplayValue(DEFAULT_INPUT_CHEF)).toThrow();
+ expect(() =>
+ getByDisplayValue(DEFAULT_INPUT_CHEF)
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"Unable to find an element with displayValue: What did you inspect?"`
+ );
expect(queryByDisplayValue(DEFAULT_INPUT_CHEF)).toBeNull();
- expect(() => getByDisplayValue('hello')).toThrow();
+ expect(() => getByDisplayValue('hello')).toThrowErrorMatchingInlineSnapshot(
+ `"Unable to find an element with displayValue: hello"`
+ );
expect(queryByDisplayValue('hello')).toBeNull();
expect(getByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy();
@@ -120,8 +126,8 @@ test('byDisplayValue queries support hidden option', () => {
);
});
-test('byDisplayValue should return composite TextInput', () => {
+test('byDisplayValue should return host component', () => {
const { getByDisplayValue } = render();
- expect(getByDisplayValue('value').type).toBe(TextInput);
+ expect(getByDisplayValue('value').type).toBe('TextInput');
});
diff --git a/src/queries/__tests__/placeholderText.breaking.test.tsx b/src/queries/__tests__/placeholderText.breaking.test.tsx
deleted file mode 100644
index ab2c82264..000000000
--- a/src/queries/__tests__/placeholderText.breaking.test.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */
-
-import * as React from 'react';
-import { View, TextInput } from 'react-native';
-import { render } from '../..';
-import { configureInternal } from '../../config';
-
-const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
-const PLACEHOLDER_CHEF = 'Who inspected freshness?';
-const INPUT_FRESHNESS = 'Custom Freshie';
-const INPUT_CHEF = 'I inspected freshie';
-const DEFAULT_INPUT_CHEF = 'What did you inspect?';
-
-beforeEach(() => {
- configureInternal({ useBreakingChanges: true });
-});
-
-const Banana = () => (
-
-
-
-
-);
-
-test('getByPlaceholderText, queryByPlaceholderText', () => {
- const { getByPlaceholderText, queryByPlaceholderText } = render();
- const input = getByPlaceholderText(/custom/i);
-
- expect(input.props.placeholder).toBe(PLACEHOLDER_FRESHNESS);
-
- const sameInput = getByPlaceholderText(PLACEHOLDER_FRESHNESS);
-
- expect(sameInput.props.placeholder).toBe(PLACEHOLDER_FRESHNESS);
- expect(() => getByPlaceholderText('no placeholder')).toThrow(
- 'Unable to find an element with placeholder: no placeholder'
- );
-
- expect(queryByPlaceholderText(/add/i)).toBe(input);
- expect(queryByPlaceholderText('no placeholder')).toBeNull();
- expect(() => queryByPlaceholderText(/fresh/)).toThrow(
- 'Found multiple elements with placeholder: /fresh/ '
- );
-});
-
-test('getAllByPlaceholderText, queryAllByPlaceholderText', () => {
- const { getAllByPlaceholderText, queryAllByPlaceholderText } = render(
-
- );
- const inputs = getAllByPlaceholderText(/fresh/i);
-
- expect(inputs).toHaveLength(2);
- expect(() => getAllByPlaceholderText('no placeholder')).toThrow(
- 'Unable to find an element with placeholder: no placeholder'
- );
-
- expect(queryAllByPlaceholderText(/fresh/i)).toEqual(inputs);
- expect(queryAllByPlaceholderText('no placeholder')).toHaveLength(0);
-});
-
-test('byPlaceholderText queries support hidden option', () => {
- const { getByPlaceholderText, queryByPlaceholderText } = render(
-
- );
-
- expect(getByPlaceholderText('hidden')).toBeTruthy();
- expect(
- getByPlaceholderText('hidden', { includeHiddenElements: true })
- ).toBeTruthy();
-
- expect(
- queryByPlaceholderText('hidden', { includeHiddenElements: false })
- ).toBeFalsy();
- expect(() =>
- getByPlaceholderText('hidden', { includeHiddenElements: false })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with placeholder: hidden"`
- );
-});
-
-test('byPlaceHolderText should return host component', () => {
- const { getByPlaceholderText } = render(
-
- );
-
- expect(getByPlaceholderText('placeholder').type).toBe('TextInput');
-});
diff --git a/src/queries/__tests__/placeholderText.test.tsx b/src/queries/__tests__/placeholderText.test.tsx
index c16cf1f59..b62d058f3 100644
--- a/src/queries/__tests__/placeholderText.test.tsx
+++ b/src/queries/__tests__/placeholderText.test.tsx
@@ -79,10 +79,10 @@ test('byPlaceholderText queries support hidden option', () => {
);
});
-test('byPlaceHolderText should return composite component', () => {
+test('byPlaceHolderText should return host component', () => {
const { getByPlaceholderText } = render(
);
- expect(getByPlaceholderText('placeholder').type).toBe(TextInput);
+ expect(getByPlaceholderText('placeholder').type).toBe('TextInput');
});
diff --git a/src/queries/__tests__/role.breaking.test.tsx b/src/queries/__tests__/role.breaking.test.tsx
deleted file mode 100644
index dbca753ed..000000000
--- a/src/queries/__tests__/role.breaking.test.tsx
+++ /dev/null
@@ -1,769 +0,0 @@
-import * as React from 'react';
-import {
- TouchableOpacity,
- TouchableWithoutFeedback,
- Text,
- View,
- Pressable,
- Button as RNButton,
-} from 'react-native';
-import { render } from '../..';
-import { configureInternal } from '../../config';
-
-beforeEach(() => {
- configureInternal({ useBreakingChanges: true });
-});
-
-const TEXT_LABEL = 'cool text';
-
-// Little hack to make all the methods happy with type
-const NO_MATCHES_TEXT: any = 'not-existent-element';
-
-const getMultipleInstancesFoundMessage = (value: string) => {
- return `Found multiple elements with role: "${value}"`;
-};
-
-const getNoInstancesFoundMessage = (value: string) => {
- return `Unable to find an element with role: "${value}"`;
-};
-
-const Typography = ({ children, ...rest }: any) => {
- return {children};
-};
-
-const Button = ({ children }: { children: React.ReactNode }) => (
-
- {children}
-
-);
-
-const Section = () => (
- <>
- Title
-
- >
-);
-
-test('getByRole, queryByRole, findByRole', async () => {
- const { getByRole, queryByRole, findByRole } = render();
-
- expect(getByRole('button').props.accessibilityRole).toEqual('button');
- const button = queryByRole(/button/g);
- expect(button?.props.accessibilityRole).toEqual('button');
-
- expect(() => getByRole(NO_MATCHES_TEXT)).toThrow(
- getNoInstancesFoundMessage(NO_MATCHES_TEXT)
- );
-
- expect(queryByRole(NO_MATCHES_TEXT)).toBeNull();
-
- expect(() => getByRole('link')).toThrow(
- getMultipleInstancesFoundMessage('link')
- );
- expect(() => queryByRole('link')).toThrow(
- getMultipleInstancesFoundMessage('link')
- );
-
- const asyncButton = await findByRole('button');
- expect(asyncButton.props.accessibilityRole).toEqual('button');
- await expect(findByRole(NO_MATCHES_TEXT)).rejects.toThrow(
- getNoInstancesFoundMessage(NO_MATCHES_TEXT)
- );
- await expect(findByRole('link')).rejects.toThrow(
- getMultipleInstancesFoundMessage('link')
- );
-});
-
-test('getAllByRole, queryAllByRole, findAllByRole', async () => {
- const { getAllByRole, queryAllByRole, findAllByRole } = render();
-
- expect(getAllByRole('link')).toHaveLength(2);
- expect(queryAllByRole(/ink/g)).toHaveLength(2);
-
- expect(() => getAllByRole(NO_MATCHES_TEXT)).toThrow(
- getNoInstancesFoundMessage(NO_MATCHES_TEXT)
- );
- expect(queryAllByRole(NO_MATCHES_TEXT)).toEqual([]);
-
- await expect(findAllByRole('link')).resolves.toHaveLength(2);
- await expect(findAllByRole(NO_MATCHES_TEXT)).rejects.toThrow(
- getNoInstancesFoundMessage(NO_MATCHES_TEXT)
- );
-});
-
-describe('supports name option', () => {
- test('returns an element that has the corresponding role and a children with the name', () => {
- const { getByRole } = render(
-
- Save
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('button', { name: 'Save' }).props.testID).toBe(
- 'target-button'
- );
- });
-
- test('returns an element that has the corresponding role when several children include the name', () => {
- const { getByRole } = render(
-
- Save
- Save
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('button', { name: 'Save' }).props.testID).toBe(
- 'target-button'
- );
- });
-
- test('returns an element that has the corresponding role and a children with a matching accessibilityLabel', () => {
- const { getByRole } = render(
-
-
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('button', { name: 'Save' }).props.testID).toBe(
- 'target-button'
- );
- });
-
- test('returns an element that has the corresponding role and a matching accessibilityLabel', () => {
- const { getByRole } = render(
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('button', { name: 'Save' }).props.testID).toBe(
- 'target-button'
- );
- });
-
- test('returns an element when the direct child is text', () => {
- const { getByRole, getByTestId } = render(
-
- About
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('header', { name: 'About' })).toBe(
- getByTestId('target-header')
- );
- expect(getByRole('header', { name: 'About' }).props.testID).toBe(
- 'target-header'
- );
- });
-
- test('returns an element with nested Text as children', () => {
- const { getByRole, getByTestId } = render(
-
- About
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('header', { name: 'About' })).toBe(getByTestId('parent'));
- expect(getByRole('header', { name: 'About' }).props.testID).toBe('parent');
- });
-
- test('returns a header with an accessibilityLabel', () => {
- const { getByRole, getByTestId } = render(
-
- );
-
- // assert on the testId to be sure that the returned element is the one with the accessibilityRole
- expect(getByRole('header', { name: 'About' })).toBe(
- getByTestId('target-header')
- );
- expect(getByRole('header', { name: 'About' }).props.testID).toBe(
- 'target-header'
- );
- });
-});
-
-describe('supports accessibility states', () => {
- describe('disabled', () => {
- test('returns a disabled element when required', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { disabled: true })).toBeTruthy();
- expect(queryByRole('button', { disabled: false })).toBe(null);
- });
-
- test('returns the correct element when only one matches all the requirements', () => {
- const { getByRole } = render(
- <>
-
- Save
-
-
- Save
-
- >
- );
-
- expect(
- getByRole('button', { name: 'Save', disabled: true }).props.testID
- ).toBe('correct');
- });
-
- test('returns an implicitly enabled element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { disabled: false })).toBeTruthy();
- expect(queryByRole('button', { disabled: true })).toBe(null);
- });
-
- test('returns an explicitly enabled element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { disabled: false })).toBeTruthy();
- expect(queryByRole('button', { disabled: true })).toBe(null);
- });
-
- test('does not return disabled elements when querying for non disabled', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('button', { disabled: false })).toBe(null);
- });
-
- test('returns elements using the built-in disabled prop', () => {
- const { getByRole } = render(
- <>
-
- Pressable
-
-
-
-
- TouchableWithoutFeedback
-
-
- {}} title="RNButton" />
- >
- );
-
- expect(
- getByRole('button', { name: 'Pressable', disabled: true })
- ).toBeTruthy();
-
- expect(
- getByRole('button', {
- name: 'TouchableWithoutFeedback',
- disabled: true,
- })
- ).toBeTruthy();
-
- expect(
- getByRole('button', { name: 'RNButton', disabled: true })
- ).toBeTruthy();
- });
- });
-
- describe('selected', () => {
- test('returns a selected element when required', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('tab', { selected: true })).toBeTruthy();
- expect(queryByRole('tab', { selected: false })).toBe(null);
- });
-
- test('returns the correct element when only one matches all the requirements', () => {
- const { getByRole } = render(
- <>
-
- Save
-
-
- Save
-
- >
- );
-
- expect(
- getByRole('tab', { name: 'Save', selected: true }).props.testID
- ).toBe('correct');
- });
-
- test('returns an implicitly non selected element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('tab', { selected: false })).toBeTruthy();
- expect(queryByRole('tab', { selected: true })).toBe(null);
- });
-
- test('returns an explicitly non selected element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('tab', { selected: false })).toBeTruthy();
- expect(queryByRole('tab', { selected: true })).toBe(null);
- });
-
- test('does not return selected elements when querying for non selected', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('tab', { selected: false })).toBe(null);
- });
- });
-
- describe('checked', () => {
- test('returns a checked element when required', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('checkbox', { checked: true })).toBeTruthy();
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- expect(queryByRole('checkbox', { checked: 'mixed' })).toBe(null);
- });
-
- it('returns `mixed` checkboxes', () => {
- const { queryByRole, getByRole } = render(
-
- );
-
- expect(getByRole('checkbox', { checked: 'mixed' })).toBeTruthy();
- expect(queryByRole('checkbox', { checked: true })).toBe(null);
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- });
-
- it('does not return mixed checkboxes when querying for checked: true', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- });
-
- test('returns the correct element when only one matches all the requirements', () => {
- const { getByRole } = render(
- <>
-
- Save
-
-
- Save
-
- >
- );
-
- expect(
- getByRole('checkbox', { name: 'Save', checked: true }).props.testID
- ).toBe('correct');
- });
-
- test('does not return return as non checked an element with checked: undefined', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- });
-
- test('returns an explicitly non checked element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('checkbox', { checked: false })).toBeTruthy();
- expect(queryByRole('checkbox', { checked: true })).toBe(null);
- });
-
- test('does not return checked elements when querying for non checked', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- });
-
- test('does not return mixed elements when querying for non checked', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('checkbox', { checked: false })).toBe(null);
- });
- });
-
- describe('busy', () => {
- test('returns a busy element when required', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { busy: true })).toBeTruthy();
- expect(queryByRole('button', { busy: false })).toBe(null);
- });
-
- test('returns the correct element when only one matches all the requirements', () => {
- const { getByRole } = render(
- <>
-
- Save
-
-
- Save
-
- >
- );
-
- expect(
- getByRole('button', { name: 'Save', busy: true }).props.testID
- ).toBe('correct');
- });
-
- test('returns an implicitly non busy element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { busy: false })).toBeTruthy();
- expect(queryByRole('button', { busy: true })).toBe(null);
- });
-
- test('returns an explicitly non busy element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { busy: false })).toBeTruthy();
- expect(queryByRole('button', { busy: true })).toBe(null);
- });
-
- test('does not return busy elements when querying for non busy', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('button', { selected: false })).toBe(null);
- });
- });
-
- describe('expanded', () => {
- test('returns a expanded element when required', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { expanded: true })).toBeTruthy();
- expect(queryByRole('button', { expanded: false })).toBe(null);
- });
-
- test('returns the correct element when only one matches all the requirements', () => {
- const { getByRole } = render(
- <>
-
- Save
-
-
- Save
-
- >
- );
-
- expect(
- getByRole('button', { name: 'Save', expanded: true }).props.testID
- ).toBe('correct');
- });
-
- test('does not return return as non expanded an element with expanded: undefined', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('button', { expanded: false })).toBe(null);
- });
-
- test('returns an explicitly non expanded element', () => {
- const { getByRole, queryByRole } = render(
-
- );
-
- expect(getByRole('button', { expanded: false })).toBeTruthy();
- expect(queryByRole('button', { expanded: true })).toBe(null);
- });
-
- test('does not return expanded elements when querying for non expanded', () => {
- const { queryByRole } = render(
-
- );
-
- expect(queryByRole('button', { expanded: false })).toBe(null);
- });
- });
-
- test('ignores non queried accessibilityState', () => {
- const { getByRole, queryByRole } = render(
-
- Save
-
- );
-
- expect(
- getByRole('button', {
- name: 'Save',
- disabled: true,
- })
- ).toBeTruthy();
- expect(
- queryByRole('button', {
- name: 'Save',
- disabled: false,
- })
- ).toBe(null);
- });
-
- test('matches an element combining all the options', () => {
- const { getByRole } = render(
-
- Save
-
- );
-
- expect(
- getByRole('button', {
- name: 'Save',
- disabled: true,
- selected: true,
- checked: true,
- busy: true,
- expanded: true,
- })
- ).toBeTruthy();
- });
-});
-
-describe('error messages', () => {
- test('gives a descriptive error message when querying with a role', () => {
- const { getByRole } = render();
-
- expect(() => getByRole('button')).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button""`
- );
- });
-
- test('gives a descriptive error message when querying with a role and a name', () => {
- const { getByRole } = render();
-
- expect(() =>
- getByRole('button', { name: 'Save' })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button", name: "Save""`
- );
- });
-
- test('gives a descriptive error message when querying with a role, a name and accessibility state', () => {
- const { getByRole } = render();
-
- expect(() =>
- getByRole('button', { name: 'Save', disabled: true })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button", name: "Save", disabled state: true"`
- );
- });
-
- test('gives a descriptive error message when querying with a role, a name and several accessibility state', () => {
- const { getByRole } = render();
-
- expect(() =>
- getByRole('button', { name: 'Save', disabled: true, selected: true })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button", name: "Save", disabled state: true, selected state: true"`
- );
- });
-
- test('gives a descriptive error message when querying with a role and an accessibility state', () => {
- const { getByRole } = render();
-
- expect(() =>
- getByRole('button', { disabled: true })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button", disabled state: true"`
- );
- });
-
- test('gives a descriptive error message when querying with a role and an accessibility value', () => {
- const { getByRole } = render();
-
- expect(() =>
- getByRole('adjustable', { value: { min: 1 } })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "adjustable", min value: 1"`
- );
-
- expect(() =>
- getByRole('adjustable', {
- value: { min: 1, max: 2, now: 1, text: /hello/ },
- })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "adjustable", min value: 1, max value: 2, now value: 1, text value: /hello/"`
- );
- });
-});
-
-test('byRole queries support hidden option', () => {
- const { getByRole, queryByRole } = render(
-
- Hidden from accessibility
-
- );
-
- expect(getByRole('button')).toBeTruthy();
- expect(getByRole('button', { includeHiddenElements: true })).toBeTruthy();
-
- expect(queryByRole('button', { includeHiddenElements: false })).toBeFalsy();
- expect(() =>
- getByRole('button', { includeHiddenElements: false })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with role: "button""`
- );
-});
-
-describe('matches only accessible elements', () => {
- test('matches elements with accessible={true}', () => {
- const { queryByRole } = render(
-
- Action
-
- );
- expect(queryByRole('menu', { name: 'Action' })).toBeTruthy();
- });
-
- test('ignores elements with accessible={false}', () => {
- const { queryByRole } = render(
-
- Action
-
- );
- expect(queryByRole('button', { name: 'Action' })).toBeFalsy();
- });
-
- test('ignores elements with accessible={undefined} and that are implicitely not accessible', () => {
- const { queryByRole } = render(
-
- Action
-
- );
- expect(queryByRole('menu', { name: 'Action' })).toBeFalsy();
- });
-});
diff --git a/src/queries/__tests__/role.test.tsx b/src/queries/__tests__/role.test.tsx
index 1b377ede0..81d6884f2 100644
--- a/src/queries/__tests__/role.test.tsx
+++ b/src/queries/__tests__/role.test.tsx
@@ -734,11 +734,31 @@ test('byRole queries support hidden option', () => {
);
});
-test('does not take accessible prop into account', () => {
- const { getByRole } = render(
-
- Action
-
- );
- expect(getByRole('button', { name: 'Action' })).toBeTruthy();
+describe('matches only accessible elements', () => {
+ test('matches elements with accessible={true}', () => {
+ const { queryByRole } = render(
+
+ Action
+
+ );
+ expect(queryByRole('menu', { name: 'Action' })).toBeTruthy();
+ });
+
+ test('ignores elements with accessible={false}', () => {
+ const { queryByRole } = render(
+
+ Action
+
+ );
+ expect(queryByRole('button', { name: 'Action' })).toBeFalsy();
+ });
+
+ test('ignores elements with accessible={undefined} and that are implicitely not accessible', () => {
+ const { queryByRole } = render(
+
+ Action
+
+ );
+ expect(queryByRole('menu', { name: 'Action' })).toBeFalsy();
+ });
});
diff --git a/src/queries/__tests__/text.breaking.test.tsx b/src/queries/__tests__/text.breaking.test.tsx
deleted file mode 100644
index eb243edd1..000000000
--- a/src/queries/__tests__/text.breaking.test.tsx
+++ /dev/null
@@ -1,514 +0,0 @@
-import * as React from 'react';
-import {
- View,
- Text,
- TouchableOpacity,
- Image,
- Button,
- TextInput,
-} from 'react-native';
-import { render, getDefaultNormalizer, within } from '../..';
-import { configureInternal } from '../../config';
-
-beforeEach(() => {
- configureInternal({ useBreakingChanges: true });
-});
-
-test('byText matches simple text', () => {
- const { getByText } = render(Hello World);
- expect(getByText('Hello World').props.testID).toBe('text');
-});
-
-test('byText matches inner nested text', () => {
- const { getByText } = render(
-
- Hello World
-
- );
- expect(getByText('Hello World').props.testID).toBe('inner');
-});
-
-test('byText matches accross multiple texts', () => {
- const { getByText } = render(
-
- Hello World
-
- );
- expect(getByText('Hello World').props.testID).toBe('outer');
-});
-
-type MyButtonProps = {
- children: React.ReactNode;
- onPress: () => void;
-};
-const MyButton = ({ children, onPress }: MyButtonProps) => (
-
- {children}
-
-);
-
-const Banana = () => {
- const test = 0;
- return (
-
- Is the banana fresh?
- not fresh
-
- Change freshness!
- First Text
- Second Text
- {test}
-
- );
-};
-
-type ChildrenProps = { children: React.ReactNode };
-
-test('getByText, queryByText', () => {
- const { getByText, queryByText } = render();
- const button = getByText(/change/i);
-
- expect(button.props.children).toBe('Change freshness!');
-
- const sameButton = getByText('not fresh');
-
- expect(sameButton.props.children).toBe('not fresh');
- expect(() => getByText('InExistent')).toThrow(
- 'Unable to find an element with text: InExistent'
- );
-
- const zeroText = getByText('0');
-
- expect(queryByText(/change/i)).toBe(button);
- expect(queryByText('InExistent')).toBeNull();
- expect(() => queryByText(/fresh/)).toThrow(
- 'Found multiple elements with text: /fresh/'
- );
- expect(queryByText('0')).toBe(zeroText);
-});
-
-test('getByText, queryByText with children as Array', () => {
- type BananaCounterProps = { numBananas: number };
- const BananaCounter = ({ numBananas }: BananaCounterProps) => (
- There are {numBananas} bananas in the bunch
- );
-
- const BananaStore = () => (
-
-
-
-
-
- );
-
- const { getByText } = render();
-
- const threeBananaBunch = getByText('There are 3 bananas in the bunch');
- expect(threeBananaBunch.props.children).toEqual([
- 'There are ',
- 3,
- ' bananas in the bunch',
- ]);
-});
-
-test('getAllByText, queryAllByText', () => {
- const { getAllByText, queryAllByText } = render();
- const buttons = getAllByText(/fresh/i);
-
- expect(buttons).toHaveLength(3);
- expect(() => getAllByText('InExistent')).toThrow(
- 'Unable to find an element with text: InExistent'
- );
-
- expect(queryAllByText(/fresh/i)).toEqual(buttons);
- expect(queryAllByText('InExistent')).toHaveLength(0);
-});
-
-test('findByText queries work asynchronously', async () => {
- const options = { timeout: 10 }; // Short timeout so that this test runs quickly
- const { rerender, findByText, findAllByText } = render();
- await expect(findByText('Some Text', {}, options)).rejects.toBeTruthy();
- await expect(findAllByText('Some Text', {}, options)).rejects.toBeTruthy();
-
- setTimeout(
- () =>
- rerender(
-
- Some Text
-
- ),
- 20
- );
-
- await expect(findByText('Some Text')).resolves.toBeTruthy();
- await expect(findAllByText('Some Text')).resolves.toHaveLength(1);
-}, 20000);
-
-test('getByText works properly with custom text component', () => {
- function BoldText({ children }: ChildrenProps) {
- return {children};
- }
-
- expect(
- render(
-
- Hello
-
- ).getByText('Hello')
- ).toBeTruthy();
-});
-
-test('getByText works properly with custom text container', () => {
- function MyText({ children }: ChildrenProps) {
- return {children};
- }
- function BoldText({ children }: ChildrenProps) {
- return {children};
- }
-
- expect(
- render(
-
- Hello
-
- ).getByText('Hello')
- ).toBeTruthy();
-});
-
-test('queryByText nested in at start', () => {
- expect(
- render(
-
-
- Hello
-
- ).queryByText('Hello')
- ).toBeTruthy();
-});
-
-test('queryByText nested in at end', () => {
- expect(
- render(
-
- Hello
-
-
- ).queryByText('Hello')
- ).toBeTruthy();
-});
-
-test('queryByText nested in in middle', () => {
- expect(
- render(
-
- Hello
-
- World
-
- ).queryByText('HelloWorld')
- ).toBeTruthy();
-});
-
-test('queryByText not found', () => {
- expect(
- render(
-
- Hello
-
-
- ).queryByText('SomethingElse')
- ).toBeFalsy();
-});
-
-test('*ByText matches text across multiple nested Text', () => {
- const { getByText } = render(
-
- Hello{' '}
-
- World
- !{true}
-
-
- );
-
- expect(getByText('Hello World!')).toBeTruthy();
-});
-
-test('queryByText with nested Text components return the closest Text', () => {
- const NestedTexts = () => (
-
- My text
-
- );
-
- const { queryByText } = render();
- expect(queryByText('My text', { exact: false })?.props.nativeID).toBe('2');
-});
-
-test('queryByText with nested Text components each with text return the lowest one', () => {
- const NestedTexts = () => (
-
- bob
- My text
-
- );
-
- const { queryByText } = render();
-
- expect(queryByText('My text', { exact: false })?.props.nativeID).toBe('2');
-});
-
-test('queryByText nested deep in ', () => {
- const CustomText = ({ children }: ChildrenProps) => {
- return {children};
- };
-
- expect(
- render(
-
- Hello World!
-
- ).getByText('Hello World!')
- ).toBeTruthy();
-});
-
-test('queryByText with nested Text components: not-exact text match returns the most deeply nested common component', () => {
- const { queryByText: queryByTextFirstCase } = render(
-
- bob
- My
- text
-
- );
-
- const { queryByText: queryByTextSecondCase } = render(
-
- bob
- My text for test
-
- );
-
- expect(queryByTextFirstCase('My text')).toBe(null);
- expect(
- queryByTextSecondCase('My text', { exact: false })?.props.nativeID
- ).toBe('2');
-});
-
-test('queryAllByText does not match several times the same text', () => {
- const allMatched = render(
-
- Start
- This is a long text
-
- ).queryAllByText('long text', { exact: false });
- expect(allMatched.length).toBe(1);
- expect(allMatched[0].props.nativeID).toBe('2');
-});
-
-test('queryAllByText matches all the matching nodes', () => {
- const allMatched = render(
-
- Start
- This is a long text
- This is another long text
-
- ).queryAllByText('long text', { exact: false });
- expect(allMatched.length).toBe(2);
- expect(allMatched.map((node) => node.props.nativeID)).toEqual(['2', '3']);
-});
-
-describe('supports TextMatch options', () => {
- test('getByText, getAllByText', () => {
- const { getByText, getAllByText } = render(
-
- Text and details
-
-
- );
-
- expect(getByText('details', { exact: false })).toBeTruthy();
- expect(getAllByText('detail', { exact: false })).toHaveLength(2);
- });
-
- test('getByPlaceholderText, getAllByPlaceholderText', () => {
- const { getByPlaceholderText, getAllByPlaceholderText } = render(
-
-
-
-
- );
-
- expect(getByPlaceholderText('details', { exact: false })).toBeTruthy();
- expect(getAllByPlaceholderText('detail', { exact: false })).toHaveLength(2);
- });
-
- test('getByDisplayValue, getAllByDisplayValue', () => {
- const { getByDisplayValue, getAllByDisplayValue } = render(
-
-
-
-
- );
-
- expect(getByDisplayValue('details', { exact: false })).toBeTruthy();
- expect(getAllByDisplayValue('detail', { exact: false })).toHaveLength(2);
- });
-
- test('getByTestId, getAllByTestId', () => {
- const { getByTestId, getAllByTestId } = render(
-
-
-
-
- );
- expect(getByTestId('id', { exact: false })).toBeTruthy();
- expect(getAllByTestId('test', { exact: false })).toHaveLength(2);
- });
-
- test('with TextMatch option exact === false text search is NOT case sensitive', () => {
- const { getByText, getAllByText } = render(
-
- Text and details
-
-
- );
-
- expect(getByText('DeTaIlS', { exact: false })).toBeTruthy();
- expect(getAllByText('detail', { exact: false })).toHaveLength(2);
- });
-});
-
-describe('Supports normalization', () => {
- test('trims and collapses whitespace by default', () => {
- const { getByText } = render(
-
- {` Text and
-
- whitespace`}
-
- );
-
- expect(getByText('Text and whitespace')).toBeTruthy();
- });
-
- test('trim and collapseWhitespace is customizable by getDefaultNormalizer param', () => {
- const testTextWithWhitespace = ` Text and
-
- whitespace`;
- const { getByText } = render(
-
- {testTextWithWhitespace}
-
- );
-
- expect(
- getByText(testTextWithWhitespace, {
- normalizer: getDefaultNormalizer({
- trim: false,
- collapseWhitespace: false,
- }),
- })
- ).toBeTruthy();
- });
-
- test('normalizer function is customisable', () => {
- const testText = 'A TO REMOVE text';
- const normalizerFn = (textToNormalize: string) =>
- textToNormalize.replace('TO REMOVE ', '');
- const { getByText } = render(
-
- {testText}
-
- );
-
- expect(getByText('A text', { normalizer: normalizerFn })).toBeTruthy();
- });
-});
-
-test('getByText and queryByText work properly with text nested in React.Fragment', () => {
- const { getByText, queryByText } = render(
-
- <>Hello>
-
- );
- expect(getByText('Hello')).toBeTruthy();
- expect(queryByText('Hello')).not.toBeNull();
-});
-
-test('getByText and queryByText work properly with text partially nested in React.Fragment', () => {
- const { getByText, queryByText } = render(
-
- He<>llo>
-
- );
- expect(getByText('Hello')).toBeTruthy();
- expect(queryByText('Hello')).not.toBeNull();
-});
-
-test('getByText and queryByText work properly with multiple nested fragments', () => {
- const { getByText, queryByText } = render(
-
- He
- <>
- l<>l>o
- >
-
- );
- expect(getByText('Hello')).toBeTruthy();
- expect(queryByText('Hello')).not.toBeNull();
-});
-
-test('getByText and queryByText work with newlines', () => {
- const textWithNewLines = 'Line 1\nLine 2';
- const { getByText, queryByText } = render({textWithNewLines});
- expect(getByText(textWithNewLines)).toBeTruthy();
- expect(queryByText(textWithNewLines)).toBeTruthy();
-});
-
-test('getByText and queryByText work with tabs', () => {
- const textWithTabs = 'Line 1\tLine 2';
- const { getByText, queryByText } = render({textWithTabs});
- expect(getByText(textWithTabs)).toBeTruthy();
- expect(queryByText(textWithTabs)).toBeTruthy();
-});
-
-test('getByText searches for text within itself', () => {
- const { getByText } = render(Hello);
- const textNode = within(getByText('Hello'));
- expect(textNode.getByText('Hello')).toBeTruthy();
-});
-
-test('getByText searches for text within self host element', () => {
- const { getByTestId } = render(Hello);
- const textNode = within(getByTestId('subject'));
- expect(textNode.getByText('Hello')).toBeTruthy();
-});
-
-test('byText support hidden option', () => {
- const { getByText, queryByText } = render(
- Hidden from accessibility
- );
-
- expect(getByText(/hidden/i)).toBeTruthy();
- expect(getByText(/hidden/i, { includeHiddenElements: true })).toBeTruthy();
-
- expect(queryByText(/hidden/i, { includeHiddenElements: false })).toBeFalsy();
- expect(() =>
- getByText(/hidden/i, { includeHiddenElements: false })
- ).toThrowErrorMatchingInlineSnapshot(
- `"Unable to find an element with text: /hidden/i"`
- );
-});
-
-test('byText should return host component', () => {
- const { getByText } = render(hello);
- expect(getByText('hello').type).toBe('Text');
-});
diff --git a/src/queries/__tests__/text.test.tsx b/src/queries/__tests__/text.test.tsx
index 27f9eb82a..06b02fa43 100644
--- a/src/queries/__tests__/text.test.tsx
+++ b/src/queries/__tests__/text.test.tsx
@@ -503,7 +503,7 @@ test('byText support hidden option', () => {
);
});
-test('byText should return composite Text', () => {
+test('byText should return host component', () => {
const { getByText } = render(hello);
- expect(getByText('hello').type).toBe(Text);
+ expect(getByText('hello').type).toBe('Text');
});
diff --git a/src/queries/displayValue.ts b/src/queries/displayValue.ts
index 334f7d042..7ceef802f 100644
--- a/src/queries/displayValue.ts
+++ b/src/queries/displayValue.ts
@@ -1,9 +1,7 @@
import type { ReactTestInstance } from 'react-test-renderer';
-import { TextInput } from 'react-native';
import { filterNodeByType } from '../helpers/filterNodeByType';
import { findAll } from '../helpers/findAll';
import { matches, TextMatch, TextMatchOptions } from '../matches';
-import { getConfig } from '../config';
import { getHostComponentNames } from '../helpers/host-component-names';
import { makeQueries } from './makeQueries';
import type {
@@ -26,13 +24,9 @@ const getTextInputNodeByDisplayValue = (
const { exact, normalizer } = options;
const nodeValue =
node.props.value !== undefined ? node.props.value : node.props.defaultValue;
- const shouldReturnHostTextInput = getConfig().useBreakingChanges;
- const textInputType = shouldReturnHostTextInput
- ? getHostComponentNames().textInput
- : TextInput;
return (
- filterNodeByType(node, textInputType) &&
+ filterNodeByType(node, getHostComponentNames().textInput) &&
matches(value, nodeValue, normalizer, exact)
);
};
diff --git a/src/queries/placeholderText.ts b/src/queries/placeholderText.ts
index 660f9941b..613dc0e84 100644
--- a/src/queries/placeholderText.ts
+++ b/src/queries/placeholderText.ts
@@ -1,8 +1,6 @@
import type { ReactTestInstance } from 'react-test-renderer';
-import { TextInput } from 'react-native';
import { findAll } from '../helpers/findAll';
import { matches, TextMatch, TextMatchOptions } from '../matches';
-import { getConfig } from '../config';
import { filterNodeByType } from '../helpers/filterNodeByType';
import { getHostComponentNames } from '../helpers/host-component-names';
import { makeQueries } from './makeQueries';
@@ -24,13 +22,9 @@ const getTextInputNodeByPlaceholderText = (
options: TextMatchOptions = {}
) => {
const { exact, normalizer } = options;
- const shouldReturnHostTextInput = getConfig().useBreakingChanges;
- const textInputType = shouldReturnHostTextInput
- ? getHostComponentNames().textInput
- : TextInput;
return (
- filterNodeByType(node, textInputType) &&
+ filterNodeByType(node, getHostComponentNames().textInput) &&
matches(placeholder, node.props.placeholder, normalizer, exact)
);
};
diff --git a/src/queries/role.ts b/src/queries/role.ts
index 3356284a8..be1a1a2f0 100644
--- a/src/queries/role.ts
+++ b/src/queries/role.ts
@@ -14,7 +14,6 @@ import {
import { matchStringProp } from '../helpers/matchers/matchStringProp';
import type { TextMatch } from '../matches';
import { getQueriesForElement } from '../within';
-import { getConfig } from '../config';
import { makeQueries } from './makeQueries';
import type {
FindAllByQuery,
@@ -62,15 +61,12 @@ const queryAllByRole = (
instance: ReactTestInstance
): ((role: TextMatch, options?: ByRoleOptions) => Array) =>
function queryAllByRoleFn(role, options) {
- const shouldMatchOnlyAccessibilityElements = getConfig().useBreakingChanges;
-
return findAll(
instance,
(node) =>
// run the cheapest checks first, and early exit to avoid unneeded computations
typeof node.type === 'string' &&
- (!shouldMatchOnlyAccessibilityElements ||
- isAccessibilityElement(node)) &&
+ isAccessibilityElement(node) &&
matchStringProp(node.props.accessibilityRole, role) &&
matchAccessibleStateIfNeeded(node, options) &&
matchAccessibilityValueIfNeeded(node, options?.value) &&
diff --git a/src/queries/text.ts b/src/queries/text.ts
index 744d88a04..425f0153c 100644
--- a/src/queries/text.ts
+++ b/src/queries/text.ts
@@ -1,10 +1,4 @@
import type { ReactTestInstance } from 'react-test-renderer';
-import { Text } from 'react-native';
-import { getConfig } from '../config';
-import {
- getCompositeParentOfType,
- isHostElementForType,
-} from '../helpers/component-tree';
import { filterNodeByType } from '../helpers/filterNodeByType';
import { findAll } from '../helpers/findAll';
import { getHostComponentNames } from '../helpers/host-component-names';
@@ -27,29 +21,6 @@ const queryAllByText = (
instance: ReactTestInstance
): ((text: TextMatch, options?: ByTextOptions) => Array) =>
function queryAllByTextFn(text, options = {}) {
- const shouldReturnHostText = getConfig().useBreakingChanges;
-
- // Legacy version: return composite Text
- if (!shouldReturnHostText) {
- const baseInstance = isHostElementForType(instance, Text)
- ? getCompositeParentOfType(instance, Text)
- : instance;
-
- if (!baseInstance) {
- return [];
- }
-
- const results = findAll(
- baseInstance,
- (node) =>
- filterNodeByType(node, Text) && matchTextContent(node, text, options),
- { ...options, matchDeepestOnly: true }
- );
-
- return results;
- }
-
- // vNext version: returns host Text
return findAll(
instance,
(node) =>
diff --git a/src/render.tsx b/src/render.tsx
index 9fac09ee3..1320c85ae 100644
--- a/src/render.tsx
+++ b/src/render.tsx
@@ -111,16 +111,7 @@ function buildRenderResult(
return getHostChildren(instance)[0];
},
UNSAFE_root: instance,
- get container() {
- if (!getConfig().useBreakingChanges) {
- // eslint-disable-next-line no-console
- console.warn(
- "'container' property is deprecated and has been renamed to 'UNSAFE_root'.\n\n" +
- "Consider using 'root' property which returns root host element."
- );
- return instance;
- }
-
+ get container(): ReactTestInstance {
throw new Error(
"'container' property has been renamed to 'UNSAFE_root'.\n\n" +
"Consider using 'root' property which returns root host element."
diff --git a/typings/index.flow.js b/typings/index.flow.js
index 16736cf28..4ac2124d2 100644
--- a/typings/index.flow.js
+++ b/typings/index.flow.js
@@ -438,7 +438,6 @@ declare module '@testing-library/react-native' {
debug: Debug;
root: ReactTestInstance;
UNSAFE_root: ReactTestInstance;
- container: ReactTestInstance;
}
declare type RenderAPI = RenderResult;
diff --git a/website/docs/MigrationV12.md b/website/docs/MigrationV12.md
new file mode 100644
index 000000000..c8119ae51
--- /dev/null
+++ b/website/docs/MigrationV12.md
@@ -0,0 +1,60 @@
+---
+id: migration-v12
+title: Migration to 12.0
+---
+
+React Native Testing Library 12 introduces a handful of breaking changes compared to 11.x versions. We believe they were necessary to improve the experience using the library and help the users [fall into the pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/) when writing meaningful tests. You will find migration instructions for each and every change described below.
+
+# Breaking changes
+
+## 1. `*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.
+
+### Examples
+Let's assume we are using `getByRole("button")` query.
+
+Following elements will match:
+
+```ts
+// Explicit "accessible" prop for View
+
+
+// No need to "accessible" prop for Text, as it is implicitly accessible element.
+Button
+```
+
+While following elements will not match:
+
+```ts
+// Missing "accessible" prop for View
+
+
+// Explicit "accessible={false}" prop for View
+
+
+// Explicit "accessible={false}" for Text,which is implicitly accessible element
+Button
+```
+
+## 2. `*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`.
+
+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.
+
+RNTL v12 introduces `root` API as an alternative that returns a root **host** element. The difference between `root` and `UNSAFE_root` properties is that that `root` will always represents a host element, while `UNSAFE_root` will typically represent a composite element.
+
+# All changes
+
+* TODO
+
+# Full Changelog
+https://github.com/callstack/react-native-testing-library/compare/v11.5.2...v12.0.0
+
diff --git a/website/sidebars.js b/website/sidebars.js
index 9b1f176b5..1df46b141 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -9,6 +9,7 @@ module.exports = {
],
Advanced: ['testing-env', 'understanding-act'],
Migrations: [
+ 'migration-v12',
'migration-v11',
'migration-v9',
'migration-v7',