2018-10-06 23:42:52 +02:00
|
|
|
// @flow
|
|
|
|
|
import * as React from 'react';
|
2018-11-17 19:13:44 +01:00
|
|
|
import prettyFormat from 'pretty-format';
|
2018-11-19 15:16:33 +01:00
|
|
|
import {
|
|
|
|
|
ErrorWithStack,
|
|
|
|
|
createLibraryNotSupportedError,
|
2019-05-28 20:47:06 +02:00
|
|
|
prepareErrorMessage,
|
2020-05-19 16:18:05 +02:00
|
|
|
throwRemovedFunctionError,
|
2020-07-24 22:53:34 +02:00
|
|
|
throwRenamedFunctionError,
|
2018-11-19 15:16:33 +01:00
|
|
|
} from './errors';
|
2021-01-13 12:28:47 +01:00
|
|
|
import { getAllByTestId, getByTestId } from './byTestId';
|
2018-10-06 23:42:52 +02:00
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export type GetByAPI = {|
|
|
|
|
|
getByText: (text: string | RegExp) => ReactTestInstance,
|
|
|
|
|
getByPlaceholderText: (placeholder: string | RegExp) => ReactTestInstance,
|
|
|
|
|
getByDisplayValue: (value: string | RegExp) => ReactTestInstance,
|
|
|
|
|
getByTestId: (testID: string | RegExp) => ReactTestInstance,
|
|
|
|
|
getAllByTestId: (testID: string | RegExp) => Array<ReactTestInstance>,
|
|
|
|
|
getAllByText: (text: string | RegExp) => Array<ReactTestInstance>,
|
|
|
|
|
getAllByPlaceholderText: (
|
|
|
|
|
placeholder: string | RegExp
|
|
|
|
|
) => Array<ReactTestInstance>,
|
|
|
|
|
getAllByDisplayValue: (value: string | RegExp) => Array<ReactTestInstance>,
|
|
|
|
|
|
|
|
|
|
// Unsafe aliases
|
|
|
|
|
UNSAFE_getByType: <P>(type: React.ComponentType<P>) => ReactTestInstance,
|
|
|
|
|
UNSAFE_getAllByType: <P>(
|
|
|
|
|
type: React.ComponentType<P>
|
|
|
|
|
) => Array<ReactTestInstance>,
|
|
|
|
|
UNSAFE_getByProps: (props: { [string]: any }) => ReactTestInstance,
|
|
|
|
|
UNSAFE_getAllByProps: (props: { [string]: any }) => Array<ReactTestInstance>,
|
|
|
|
|
|
|
|
|
|
getByName: () => void,
|
|
|
|
|
getByType: () => void,
|
|
|
|
|
getByProps: () => void,
|
|
|
|
|
getAllByName: () => void,
|
|
|
|
|
getAllByType: () => void,
|
|
|
|
|
getAllByProps: () => void,
|
|
|
|
|
|
|
|
|
|
getByPlaceholder: () => void,
|
|
|
|
|
getAllByPlaceholder: () => void,
|
|
|
|
|
|};
|
|
|
|
|
|
2018-11-17 19:13:44 +01:00
|
|
|
const filterNodeByType = (node, type) => node.type === type;
|
2018-10-06 23:42:52 +02:00
|
|
|
|
2018-11-17 19:13:44 +01:00
|
|
|
const getNodeByText = (node, text) => {
|
|
|
|
|
try {
|
2020-05-01 12:54:12 +01:00
|
|
|
const { Text } = require('react-native');
|
2019-06-10 17:30:27 +02:00
|
|
|
const isTextComponent = filterNodeByType(node, Text);
|
2019-03-18 11:25:13 -04:00
|
|
|
if (isTextComponent) {
|
2020-05-11 19:13:15 +02:00
|
|
|
const textChildren = getChildrenAsText(node.props.children, Text);
|
2019-03-18 11:25:13 -04:00
|
|
|
if (textChildren) {
|
|
|
|
|
const textToTest = textChildren.join('');
|
|
|
|
|
return typeof text === 'string'
|
|
|
|
|
? text === textToTest
|
|
|
|
|
: text.test(textToTest);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2018-11-17 19:13:44 +01:00
|
|
|
} catch (error) {
|
|
|
|
|
throw createLibraryNotSupportedError(error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-11 19:13:15 +02:00
|
|
|
const getChildrenAsText = (children, TextComponent, textContent = []) => {
|
2020-05-15 10:10:49 +02:00
|
|
|
React.Children.forEach(children, (child) => {
|
2020-05-01 12:54:12 +01:00
|
|
|
if (typeof child === 'string') {
|
2020-05-11 19:13:15 +02:00
|
|
|
textContent.push(child);
|
|
|
|
|
return;
|
2020-05-01 12:54:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof child === 'number') {
|
2020-05-11 19:13:15 +02:00
|
|
|
textContent.push(child.toString());
|
|
|
|
|
return;
|
2020-05-01 12:54:12 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-11 19:13:15 +02:00
|
|
|
if (child?.props?.children) {
|
2020-08-16 21:26:29 +02:00
|
|
|
// Bail on traversing text children down the tree if current node (child)
|
|
|
|
|
// has no text. In such situations, react-test-renderer will traverse down
|
|
|
|
|
// this tree in a separate call and run this query again. As a result, the
|
|
|
|
|
// query will match the deepest text node that matches requested text.
|
|
|
|
|
if (filterNodeByType(child, TextComponent) && textContent.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-11 19:13:15 +02:00
|
|
|
getChildrenAsText(child.props.children, TextComponent, textContent);
|
|
|
|
|
}
|
2020-05-01 12:54:12 +01:00
|
|
|
});
|
2020-05-11 19:13:15 +02:00
|
|
|
|
|
|
|
|
return textContent;
|
2020-05-01 12:54:12 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-24 22:53:34 +02:00
|
|
|
const getTextInputNodeByPlaceholderText = (node, placeholder) => {
|
2018-12-22 17:09:28 -05:00
|
|
|
try {
|
|
|
|
|
const { TextInput } = require('react-native');
|
|
|
|
|
return (
|
|
|
|
|
filterNodeByType(node, TextInput) &&
|
|
|
|
|
(typeof placeholder === 'string'
|
|
|
|
|
? placeholder === node.props.placeholder
|
|
|
|
|
: placeholder.test(node.props.placeholder))
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw createLibraryNotSupportedError(error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-10 17:30:27 +02:00
|
|
|
const getTextInputNodeByDisplayValue = (node, value) => {
|
|
|
|
|
try {
|
|
|
|
|
const { TextInput } = require('react-native');
|
2021-01-25 14:12:48 +01:00
|
|
|
const nodeValue =
|
|
|
|
|
node.props.value !== undefined
|
|
|
|
|
? node.props.value
|
|
|
|
|
: node.props.defaultValue;
|
2019-06-10 17:30:27 +02:00
|
|
|
return (
|
|
|
|
|
filterNodeByType(node, TextInput) &&
|
2021-01-25 14:12:48 +01:00
|
|
|
(typeof value === 'string' ? value === nodeValue : value.test(nodeValue))
|
2019-06-10 17:30:27 +02:00
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw createLibraryNotSupportedError(error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getByText = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((text: string | RegExp) => ReactTestInstance) =>
|
2018-10-07 17:10:26 +02:00
|
|
|
function getByTextFn(text: string | RegExp) {
|
|
|
|
|
try {
|
2020-05-15 10:10:49 +02:00
|
|
|
return instance.find((node) => getNodeByText(node, text));
|
2018-10-07 17:10:26 +02:00
|
|
|
} catch (error) {
|
2020-10-12 12:01:42 -04:00
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
prepareErrorMessage(error, 'text', text),
|
|
|
|
|
getByTextFn
|
|
|
|
|
);
|
2018-10-07 17:10:26 +02:00
|
|
|
}
|
|
|
|
|
};
|
2018-10-06 23:42:52 +02:00
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getByPlaceholderText = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((placeholder: string | RegExp) => ReactTestInstance) =>
|
2020-07-24 22:53:34 +02:00
|
|
|
function getByPlaceholderTextFn(placeholder: string | RegExp) {
|
2018-12-22 17:09:28 -05:00
|
|
|
try {
|
2020-05-15 10:10:49 +02:00
|
|
|
return instance.find((node) =>
|
2020-07-24 22:53:34 +02:00
|
|
|
getTextInputNodeByPlaceholderText(node, placeholder)
|
2018-12-22 17:09:28 -05:00
|
|
|
);
|
|
|
|
|
} catch (error) {
|
2020-07-24 22:53:34 +02:00
|
|
|
throw new ErrorWithStack(
|
2020-10-12 12:01:42 -04:00
|
|
|
prepareErrorMessage(error, 'placeholder', placeholder),
|
2020-07-24 22:53:34 +02:00
|
|
|
getByPlaceholderTextFn
|
|
|
|
|
);
|
2018-12-22 17:09:28 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getByDisplayValue = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((displayValue: string | RegExp) => ReactTestInstance) =>
|
2020-10-12 12:01:42 -04:00
|
|
|
function getByDisplayValueFn(displayValue: string | RegExp) {
|
2019-06-10 17:30:27 +02:00
|
|
|
try {
|
2020-05-15 10:10:49 +02:00
|
|
|
return instance.find((node) =>
|
2020-10-12 12:01:42 -04:00
|
|
|
getTextInputNodeByDisplayValue(node, displayValue)
|
2019-06-10 17:30:27 +02:00
|
|
|
);
|
|
|
|
|
} catch (error) {
|
2020-10-12 12:01:42 -04:00
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
prepareErrorMessage(error, 'display value', displayValue),
|
|
|
|
|
getByDisplayValueFn
|
|
|
|
|
);
|
2019-06-10 17:30:27 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getAllByText = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((text: string | RegExp) => Array<ReactTestInstance>) =>
|
2018-10-07 17:10:26 +02:00
|
|
|
function getAllByTextFn(text: string | RegExp) {
|
2020-05-15 10:10:49 +02:00
|
|
|
const results = instance.findAll((node) => getNodeByText(node, text));
|
2018-10-07 17:10:26 +02:00
|
|
|
if (results.length === 0) {
|
2018-11-17 19:13:44 +01:00
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
`No instances found with text: ${String(text)}`,
|
|
|
|
|
getAllByTextFn
|
|
|
|
|
);
|
2018-10-07 17:10:26 +02:00
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
2018-10-06 23:42:52 +02:00
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getAllByPlaceholderText = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((placeholder: string | RegExp) => Array<ReactTestInstance>) =>
|
2020-07-24 22:53:34 +02:00
|
|
|
function getAllByPlaceholderTextFn(placeholder: string | RegExp) {
|
2020-05-15 10:10:49 +02:00
|
|
|
const results = instance.findAll((node) =>
|
2020-07-24 22:53:34 +02:00
|
|
|
getTextInputNodeByPlaceholderText(node, placeholder)
|
2018-12-22 17:09:28 -05:00
|
|
|
);
|
|
|
|
|
if (results.length === 0) {
|
|
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
`No instances found with placeholder: ${String(placeholder)}`,
|
2020-07-24 22:53:34 +02:00
|
|
|
getAllByPlaceholderTextFn
|
2018-12-22 17:09:28 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getAllByDisplayValue = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((value: string | RegExp) => Array<ReactTestInstance>) =>
|
2019-06-10 17:30:27 +02:00
|
|
|
function getAllByDisplayValueFn(value: string | RegExp) {
|
2020-05-15 10:10:49 +02:00
|
|
|
const results = instance.findAll((node) =>
|
2019-06-10 17:30:27 +02:00
|
|
|
getTextInputNodeByDisplayValue(node, value)
|
|
|
|
|
);
|
|
|
|
|
if (results.length === 0) {
|
|
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
`No instances found with display value: ${String(value)}`,
|
|
|
|
|
getAllByDisplayValueFn
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
2019-07-03 01:17:32 -07:00
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const UNSAFE_getByType = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((type: React.ComponentType<any>) => ReactTestInstance) =>
|
2020-05-19 16:18:05 +02:00
|
|
|
function getByTypeFn(type: React.ComponentType<any>) {
|
|
|
|
|
try {
|
|
|
|
|
return instance.findByType(type);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new ErrorWithStack(prepareErrorMessage(error), getByTypeFn);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const UNSAFE_getByProps = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((props: { [propName: string]: any }) => ReactTestInstance) =>
|
2020-05-19 16:18:05 +02:00
|
|
|
function getByPropsFn(props: { [propName: string]: any }) {
|
|
|
|
|
try {
|
|
|
|
|
return instance.findByProps(props);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new ErrorWithStack(prepareErrorMessage(error), getByPropsFn);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const UNSAFE_getAllByType = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((type: React.ComponentType<any>) => Array<ReactTestInstance>) =>
|
2020-05-19 16:18:05 +02:00
|
|
|
function getAllByTypeFn(type: React.ComponentType<any>) {
|
|
|
|
|
const results = instance.findAllByType(type);
|
|
|
|
|
if (results.length === 0) {
|
|
|
|
|
throw new ErrorWithStack('No instances found', getAllByTypeFn);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const UNSAFE_getAllByProps = (
|
|
|
|
|
instance: ReactTestInstance
|
|
|
|
|
): ((props: { [propName: string]: any }) => Array<ReactTestInstance>) =>
|
2020-05-19 16:18:05 +02:00
|
|
|
function getAllByPropsFn(props: { [propName: string]: any }) {
|
|
|
|
|
const results = instance.findAllByProps(props);
|
|
|
|
|
if (results.length === 0) {
|
|
|
|
|
throw new ErrorWithStack(
|
|
|
|
|
`No instances found with props:\n${prettyFormat(props)}`,
|
|
|
|
|
getAllByPropsFn
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-09 17:49:39 +01:00
|
|
|
export const getByAPI = (instance: ReactTestInstance): GetByAPI => ({
|
2018-10-07 10:59:27 +02:00
|
|
|
getByText: getByText(instance),
|
2020-07-24 22:53:34 +02:00
|
|
|
getByPlaceholderText: getByPlaceholderText(instance),
|
2019-06-10 17:30:27 +02:00
|
|
|
getByDisplayValue: getByDisplayValue(instance),
|
2020-05-19 16:18:05 +02:00
|
|
|
getByTestId: getByTestId(instance),
|
2018-10-07 10:59:27 +02:00
|
|
|
getAllByText: getAllByText(instance),
|
2020-07-24 22:53:34 +02:00
|
|
|
getAllByPlaceholderText: getAllByPlaceholderText(instance),
|
2019-06-10 17:30:27 +02:00
|
|
|
getAllByDisplayValue: getAllByDisplayValue(instance),
|
2020-05-19 16:18:05 +02:00
|
|
|
getAllByTestId: getAllByTestId(instance),
|
|
|
|
|
|
|
|
|
|
// Unsafe
|
|
|
|
|
UNSAFE_getByType: UNSAFE_getByType(instance),
|
|
|
|
|
UNSAFE_getAllByType: UNSAFE_getAllByType(instance),
|
|
|
|
|
UNSAFE_getByProps: UNSAFE_getByProps(instance),
|
|
|
|
|
UNSAFE_getAllByProps: UNSAFE_getAllByProps(instance),
|
2020-03-18 10:30:41 +01:00
|
|
|
|
2020-05-19 16:18:05 +02:00
|
|
|
// Removed
|
|
|
|
|
getByName: () =>
|
|
|
|
|
throwRemovedFunctionError('getByName', 'migration-v2#removed-functions'),
|
|
|
|
|
getAllByName: () =>
|
|
|
|
|
throwRemovedFunctionError('getAllByName', 'migration-v2#removed-functions'),
|
|
|
|
|
getByType: () =>
|
|
|
|
|
throwRemovedFunctionError('getByType', 'migration-v2#removed-functions'),
|
|
|
|
|
getAllByType: () =>
|
|
|
|
|
throwRemovedFunctionError('getAllByType', 'migration-v2#removed-functions'),
|
|
|
|
|
getByProps: () =>
|
|
|
|
|
throwRemovedFunctionError('getByProps', 'migration-v2#removed-functions'),
|
|
|
|
|
getAllByProps: () =>
|
|
|
|
|
throwRemovedFunctionError(
|
|
|
|
|
'getAllByProps',
|
|
|
|
|
'migration-v2#removed-functions'
|
|
|
|
|
),
|
2020-07-24 22:53:34 +02:00
|
|
|
|
|
|
|
|
// Renamed
|
|
|
|
|
getByPlaceholder: () =>
|
|
|
|
|
throwRenamedFunctionError('getByPlaceholder', 'getByPlaceholderText'),
|
|
|
|
|
getAllByPlaceholder: () =>
|
|
|
|
|
throwRenamedFunctionError('getAllByPlaceholder', 'getByPlaceholderText'),
|
2018-10-07 10:59:27 +02:00
|
|
|
});
|