diff --git a/src/__tests__/a11yAPI.test.js b/src/__tests__/a11yAPI.test.js index dad5b1b83..a5cebd7e9 100644 --- a/src/__tests__/a11yAPI.test.js +++ b/src/__tests__/a11yAPI.test.js @@ -18,6 +18,8 @@ const Typography = ({ children, ...rest }: any) => { return {children}; }; +const waitForOptions = { timeout: 10 }; + class Button extends React.Component { render() { return ( @@ -62,74 +64,133 @@ function Section() { ); } -test('getByA11yLabel, queryByA11yLabel', () => { - const { getByA11yLabel, queryByA11yLabel } = render(
); +test('getByA11yLabel, queryByA11yLabel, findByA11yLabel', async () => { + const { getByA11yLabel, queryByA11yLabel, findByA11yLabel } = render( +
+ ); expect(getByA11yLabel(BUTTON_LABEL).props.accessibilityLabel).toEqual( BUTTON_LABEL ); const button = queryByA11yLabel(/button/g); expect(button && button.props.accessibilityLabel).toEqual(BUTTON_LABEL); + expect(() => getByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryByA11yLabel(NO_MATCHES_TEXT)).toBeNull(); expect(() => getByA11yLabel(TEXT_LABEL)).toThrow(FOUND_TWO_INSTANCES); expect(() => queryByA11yLabel(TEXT_LABEL)).toThrow(FOUND_TWO_INSTANCES); + + const asyncButton = await findByA11yLabel(BUTTON_LABEL); + expect(asyncButton.props.accessibilityLabel).toEqual(BUTTON_LABEL); + await expect( + findByA11yLabel(NO_MATCHES_TEXT, waitForOptions) + ).rejects.toThrow(NO_INSTANCES_FOUND); + + await expect(findByA11yLabel(TEXT_LABEL, waitForOptions)).rejects.toThrow( + FOUND_TWO_INSTANCES + ); }); -test('getAllByA11yLabel, queryAllByA11yLabel', () => { - const { getAllByA11yLabel, queryAllByA11yLabel } = render(
); +test('getAllByA11yLabel, queryAllByA11yLabel', async () => { + const { getAllByA11yLabel, queryAllByA11yLabel, findAllByA11yLabel } = render( +
+ ); expect(getAllByA11yLabel(TEXT_LABEL)).toHaveLength(2); expect(queryAllByA11yLabel(/cool/g)).toHaveLength(3); + expect(() => getAllByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryAllByA11yLabel(NO_MATCHES_TEXT)).toEqual([]); + + await expect(findAllByA11yLabel(TEXT_LABEL)).resolves.toHaveLength(2); + await expect(findAllByA11yLabel(NO_MATCHES_TEXT)).rejects.toThrow( + NO_INSTANCES_FOUND + ); }); -test('getByA11yHint, queryByA11yHint', () => { - const { getByA11yHint, queryByA11yHint } = render(
); +test('getByA11yHint, queryByA11yHint, findByA11yHint', async () => { + const { getByA11yHint, queryByA11yHint, findByA11yHint } = render( +
+ ); expect(getByA11yHint(BUTTON_HINT).props.accessibilityHint).toEqual( BUTTON_HINT ); const button = queryByA11yHint(/button/g); expect(button && button.props.accessibilityHint).toEqual(BUTTON_HINT); + expect(() => getByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryByA11yHint(NO_MATCHES_TEXT)).toBeNull(); expect(() => getByA11yHint(TEXT_HINT)).toThrow(FOUND_TWO_INSTANCES); expect(() => queryByA11yHint(TEXT_HINT)).toThrow(FOUND_TWO_INSTANCES); + + const asyncButton = await findByA11yHint(BUTTON_HINT); + expect(asyncButton.props.accessibilityHint).toEqual(BUTTON_HINT); + await expect(findByA11yHint(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow( + NO_INSTANCES_FOUND + ); + await expect(findByA11yHint(TEXT_HINT, waitForOptions)).rejects.toThrow( + FOUND_TWO_INSTANCES + ); }); -test('getAllByA11yHint, queryAllByA11yHint', () => { - const { getAllByA11yHint, queryAllByA11yHint } = render(
); +test('getAllByA11yHint, queryAllByA11yHint', async () => { + const { getAllByA11yHint, queryAllByA11yHint, findAllByA11yHint } = render( +
+ ); expect(getAllByA11yHint(TEXT_HINT)).toHaveLength(2); expect(queryAllByA11yHint(/static/g)).toHaveLength(2); + expect(() => getAllByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryAllByA11yHint(NO_MATCHES_TEXT)).toEqual([]); + + await expect(findAllByA11yHint(TEXT_HINT)).resolves.toHaveLength(2); + await expect(findAllByA11yHint(NO_MATCHES_TEXT)).rejects.toThrow( + NO_INSTANCES_FOUND + ); }); -test('getByA11yRole, queryByA11yRole', () => { - const { getByA11yRole, queryByA11yRole } = render(
); +test('getByA11yRole, queryByA11yRole, findByA11yRole', async () => { + const { getByA11yRole, queryByA11yRole, findByA11yRole } = render( +
+ ); expect(getByA11yRole('button').props.accessibilityRole).toEqual('button'); const button = queryByA11yRole(/button/g); expect(button && button.props.accessibilityRole).toEqual('button'); + expect(() => getByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryByA11yRole(NO_MATCHES_TEXT)).toBeNull(); expect(() => getByA11yRole('link')).toThrow(FOUND_TWO_INSTANCES); expect(() => queryByA11yRole('link')).toThrow(FOUND_TWO_INSTANCES); + + const asyncButton = await findByA11yRole('button'); + expect(asyncButton.props.accessibilityRole).toEqual('button'); + await expect(findByA11yRole(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow( + NO_INSTANCES_FOUND + ); + await expect(findByA11yRole('link')).rejects.toThrow(FOUND_TWO_INSTANCES); }); -test('getAllByA11yRole, queryAllByA11yRole', () => { - const { getAllByA11yRole, queryAllByA11yRole } = render(
); +test('getAllByA11yRole, queryAllByA11yRole, findAllByA11yRole', async () => { + const { getAllByA11yRole, queryAllByA11yRole, findAllByA11yRole } = render( +
+ ); expect(getAllByA11yRole('link')).toHaveLength(2); expect(queryAllByA11yRole(/ink/g)).toHaveLength(2); + expect(() => getAllByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND); expect(queryAllByA11yRole(NO_MATCHES_TEXT)).toEqual([]); + + await expect(findAllByA11yRole('link')).resolves.toHaveLength(2); + await expect( + findAllByA11yRole(NO_MATCHES_TEXT, waitForOptions) + ).rejects.toThrow(NO_INSTANCES_FOUND); }); // TODO: accessibilityStates was removed from RN 0.62 @@ -169,8 +230,10 @@ test.skip('getAllByA11yStates, queryAllByA11yStates', () => { expect(queryAllByA11yStates(NO_MATCHES_TEXT)).toEqual([]); }); -test('getByA11yState, queryByA11yState', () => { - const { getByA11yState, queryByA11yState } = render(
); +test('getByA11yState, queryByA11yState, findByA11yState', async () => { + const { getByA11yState, queryByA11yState, findByA11yState } = render( +
+ ); expect(getByA11yState({ selected: true }).props.accessibilityState).toEqual({ selected: true, @@ -192,25 +255,49 @@ test('getByA11yState, queryByA11yState', () => { expect(() => queryByA11yState({ expanded: false })).toThrow( FOUND_TWO_INSTANCES ); + + const asyncButton = await findByA11yState({ selected: true }); + expect(asyncButton.props.accessibilityState).toEqual({ + selected: true, + expanded: false, + }); + await expect( + findByA11yState({ disabled: true }, waitForOptions) + ).rejects.toThrow(NO_INSTANCES_FOUND); + await expect( + findByA11yState({ expanded: false }, waitForOptions) + ).rejects.toThrow(FOUND_TWO_INSTANCES); }); -test('getAllByA11yState, queryAllByA11yState', () => { - const { getAllByA11yState, queryAllByA11yState } = render(
); +test('getAllByA11yState, queryAllByA11yState, findAllByA11yState', async () => { + const { getAllByA11yState, queryAllByA11yState, findAllByA11yState } = render( +
+ ); - expect(getAllByA11yState({ selected: true }).length).toEqual(1); - expect(queryAllByA11yState({ selected: true }).length).toEqual(1); + expect(getAllByA11yState({ selected: true })).toHaveLength(1); + expect(queryAllByA11yState({ selected: true })).toHaveLength(1); expect(() => getAllByA11yState({ disabled: true })).toThrow( NO_INSTANCES_FOUND ); expect(queryAllByA11yState({ disabled: true })).toEqual([]); - expect(getAllByA11yState({ expanded: false }).length).toEqual(2); - expect(queryAllByA11yState({ expanded: false }).length).toEqual(2); + expect(getAllByA11yState({ expanded: false })).toHaveLength(2); + expect(queryAllByA11yState({ expanded: false })).toHaveLength(2); + + await expect(findAllByA11yState({ selected: true })).resolves.toHaveLength(1); + await expect( + findAllByA11yState({ disabled: true }, waitForOptions) + ).rejects.toThrow(NO_INSTANCES_FOUND); + await expect(findAllByA11yState({ expanded: false })).resolves.toHaveLength( + 2 + ); }); -test('getByA11yValue, queryByA11yValue', () => { - const { getByA11yValue, queryByA11yValue } = render(
); +test('getByA11yValue, queryByA11yValue, findByA11yValue', async () => { + const { getByA11yValue, queryByA11yValue, findByA11yValue } = render( +
+ ); expect(getByA11yValue({ min: 40 }).props.accessibilityValue).toEqual({ min: 40, @@ -226,17 +313,37 @@ test('getByA11yValue, queryByA11yValue', () => { expect(() => getByA11yValue({ max: 60 })).toThrow(FOUND_TWO_INSTANCES); expect(() => queryByA11yValue({ max: 60 })).toThrow(FOUND_TWO_INSTANCES); + + const asyncElement = await findByA11yValue({ min: 40 }); + expect(asyncElement.props.accessibilityValue).toEqual({ + min: 40, + max: 60, + }); + await expect(findByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow( + NO_INSTANCES_FOUND + ); + await expect(findByA11yValue({ max: 60 }, waitForOptions)).rejects.toThrow( + FOUND_TWO_INSTANCES + ); }); -test('getAllByA11yValue, queryAllByA11yValue', () => { - const { getAllByA11yValue, queryAllByA11yValue } = render(
); +test('getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue', async () => { + const { getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue } = render( +
+ ); - expect(getAllByA11yValue({ min: 40 }).length).toEqual(1); - expect(queryAllByA11yValue({ min: 40 }).length).toEqual(1); + expect(getAllByA11yValue({ min: 40 })).toHaveLength(1); + expect(queryAllByA11yValue({ min: 40 })).toHaveLength(1); expect(() => getAllByA11yValue({ min: 50 })).toThrow(NO_INSTANCES_FOUND); expect(queryAllByA11yValue({ min: 50 })).toEqual([]); - expect(getAllByA11yValue({ max: 60 }).length).toEqual(2); - expect(queryAllByA11yValue({ max: 60 }).length).toEqual(2); + expect(queryAllByA11yValue({ max: 60 })).toHaveLength(2); + expect(getAllByA11yValue({ max: 60 })).toHaveLength(2); + + await expect(findAllByA11yValue({ min: 40 })).resolves.toHaveLength(1); + await expect(findAllByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow( + NO_INSTANCES_FOUND + ); + await expect(findAllByA11yValue({ max: 60 })).resolves.toHaveLength(2); }); diff --git a/src/__tests__/findByApi.test.js b/src/__tests__/findByApi.test.js new file mode 100644 index 000000000..3f15c7840 --- /dev/null +++ b/src/__tests__/findByApi.test.js @@ -0,0 +1,58 @@ +// @flow +import React from 'react'; +import { View, Text, TextInput } from 'react-native'; +import { render } from '..'; + +test('findBy queries work asynchronously', async () => { + const options = { timeout: 10 }; // Short timeout so that this test runs quickly + const { + rerender, + findByTestId, + findAllByTestId, + findByText, + findAllByText, + findByPlaceholder, + findAllByPlaceholder, + findByDisplayValue, + findAllByDisplayValue, + } = render(); + await expect(findByTestId('aTestId', options)).rejects.toBeTruthy(); + await expect(findAllByTestId('aTestId', options)).rejects.toBeTruthy(); + await expect(findByText('Some Text', options)).rejects.toBeTruthy(); + await expect(findAllByText('Some Text', options)).rejects.toBeTruthy(); + await expect( + findByPlaceholder('Placeholder Text', options) + ).rejects.toBeTruthy(); + await expect( + findAllByPlaceholder('Placeholder Text', options) + ).rejects.toBeTruthy(); + await expect( + findByDisplayValue('Display Value', options) + ).rejects.toBeTruthy(); + await expect( + findAllByDisplayValue('Display Value', options) + ).rejects.toBeTruthy(); + + setTimeout( + () => + rerender( + + Some Text + + + + ), + 20 + ); + + await expect(findByTestId('aTestId')).resolves.toBeTruthy(); + await expect(findAllByTestId('aTestId')).resolves.toHaveLength(1); + await expect(findByText('Some Text')).resolves.toBeTruthy(); + await expect(findAllByText('Some Text')).resolves.toHaveLength(1); + await expect(findByPlaceholder('Placeholder Text')).resolves.toBeTruthy(); + await expect(findAllByPlaceholder('Placeholder Text')).resolves.toHaveLength( + 1 + ); + await expect(findByDisplayValue('Display Value')).resolves.toBeTruthy(); + await expect(findAllByDisplayValue('Display Value')).resolves.toHaveLength(1); +}, 10000); diff --git a/src/__tests__/within.test.js b/src/__tests__/within.test.js index 97bf5252b..a4fb956c1 100644 --- a/src/__tests__/within.test.js +++ b/src/__tests__/within.test.js @@ -3,7 +3,7 @@ import React from 'react'; import { View, Text, TextInput } from 'react-native'; import { render, within } from '..'; -test('within() exposes basic queries', () => { +test('within() exposes basic queries', async () => { const rootQueries = render( @@ -28,6 +28,12 @@ test('within() exposes basic queries', () => { expect(firstQueries.queryByText('Same Text')).toBeTruthy(); expect(firstQueries.getByDisplayValue('Same Value')).toBeTruthy(); expect(firstQueries.getByPlaceholder('Same Placeholder')).toBeTruthy(); + await expect( + firstQueries.findByDisplayValue('Same Value') + ).resolves.toBeTruthy(); + await expect( + firstQueries.findAllByPlaceholder('Same Placeholder') + ).resolves.toHaveLength(1); const secondQueries = within(rootQueries.getByA11yHint('second')); expect(secondQueries.getAllByText('Same Text')).toHaveLength(1); @@ -38,7 +44,7 @@ test('within() exposes basic queries', () => { expect(secondQueries.getByPlaceholder('Same Placeholder')).toBeTruthy(); }); -test('within() exposes a11y queries', () => { +test('within() exposes a11y queries', async () => { const rootQueries = render( @@ -64,8 +70,22 @@ test('within() exposes a11y queries', () => { const firstQueries = within(rootQueries.getByA11yHint('first')); expect(firstQueries.getByA11yLabel('Same Label')).toBeTruthy(); expect(firstQueries.getByA11yHint('Same Hint')).toBeTruthy(); + expect(firstQueries.queryByA11yLabel('Same Label')).toBeTruthy(); + expect(firstQueries.queryByA11yHint('Same Hint')).toBeTruthy(); + await expect( + firstQueries.findByA11yLabel('Same Label') + ).resolves.toBeTruthy(); + await expect(firstQueries.findByA11yHint('Same Hint')).resolves.toBeTruthy(); const secondQueries = within(rootQueries.getByA11yHint('second')); expect(secondQueries.getAllByA11yLabel('Same Label')).toHaveLength(1); expect(secondQueries.getAllByA11yHint('Same Hint')).toHaveLength(1); + expect(secondQueries.queryAllByA11yLabel('Same Label')).toHaveLength(1); + expect(secondQueries.queryAllByA11yHint('Same Hint')).toHaveLength(1); + await expect( + secondQueries.findAllByA11yLabel('Same Label') + ).resolves.toHaveLength(1); + await expect( + secondQueries.findAllByA11yHint('Same Hint') + ).resolves.toHaveLength(1); }); diff --git a/src/helpers/a11yAPI.js b/src/helpers/a11yAPI.js index b3629110b..3e6edf79f 100644 --- a/src/helpers/a11yAPI.js +++ b/src/helpers/a11yAPI.js @@ -1,11 +1,14 @@ // @flow import makeQuery from './makeQuery'; import type { A11yRole, A11yStates, A11yState, A11yValue } from '../types.flow'; +import type { WaitForOptions } from './findByAPI'; type GetReturn = ReactTestInstance; type GetAllReturn = Array; type QueryReturn = ReactTestInstance | null; type QueryAllReturn = Array | []; +type FindReturn = Promise; +type FindAllReturn = Promise; type A11yAPI = {| // Label @@ -13,36 +16,48 @@ type A11yAPI = {| getAllByA11yLabel: (string | RegExp) => GetAllReturn, queryByA11yLabel: (string | RegExp) => QueryReturn, queryAllByA11yLabel: (string | RegExp) => QueryAllReturn, + findByA11yLabel: (string | RegExp, ?WaitForOptions) => FindReturn, + findAllByA11yLabel: (string | RegExp, ?WaitForOptions) => FindAllReturn, // Hint getByA11yHint: (string | RegExp) => GetReturn, getAllByA11yHint: (string | RegExp) => GetAllReturn, queryByA11yHint: (string | RegExp) => QueryReturn, queryAllByA11yHint: (string | RegExp) => QueryAllReturn, + findByA11yHint: (string | RegExp, ?WaitForOptions) => FindReturn, + findAllByA11yHint: (string | RegExp, ?WaitForOptions) => FindAllReturn, // Role getByA11yRole: (A11yRole | RegExp) => GetReturn, getAllByA11yRole: (A11yRole | RegExp) => GetAllReturn, queryByA11yRole: (A11yRole | RegExp) => QueryReturn, queryAllByA11yRole: (A11yRole | RegExp) => QueryAllReturn, + findByA11yRole: (A11yRole, ?WaitForOptions) => FindReturn, + findAllByA11yRole: (A11yRole, ?WaitForOptions) => FindAllReturn, // States getByA11yStates: (A11yStates | Array) => GetReturn, getAllByA11yStates: (A11yStates | Array) => GetAllReturn, queryByA11yStates: (A11yStates | Array) => QueryReturn, queryAllByA11yStates: (A11yStates | Array) => QueryAllReturn, + findByA11yStates: (A11yStates, ?WaitForOptions) => FindReturn, + findAllByA11yStates: (A11yStates, ?WaitForOptions) => FindAllReturn, // State getByA11yState: A11yState => GetReturn, getAllByA11yState: A11yState => GetAllReturn, queryByA11yState: A11yState => QueryReturn, queryAllByA11yState: A11yState => QueryAllReturn, + findByA11yState: (A11yState, ?WaitForOptions) => FindReturn, + findAllByA11yState: (A11yState, ?WaitForOptions) => FindAllReturn, // Value getByA11yValue: A11yValue => GetReturn, getAllByA11yValue: A11yValue => GetAllReturn, queryByA11yValue: A11yValue => QueryReturn, queryAllByA11yValue: A11yValue => QueryAllReturn, + findByA11yValue: (A11yValue, ?WaitForOptions) => FindReturn, + findAllByA11yValue: (A11yValue, ?WaitForOptions) => FindAllReturn, |}; export function matchStringValue( @@ -92,6 +107,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yLabel', 'getAllByAccessibilityLabel'], queryBy: ['queryByA11yLabel', 'queryByAccessibilityLabel'], queryAllBy: ['queryAllByA11yLabel', 'queryAllByAccessibilityLabel'], + findBy: ['findByA11yLabel', 'findByAccessibilityLabel'], + findAllBy: ['findAllByA11yLabel', 'findAllByAccessibilityLabel'], }, matchStringValue )(instance), @@ -102,6 +119,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yHint', 'getAllByAccessibilityHint'], queryBy: ['queryByA11yHint', 'queryByAccessibilityHint'], queryAllBy: ['queryAllByA11yHint', 'queryAllByAccessibilityHint'], + findBy: ['findByA11yHint', 'findByAccessibilityHint'], + findAllBy: ['findAllByA11yHint', 'findAllByAccessibilityHint'], }, matchStringValue )(instance), @@ -112,6 +131,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yRole', 'getAllByAccessibilityRole'], queryBy: ['queryByA11yRole', 'queryByAccessibilityRole'], queryAllBy: ['queryAllByA11yRole', 'queryAllByAccessibilityRole'], + findBy: ['findByA11yRole', 'findByAccessibilityRole'], + findAllBy: ['findAllByA11yRole', 'findAllByAccessibilityRole'], }, matchStringValue )(instance), @@ -122,6 +143,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yStates', 'getAllByAccessibilityStates'], queryBy: ['queryByA11yStates', 'queryByAccessibilityStates'], queryAllBy: ['queryAllByA11yStates', 'queryAllByAccessibilityStates'], + findBy: ['findByA11yStates', 'findByAccessibilityStates'], + findAllBy: ['findAllByA11yStates', 'findAllByAccessibilityStates'], }, matchArrayValue )(instance), @@ -132,6 +155,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yState', 'getAllByAccessibilityState'], queryBy: ['queryByA11yState', 'queryByAccessibilityState'], queryAllBy: ['queryAllByA11yState', 'queryAllByAccessibilityState'], + findBy: ['findByA11yState', 'findByAccessibilityState'], + findAllBy: ['findAllByA11yState', 'findAllByAccessibilityState'], }, matchObject )(instance), @@ -142,6 +167,8 @@ const a11yAPI = (instance: ReactTestInstance): A11yAPI => getAllBy: ['getAllByA11yValue', 'getAllByAccessibilityValue'], queryBy: ['queryByA11yValue', 'queryByAccessibilityValue'], queryAllBy: ['queryAllByA11yValue', 'queryAllByAccessibilityValue'], + findBy: ['findByA11yValue', 'findByAccessibilityValue'], + findAllBy: ['findAllByA11yValue', 'findAllByAccessibilityValue'], }, matchObject )(instance), diff --git a/src/helpers/findByAPI.js b/src/helpers/findByAPI.js new file mode 100644 index 000000000..f7878ad51 --- /dev/null +++ b/src/helpers/findByAPI.js @@ -0,0 +1,80 @@ +// @flow +import { + fixedGetByTestId, + getAllByTestId, + getByText, + getAllByText, + getByPlaceholder, + getAllByPlaceholder, + getByDisplayValue, + getAllByDisplayValue, +} from './getByAPI'; +import waitForElement from '../waitForElement'; + +export type WaitForOptions = { + timeout?: number, + interval?: number, +}; + +const makeFindQuery = ( + instance: ReactTestInstance, + getQuery: (instance: ReactTestInstance) => (text: Text) => Result, + text: Text, + waitForOptions: WaitForOptions +): Promise => + waitForElement( + () => getQuery(instance)(text), + waitForOptions.timeout, + waitForOptions.interval + ); + +export const findByTestId = (instance: ReactTestInstance) => ( + testId: string, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, fixedGetByTestId, testId, waitForOptions); + +export const findAllByTestId = (instance: ReactTestInstance) => ( + testId: string, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getAllByTestId, testId, waitForOptions); + +export const findByText = (instance: ReactTestInstance) => ( + text: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getByText, text, waitForOptions); + +export const findAllByText = (instance: ReactTestInstance) => ( + text: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getAllByText, text, waitForOptions); + +export const findByPlaceholder = (instance: ReactTestInstance) => ( + placeholder: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getByPlaceholder, placeholder, waitForOptions); + +export const findAllByPlaceholder = (instance: ReactTestInstance) => ( + placeholder: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getAllByPlaceholder, placeholder, waitForOptions); + +export const findByDisplayValue = (instance: ReactTestInstance) => ( + value: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getByDisplayValue, value, waitForOptions); + +export const findAllByDisplayValue = (instance: ReactTestInstance) => ( + value: string | RegExp, + waitForOptions: WaitForOptions = {} +) => makeFindQuery(instance, getAllByDisplayValue, value, waitForOptions); + +export const findByAPI = (instance: ReactTestInstance) => ({ + findByTestId: findByTestId(instance), + findByText: findByText(instance), + findByPlaceholder: findByPlaceholder(instance), + findByDisplayValue: findByDisplayValue(instance), + findAllByTestId: findAllByTestId(instance), + findAllByText: findAllByText(instance), + findAllByPlaceholder: findAllByPlaceholder(instance), + findAllByDisplayValue: findAllByDisplayValue(instance), +}); diff --git a/src/helpers/getByAPI.js b/src/helpers/getByAPI.js index 8a2af2d42..6ed49f47f 100644 --- a/src/helpers/getByAPI.js +++ b/src/helpers/getByAPI.js @@ -157,6 +157,25 @@ export const getByTestId = (instance: ReactTestInstance) => } }; +export const fixedGetByTestId = (instance: ReactTestInstance) => + function getByTestIdFn(testID: string) { + try { + const results = getAllByTestId(instance)(testID); + if (results.length === 1) { + return results[0]; + } else { + throw new ErrorWithStack( + ` Expected 1 but found ${ + results.length + } instances with testID: ${String(testID)}`, + getByTestIdFn + ); + } + } catch (error) { + throw new ErrorWithStack(prepareErrorMessage(error), getByTestIdFn); + } + }; + export const getAllByName = (instance: ReactTestInstance, warnFn?: Function) => function getAllByNameFn(name: string | React.ComponentType) { warnFn && warnFn('getAllByName'); diff --git a/src/helpers/makeQuery.js b/src/helpers/makeQuery.js index 059a853b2..fe257708f 100644 --- a/src/helpers/makeQuery.js +++ b/src/helpers/makeQuery.js @@ -4,6 +4,8 @@ import { prepareErrorMessage, createQueryByError, } from './errors'; +import waitForElement from '../waitForElement'; +import type { WaitForOptions } from './findByAPI'; function isNodeValid(node: ReactTestInstance) { return typeof node.type === 'string'; @@ -20,6 +22,8 @@ type QueryNames = { getAllBy: Array, queryBy: Array, queryAllBy: Array, + findBy: Array, + findAllBy: Array, }; const makeQuery = ( @@ -65,11 +69,29 @@ const makeQuery = ( } }; + const findBy = (matcher: M, waitForOptions?: WaitForOptions) => { + return waitForElement( + () => getBy(matcher), + waitForOptions?.timeout, + waitForOptions?.interval + ); + }; + + const findAllBy = (matcher: M, waitForOptions?: WaitForOptions) => { + return waitForElement( + () => getAllBy(matcher), + waitForOptions?.timeout, + waitForOptions?.interval + ); + }; + return { ...makeAliases(queryNames.getBy, getBy), ...makeAliases(queryNames.getAllBy, getAllBy), ...makeAliases(queryNames.queryBy, queryBy), ...makeAliases(queryNames.queryAllBy, queryAllBy), + ...makeAliases(queryNames.findBy, findBy), + ...makeAliases(queryNames.findAllBy, findAllBy), }; }; diff --git a/src/render.js b/src/render.js index cb46f726e..658aba1c1 100644 --- a/src/render.js +++ b/src/render.js @@ -5,6 +5,7 @@ import act from './act'; import { addToCleanupQueue } from './cleanup'; import { getByAPI } from './helpers/getByAPI'; import { queryByAPI } from './helpers/queryByAPI'; +import { findByAPI } from './helpers/findByAPI'; import a11yAPI from './helpers/a11yAPI'; import debugShallow from './helpers/debugShallow'; import debugDeep from './helpers/debugDeep'; @@ -40,6 +41,7 @@ export default function render( return { ...getByAPI(instance), ...queryByAPI(instance), + ...findByAPI(instance), ...a11yAPI(instance), update, rerender: update, // alias for `update` diff --git a/src/within.js b/src/within.js index a7dd00b40..3b12c3e06 100644 --- a/src/within.js +++ b/src/within.js @@ -1,12 +1,14 @@ // @flow import { getByAPI } from './helpers/getByAPI'; import { queryByAPI } from './helpers/queryByAPI'; +import { findByAPI } from './helpers/findByAPI'; import a11yAPI from './helpers/a11yAPI'; export default function within(instance: ReactTestInstance) { return { ...getByAPI(instance), ...queryByAPI(instance), + ...findByAPI(instance), ...a11yAPI(instance), }; } diff --git a/typings/__tests__/index.test.tsx b/typings/__tests__/index.test.tsx index 5e0db16f4..3caf5f299 100644 --- a/typings/__tests__/index.test.tsx +++ b/typings/__tests__/index.test.tsx @@ -121,6 +121,57 @@ const queryAllByDisplayValueRegExp: Array< ReactTestInstance > = tree.queryAllByDisplayValue(/View/g); +// findBy API tests +const findBy: Promise[] = [ + tree.findByText('View'), + tree.findByText('View', { timeout: 10, interval: 10 }), + tree.findByText(/View/g), + tree.findByText(/View/g, { timeout: 10, interval: 5 }), + tree.findByPlaceholder('my placeholder'), + tree.findByPlaceholder('my placeholder', { timeout: 10, interval: 5 }), + tree.findByPlaceholder(/placeholder/g), + tree.findByPlaceholder(/placeholder/g, { timeout: 10, interval: 5 }), + tree.findByDisplayValue('my value'), + tree.findByDisplayValue('my value', { timeout: 10, interval: 10 }), + tree.findByDisplayValue(/value/g), + tree.findByDisplayValue(/value/g, { timeout: 10, interval: 10 }), + tree.findByTestId('test-id'), + tree.findByTestId('test-id', { timeout: 10, interval: 10 }), + tree.findByA11yLabel('label'), + tree.findByA11yLabel('label', { timeout: 10, interval: 10 }), + tree.findByA11yHint('label'), + tree.findByA11yHint('label', { timeout: 10, interval: 10 }), + tree.findByA11yRole('button'), + tree.findByA11yRole('button', { timeout: 10, interval: 10 }), + tree.findByA11yState({ busy: true }), + tree.findByA11yState({ busy: true }, { timeout: 10, interval: 10 }), + tree.findByA11yValue({ min: 10 }), + tree.findByA11yValue({ min: 10 }, { timeout: 10, interval: 10 }), +]; + +const findAllBy: Promise[] = [ + tree.findAllByText('View'), + tree.findAllByText('View', { timeout: 10, interval: 10 }), + tree.findAllByText(/View/g), + tree.findAllByText(/View/g, { timeout: 10, interval: 5 }), + tree.findAllByPlaceholder('my placeholder'), + tree.findAllByPlaceholder('my placeholder', { timeout: 10, interval: 10 }), + tree.findAllByPlaceholder(/placeholder/g), + tree.findAllByPlaceholder(/placeholder/g, { timeout: 10, interval: 10 }), + tree.findAllByDisplayValue('View'), + tree.findAllByDisplayValue('View', { timeout: 10, interval: 10 }), + tree.findAllByDisplayValue(/View/g), + tree.findAllByDisplayValue(/View/g, { timeout: 10, interval: 10 }), + tree.findAllByTestId('test-id'), + tree.findAllByTestId('test-id', { timeout: 10, interval: 10 }), + tree.findAllByA11yHint('label'), + tree.findAllByA11yHint('label', { timeout: 10, interval: 10 }), + tree.findAllByA11yState({ busy: true }), + tree.findAllByA11yState({ busy: true }, { timeout: 10, interval: 10 }), + tree.findAllByA11yValue({ min: 10 }), + tree.findAllByA11yValue({ min: 10 }, { timeout: 10, interval: 10 }), +]; + // Accessibility queries const getByA11yLabel: ReactTestInstance = tree.getByA11yLabel('label'); const getAllByA11yLabel: Array = tree.getAllByA11yLabel( @@ -130,6 +181,7 @@ const queryByA11yLabel: ReactTestInstance = tree.queryByA11yLabel('label'); const queryAllByA11yLabel: Array = tree.queryAllByA11yLabel( 'label' ); + const getByA11yHint: ReactTestInstance = tree.getByA11yHint('label'); const getAllByA11yHint: Array = tree.getAllByA11yHint( 'label' @@ -138,6 +190,7 @@ const queryByA11yHint: ReactTestInstance = tree.queryByA11yHint('label'); const queryAllByA11yHint: Array = tree.queryAllByA11yHint( 'label' ); + const getByA11yRole: ReactTestInstance = tree.getByA11yRole('button'); const getAllByA11yRole: Array = tree.getAllByA11yRole( 'button' @@ -146,6 +199,7 @@ const queryByA11yRole: ReactTestInstance = tree.queryByA11yRole('button'); const queryAllByA11yRole: Array = tree.queryAllByA11yRole( 'button' ); + const getByA11yStates: ReactTestInstance = tree.getByA11yStates('selected'); const getByA11yStatesArray: ReactTestInstance = tree.getByA11yStates(['selected']); const getAllByA11yStates: Array = tree.getAllByA11yStates( @@ -221,112 +275,75 @@ act(() => { // within API const instance: ReactTestInstance = tree.getByText(''); -const withinGetByText: ReactTestInstance = within(instance).getByText('Test'); -const withinGetAllByText: ReactTestInstance[] = within(instance).getAllByText( - 'Test' -); -const withinGetByDisplayValue: ReactTestInstance = within( - instance -).getByDisplayValue('Test'); -const withinGetAllByDisplayValue: ReactTestInstance[] = within( - instance -).getAllByDisplayValue('Test'); -const withinGetByPlaceholder: ReactTestInstance = within( - instance -).getByPlaceholder('Test'); -const withinGetAllByPlaceholder: ReactTestInstance[] = within( - instance -).getAllByPlaceholder('Test'); -const withinGetByTestId: ReactTestInstance = within(instance).getByTestId( - 'Test' -); -const withinGetAllByTestId: ReactTestInstance[] = within( - instance -).getAllByTestId('Test'); -const withinQueryByText: ReactTestInstance | null = within( - instance -).queryByText('Test'); -const withinQueryAllByText: ReactTestInstance[] = within( - instance -).queryAllByText('Test'); -const withinQueryByDisplayValue: ReactTestInstance | null = within( - instance -).queryByDisplayValue('Test'); -const withinQueryAllByDisplayValue: ReactTestInstance[] = within( - instance -).queryAllByDisplayValue('Test'); -const withinQueryByPlaceholder: ReactTestInstance | null = within( - instance -).queryByPlaceholder('Test'); -const withinQueryAllByPlaceholder: ReactTestInstance[] = within( - instance -).queryAllByPlaceholder('Test'); -const withinQueryByTestId: ReactTestInstance | null = within( - instance -).queryByTestId('Test'); -const withinQueryAllByTestId: ReactTestInstance[] = within( - instance -).queryAllByTestId('Test'); +const withinGet: Array = [ + within(instance).getByText('Test'), + within(instance).getByDisplayValue('Test'), + within(instance).getByPlaceholder('Test'), + within(instance).getByTestId('Test'), + within(instance).getByA11yLabel('Test'), + within(instance).getByA11yHint('Test'), + within(instance).getByA11yRole('button'), + within(instance).getByA11yState({ busy: true }), + within(instance).getByA11yValue({ min: 10 }), +]; -const withinGetByA11yLabel: ReactTestInstance = within(instance).getByA11yLabel( - 'Test' -); -const withinGetAllByA11yLabel: ReactTestInstance[] = within( - instance -).getAllByA11yLabel('Test'); -const withinGetByA11yHint: ReactTestInstance = within(instance).getByA11yHint( - 'Test' -); -const withinGetAllByA11yHint: ReactTestInstance[] = within( - instance -).getAllByA11yHint('Test'); -const withinGetByA11yRole: ReactTestInstance = within(instance).getByA11yRole( - 'button' -); -const withinGetAllByA11yRole: ReactTestInstance[] = within( - instance -).getAllByA11yRole('button'); -const withinGetByA11yState: ReactTestInstance = within( - instance -).getByA11yState({ busy: true }); -const withinGetAllByA11yState: ReactTestInstance[] = within( - instance -).getAllByA11yState({ busy: true }); -const withinGetByA11yValue: ReactTestInstance = within( - instance -).getByA11yValue({ min: 10 }); -const withinGetAllByA11yValue: ReactTestInstance[] = within( - instance -).getAllByA11yValue({ min: 10 }); +const withinGetAll: Array = [ + within(instance).getAllByText('Test'), + within(instance).getAllByDisplayValue('Test'), + within(instance).getAllByPlaceholder('Test'), + within(instance).getAllByTestId('Test'), + within(instance).getAllByA11yLabel('Test'), + within(instance).getAllByA11yHint('Test'), + within(instance).getAllByA11yRole('button'), + within(instance).getAllByA11yState({ busy: true }), + within(instance).getAllByA11yValue({ min: 10 }), +]; + +const withinQuery: Array = [ + within(instance).queryByText('Test'), + within(instance).queryByDisplayValue('Test'), + within(instance).queryByPlaceholder('Test'), + within(instance).queryByTestId('Test'), + within(instance).queryByA11yLabel('Test'), + within(instance).queryByA11yHint('Test'), + within(instance).queryByA11yRole('button'), + within(instance).queryByA11yState({ busy: true }), + within(instance).queryByA11yValue({ min: 10 }), +]; + +const withinQueryAll: Array = [ + within(instance).queryAllByText('Test'), + within(instance).queryAllByDisplayValue('Test'), + within(instance).queryAllByPlaceholder('Test'), + within(instance).queryAllByTestId('Test'), + within(instance).queryAllByA11yLabel('Test'), + within(instance).queryAllByA11yHint('Test'), + within(instance).queryAllByA11yRole('button'), + within(instance).queryAllByA11yState({ busy: true }), + within(instance).queryAllByA11yValue({ min: 10 }), +]; + +const withinFind: Promise[] = [ + within(instance).findByText('Test'), + within(instance).findByDisplayValue('Test'), + within(instance).findByPlaceholder('Test'), + within(instance).findByTestId('Test'), + within(instance).findByA11yLabel('Test'), + within(instance).findByA11yHint('Test'), + within(instance).findByA11yRole('button'), + within(instance).findByA11yState({ busy: true }), + within(instance).findByA11yValue({ min: 10 }), +]; -const withinQueryByA11yLabel: ReactTestInstance | null = within( - instance -).queryByA11yLabel('Test'); -const withinQueryAllByA11yLabel: ReactTestInstance[] = within( - instance -).queryAllByA11yLabel('Test'); -const withinQueryByA11yHint: ReactTestInstance | null = within( - instance -).queryByA11yHint('Test'); -const withinQueryAllByA11yHint: ReactTestInstance[] = within( - instance -).queryAllByA11yHint('Test'); -const withinQueryByA11yRole: ReactTestInstance | null = within( - instance -).queryByA11yRole('button'); -const withinQueryAllByA11yRole: ReactTestInstance[] = within( - instance -).queryAllByA11yRole('button'); -const withinQueryByA11yState: ReactTestInstance | null = within( - instance -).queryByA11yState({ busy: true }); -const withinQueryAllByA11yState: ReactTestInstance[] = within( - instance -).queryAllByA11yState({ busy: true }); -const withinQueryByA11yValue: ReactTestInstance | null = within( - instance -).queryByA11yValue({ min: 10 }); -const withinQueryAllByA11yValue: ReactTestInstance[] = within( - instance -).queryAllByA11yValue({ min: 10 }); +const withinFindAll: Promise[] = [ + within(instance).findAllByText('Test'), + within(instance).findAllByDisplayValue('Test'), + within(instance).findAllByPlaceholder('Test'), + within(instance).findAllByTestId('Test'), + within(instance).findAllByA11yLabel('Test'), + within(instance).findAllByA11yHint('Test'), + within(instance).findAllByA11yRole('button'), + within(instance).findAllByA11yState({ busy: true }), + within(instance).findAllByA11yValue({ min: 10 }), +]; diff --git a/typings/index.d.ts b/typings/index.d.ts index 4afc01665..0a5bb8d2f 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2,7 +2,14 @@ import * as React from 'react'; import { AccessibilityState, AccessibilityStates, AccessibilityRole } from 'react-native'; import { ReactTestInstance, ReactTestRendererJSON } from 'react-test-renderer'; -export interface GetByAPI { +type GetReturn = ReactTestInstance; +type GetAllReturn = Array; +type QueryReturn = ReactTestInstance | null; +type QueryAllReturn = Array | []; +type FindReturn = Promise; +type FindAllReturn = Promise; + +interface GetByAPI { getByName: (name: React.ReactType | string) => ReactTestInstance; getByType:

(type: React.ComponentType

) => ReactTestInstance; getByText: (text: string | RegExp) => ReactTestInstance; @@ -28,7 +35,7 @@ export interface GetByAPI { UNSAFE_getAllByProps: (props: Record) => Array, } -export interface QueryByAPI { +interface QueryByAPI { queryByName: (name: React.ReactType | string) => ReactTestInstance | null; queryByType:

(type: React.ComponentType

) => ReactTestInstance | null; queryByText: (name: string | RegExp) => ReactTestInstance | null; @@ -63,10 +70,42 @@ export interface QueryByAPI { UNSAFE_queryAllByProps: (props: Record) => Array | [], } -type GetReturn = ReactTestInstance; -type GetAllReturn = Array; -type QueryReturn = ReactTestInstance | null; -type QueryAllReturn = Array | []; +export interface WaitForOptions { + timeout: number; + interval: number; +} + +interface FindByAPI { + findByText: ( + text: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findByPlaceholder: ( + placeholder: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findByDisplayValue: ( + value: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findByTestId: (testID: string, waitForOptions?: WaitForOptions) => FindReturn; + findAllByText: ( + text: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; + findAllByPlaceholder: ( + placeholder: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; + findAllByDisplayValue: ( + value: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; + findAllByTestId: ( + testID: string, + waitForOptions?: WaitForOptions + ) => FindAllReturn; +} // Not yet available in DefinitelyTyped export type A11yValue = { @@ -82,18 +121,42 @@ type A11yAPI = { getAllByA11yLabel: (matcher: string | RegExp) => GetAllReturn, queryByA11yLabel: (matcher: string | RegExp) => QueryReturn, queryAllByA11yLabel: (matcher: string | RegExp) => QueryAllReturn, + findByA11yLabel: ( + matcher: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findAllByA11yLabel: ( + matcher: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; // Hint getByA11yHint: (matcher: string | RegExp) => GetReturn, getAllByA11yHint: (matcher: string | RegExp) => GetAllReturn, queryByA11yHint: (matcher: string | RegExp) => QueryReturn, queryAllByA11yHint: (matcher: string | RegExp) => QueryAllReturn, + findByA11yHint: ( + matcher: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findAllByA11yHint: ( + matcher: string | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; // Role getByA11yRole: (matcher: AccessibilityRole | RegExp) => GetReturn, getAllByA11yRole: (matcher: AccessibilityRole | RegExp) => GetAllReturn, queryByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryReturn, queryAllByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryAllReturn, + findByA11yRole: ( + matcher: AccessibilityRole | RegExp, + waitForOptions?: WaitForOptions + ) => FindReturn; + findAllByA11yRole: ( + matcher: AccessibilityRole | RegExp, + waitForOptions?: WaitForOptions + ) => FindAllReturn; // States getByA11yStates: (matcher: AccessibilityStates | Array) => GetReturn, @@ -106,12 +169,28 @@ type A11yAPI = { getAllByA11yState: (matcher: AccessibilityState) => GetAllReturn, queryByA11yState: (matcher: AccessibilityState) => QueryReturn, queryAllByA11yState: (matcher: AccessibilityState) => QueryAllReturn, + findByA11yState: ( + matcher: AccessibilityState, + waitForOptions?: WaitForOptions + ) => FindReturn; + findAllByA11yState: ( + matcher: AccessibilityState, + waitForOptions?: WaitForOptions + ) => FindAllReturn; // Value getByA11yValue: (matcher: A11yValue) => GetReturn, getAllByA11yValue: (matcher: A11yValue) => GetAllReturn, queryByA11yValue: (matcher: A11yValue) => QueryReturn, queryAllByA11yValue: (matcher: A11yValue) => QueryAllReturn, + findByA11yValue: ( + matcher: A11yValue, + waitForOptions?: WaitForOptions + ) => FindReturn; + findAllByA11yValue: ( + matcher: A11yValue, + waitForOptions?: WaitForOptions + ) => FindAllReturn; }; export interface Thenable { @@ -123,7 +202,7 @@ export interface RenderOptions { createNodeMock?: (element: React.ReactElement) => any; } -type Queries = GetByAPI & QueryByAPI & A11yAPI; +type Queries = GetByAPI & QueryByAPI & FindByAPI & A11yAPI; export interface RenderAPI extends Queries { update(nextElement: React.ReactElement): void; diff --git a/website/docs/API.md b/website/docs/API.md index f3037d779..62176f09b 100644 --- a/website/docs/API.md +++ b/website/docs/API.md @@ -351,7 +351,7 @@ Defined as: function within(instance: ReactTestInstance): Queries ``` -Perform [queries](./Queries.md) scoped to given element. +Perform [queries](./Queries.md) scoped to given element. :::note Please note that additional `render` specific operations like `update`, `unmount`, `debug`, `toJSON` are _not_ included. @@ -361,13 +361,14 @@ Please note that additional `render` specific operations like `update`, `unmount const detailsScreen = within(getByA11yHint('Details Screen')); expect(detailsScreen.getByText('Some Text')).toBeTruthy(); expect(detailsScreen.getByDisplayValue('Some Value')).toBeTruthy(); -expect(detailsScreen.getByA11yLabel('Some Label')).toBeTruthy(); -expect(detailsScreen.getByA11yHint('Some Label')).toBeTruthy(); +expect(detailsScreen.queryByA11yLabel('Some Label')).toBeTruthy(); +await expect(detailsScreen.findByA11yHint('Some Label')).resolves.toBeTruthy(); ``` Use cases for scoped queries include: -* queries scoped to a single item inside a FlatList containing many items -* queries scoped to a single screen in tests involving screen transitions (e.g. with react-navigation) + +- queries scoped to a single item inside a FlatList containing many items +- queries scoped to a single screen in tests involving screen transitions (e.g. with react-navigation) ## `debug` diff --git a/website/docs/Queries.md b/website/docs/Queries.md index 461e975b1..ab1465f1a 100644 --- a/website/docs/Queries.md +++ b/website/docs/Queries.md @@ -24,6 +24,19 @@ title: Queries `queryAllBy*` queries return an array of all matching nodes for a query, and return an empty array (`[]`) if no elements match. +### findBy + +`findBy` queries return a promise which resolves when a matching element is found. The promise is rejected if no elements match or if more than one match is found after a default timeout of 4500ms. If you need to find more than one element, then use `findAllBy`. + +### findAllBy + +`findAllBy` queries return a promise which resolves to an array when any matching elements are found. The promise is rejected if no elements match after a default timeout of 4500ms. + + +:::info +`findBy` and `findAllBy` queries accept optional `waitForOptions` object argument which can contain `timeout` and `interval` properies which have the same meaning as respective arguments to [`waitForElement`](https://callstack.github.io/react-native-testing-library/docs/api#waitforelement) function. +::: + ## Queries _Note: most methods like this one return a [`ReactTestInstance`](https://reactjs.org/docs/test-renderer.html#testinstance) with following properties that you may be interested in:_ @@ -39,7 +52,7 @@ type ReactTestInstance = { ### `ByText` -> getByText, getAllByText, queryByText, queryAllByText +> getByText, getAllByText, queryByText, queryAllByText, findByText, findAllByText Returns a `ReactTestInstance` with matching text – may be a string or regular expression. @@ -54,7 +67,7 @@ const element = getByText('banana'); ### `ByPlaceholder` -> getByPlaceholder, getAllByPlaceholder, queryByPlaceholder, queryAllByPlaceholder +> getByPlaceholder, getAllByPlaceholder, queryByPlaceholder, queryAllByPlaceholder, findByPlaceholder, findAllByPlaceholder Returns a `ReactTestInstance` for a `TextInput` with a matching placeholder – may be a string or regular expression. @@ -67,7 +80,7 @@ const element = getByPlaceholder('username'); ### `ByDisplayValue` -> getByDisplayValue, getAllByDisplayValue, queryByDisplayValue, queryAllByDisplayValue +> getByDisplayValue, getAllByDisplayValue, queryByDisplayValue, queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue Returns a `ReactTestInstance` for a `TextInput` with a matching display value – may be a string or regular expression. @@ -80,7 +93,7 @@ const element = getByDisplayValue('username'); ### `ByTestId` -> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId +> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId, findByTestId, findAllByTestId Returns a `ReactTestInstance` with matching `testID` prop. @@ -101,8 +114,8 @@ Current implementation of `getByTestId` and `queryByTestId` has a serious flaw, ### `ByA11yLabel`, `ByAccessibilityLabel` -> getByA11yLabel, getAllByA11yLabel, queryByA11yLabel, queryAllByA11yLabel -> getByAccessibilityLabel, getAllByAccessibilityLabel, queryByAccessibilityLabel, queryAllByAccessibilityLabel +> getByA11yLabel, getAllByA11yLabel, queryByA11yLabel, queryAllByA11yLabel, findByA11yLabel, findAllByA11yLabel +> getByAccessibilityLabel, getAllByAccessibilityLabel, queryByAccessibilityLabel, queryAllByAccessibilityLabel, findByAccessibilityLabel, findAllByAccessibilityLabel Returns a `ReactTestInstance` with matching `accessibilityLabel` prop. @@ -115,8 +128,8 @@ const element = getByA11yLabel('my-label'); ### `ByA11yHint`, `ByAccessibilityHint` -> getByA11yHint, getAllByA11yHint, queryByA11yHint, queryAllByA11yHint -> getByAccessibilityHint, getAllByAccessibilityHint, queryByAccessibilityHint, queryAllByAccessibilityHint +> getByA11yHint, getAllByA11yHint, queryByA11yHint, queryAllByA11yHint, findByA11yHint, findAllByA11yHint +> getByAccessibilityHint, getAllByAccessibilityHint, queryByAccessibilityHint, queryAllByAccessibilityHint, findByAccessibilityHint, findAllByAccessibilityHint Returns a `ReactTestInstance` with matching `accessibilityHint` prop. @@ -144,8 +157,8 @@ const element2 = getByA11yStates('checked'); ### `ByA11yRole`, `ByAccessibilityRole` -> getByA11yRole, getAllByA11yRole, queryByA11yRole, queryAllByA11yRole -> getByAccessibilityRole, getAllByAccessibilityRole, queryByAccessibilityRole, queryAllByAccessibilityRole +> getByA11yRole, getAllByA11yRole, queryByA11yRole, queryAllByA11yRole, findByA11yRole, findAllByA11yRole +> getByAccessibilityRole, getAllByAccessibilityRole, queryByAccessibilityRole, queryAllByAccessibilityRole, findByAccessibilityRole, findAllByAccessibilityRole Returns a `ReactTestInstance` with matching `accessibilityRole` prop. @@ -158,8 +171,8 @@ const element = getByA11yRole('button'); ### `ByA11yState`, `ByAccessibilityState` -> getByA11yState, getAllByA11yState, queryByA11yState, queryAllByA11yState -> getByAccessibilityState, getAllByAccessibilityState, queryByAccessibilityState, queryAllByAccessibilityState +> getByA11yState, getAllByA11yState, queryByA11yState, queryAllByA11yState, findByA11yState, findAllByA11yState +> getByAccessibilityState, getAllByAccessibilityState, queryByAccessibilityState, queryAllByAccessibilityState, findByAccessibilityState, findAllByAccessibilityState Returns a `ReactTestInstance` with matching `accessibilityState` prop. @@ -172,8 +185,8 @@ const element = getByA11yState({ disabled: true }); ### `ByA11Value`, `ByAccessibilityValue` -> getByA11yValue, getAllByA11yValue, queryByA11yValue, queryAllByA11yValue -> getByAccessibilityValue, getAllByAccessibilityValue, queryByAccessibilityValue, queryAllByAccessibilityValue +> getByA11yValue, getAllByA11yValue, queryByA11yValue, queryAllByA11yValue, findByA11yValue, findAllByA11yValue +> getByAccessibilityValue, getAllByAccessibilityValue, queryByAccessibilityValue, queryAllByAccessibilityValue, findByAccessibilityValue, findAllByAccessibilityValue Returns a `ReactTestInstance` with matching `accessibilityValue` prop.