Skip to content

feat: implement ByDisplayValue queries #190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/Queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ const { getByPlaceholder } = render(<MyComponent />);
const element = getByPlaceholder('username');
```

### `ByDisplayValue`

> getByDisplayValue, getAllByDisplayValue, queryByDisplayValue, queryAllByDisplayValue

Returns a `ReactTestInstance` for a `TextInput` with a matching display value – may be a string or regular expression.

```jsx
import { render } from 'react-native-testing-library';

const { getByDisplayValue } = render(<MyComponent />);
const element = getByDisplayValue('username');
```

### `ByTestId`

> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/__snapshots__/render.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ exports[`debug 1`] = `
rejectResponderTermination={true}
testID=\\"bananaCustomFreshness\\"
underlineColorAndroid=\\"transparent\\"
value=\\"Custom Freshie\\"
/>
<TextInput
allowFontScaling={true}
placeholder=\\"Who inspected freshness?\\"
rejectResponderTermination={true}
testID=\\"bananaChef\\"
underlineColorAndroid=\\"transparent\\"
value=\\"I inspected freshie\\"
/>
<View
accessible={true}
Expand Down Expand Up @@ -62,13 +64,15 @@ exports[`debug changing component: bananaFresh button message should now be "fre
rejectResponderTermination={true}
testID=\\"bananaCustomFreshness\\"
underlineColorAndroid=\\"transparent\\"
value=\\"Custom Freshie\\"
/>
<TextInput
allowFontScaling={true}
placeholder=\\"Who inspected freshness?\\"
rejectResponderTermination={true}
testID=\\"bananaChef\\"
underlineColorAndroid=\\"transparent\\"
value=\\"I inspected freshie\\"
/>
<View
accessible={true}
Expand Down Expand Up @@ -108,13 +112,15 @@ exports[`debug: shallow 1`] = `
rejectResponderTermination={true}
testID=\\"bananaCustomFreshness\\"
underlineColorAndroid=\\"transparent\\"
value=\\"Custom Freshie\\"
/>
<TextInput
allowFontScaling={true}
placeholder=\\"Who inspected freshness?\\"
rejectResponderTermination={true}
testID=\\"bananaChef\\"
underlineColorAndroid=\\"transparent\\"
value=\\"I inspected freshie\\"
/>
<Button
onPress={[Function anonymous]}
Expand Down Expand Up @@ -143,13 +149,15 @@ exports[`debug: shallow with message 1`] = `
rejectResponderTermination={true}
testID=\\"bananaCustomFreshness\\"
underlineColorAndroid=\\"transparent\\"
value=\\"Custom Freshie\\"
/>
<TextInput
allowFontScaling={true}
placeholder=\\"Who inspected freshness?\\"
rejectResponderTermination={true}
testID=\\"bananaChef\\"
underlineColorAndroid=\\"transparent\\"
value=\\"I inspected freshie\\"
/>
<Button
onPress={[Function anonymous]}
Expand Down Expand Up @@ -178,13 +186,15 @@ exports[`debug: with message 1`] = `
rejectResponderTermination={true}
testID=\\"bananaCustomFreshness\\"
underlineColorAndroid=\\"transparent\\"
value=\\"Custom Freshie\\"
/>
<TextInput
allowFontScaling={true}
placeholder=\\"Who inspected freshness?\\"
rejectResponderTermination={true}
testID=\\"bananaChef\\"
underlineColorAndroid=\\"transparent\\"
value=\\"I inspected freshie\\"
/>
<View
accessible={true}
Expand Down
36 changes: 35 additions & 1 deletion src/__tests__/render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type ConsoleLogMock = JestMockFn<Array<string>, void>;

const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
const PLACEHOLDER_CHEF = 'Who inspected freshness?';
const INPUT_FRESHNESS = 'Custom Freshie';
const INPUT_CHEF = 'I inspected freshie';

class Button extends React.Component<*> {
render() {
Expand Down Expand Up @@ -59,8 +61,13 @@ class Banana extends React.Component<*, *> {
<TextInput
testID="bananaCustomFreshness"
placeholder={PLACEHOLDER_FRESHNESS}
value={INPUT_FRESHNESS}
/>
<TextInput
testID="bananaChef"
placeholder={PLACEHOLDER_CHEF}
value={INPUT_CHEF}
/>
<TextInput testID="bananaChef" placeholder={PLACEHOLDER_CHEF} />
<Button onPress={this.changeFresh} type="primary">
Change freshness!
</Button>
Expand Down Expand Up @@ -208,6 +215,33 @@ test('getAllByPlaceholder, queryAllByPlaceholder', () => {
expect(queryAllByPlaceholder('no placeholder')).toHaveLength(0);
});

test('getByDisplayValue, queryByDisplayValue', () => {
const { getByDisplayValue, queryByDisplayValue } = render(<Banana />);
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('No instances found');

expect(queryByDisplayValue(/custom/i)).toBe(input);
expect(queryByDisplayValue('no value')).toBeNull();
expect(() => queryByDisplayValue(/fresh/i)).toThrow('Expected 1 but found 2');
});

test('getAllByDisplayValue, queryAllByDisplayValue', () => {
const { getAllByDisplayValue, queryAllByDisplayValue } = render(<Banana />);
const inputs = getAllByDisplayValue(/fresh/i);

expect(inputs).toHaveLength(2);
expect(() => getAllByDisplayValue('no value')).toThrow('No instances found');

expect(queryAllByDisplayValue(/fresh/i)).toEqual(inputs);
expect(queryAllByDisplayValue('no value')).toHaveLength(0);
});

test('getByProps, queryByProps', () => {
const { getByProps, queryByProps } = render(<Banana />);
const primaryType = getByProps({ type: 'primary' });
Expand Down
45 changes: 43 additions & 2 deletions src/helpers/getByAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const getNodeByText = (node, text) => {
try {
// eslint-disable-next-line
const { Text, TextInput } = require('react-native');
const isTextComponent =
filterNodeByType(node, Text) || filterNodeByType(node, TextInput);
const isTextComponent = filterNodeByType(node, Text);
if (isTextComponent) {
const textChildren = React.Children.map(
node.props.children,
Expand Down Expand Up @@ -54,6 +53,21 @@ const getTextInputNodeByPlaceholder = (node, placeholder) => {
}
};

const getTextInputNodeByDisplayValue = (node, value) => {
try {
// eslint-disable-next-line
const { TextInput } = require('react-native');
return (
filterNodeByType(node, TextInput) &&
(typeof value === 'string'
? value === node.props.value
: value.test(node.props.value))
);
} catch (error) {
throw createLibraryNotSupportedError(error);
}
};

export const getByName = (instance: ReactTestInstance) =>
function getByNameFn(name: string | React.ComponentType<*>) {
logDeprecationWarning('getByName', 'getByType');
Expand Down Expand Up @@ -95,6 +109,17 @@ export const getByPlaceholder = (instance: ReactTestInstance) =>
}
};

export const getByDisplayValue = (instance: ReactTestInstance) =>
function getByDisplayValueFn(placeholder: string | RegExp) {
try {
return instance.find(node =>
getTextInputNodeByDisplayValue(node, placeholder)
);
} catch (error) {
throw new ErrorWithStack(prepareErrorMessage(error), getByDisplayValueFn);
}
};

export const getByProps = (instance: ReactTestInstance) =>
function getByPropsFn(props: { [propName: string]: any }) {
try {
Expand Down Expand Up @@ -161,6 +186,20 @@ export const getAllByPlaceholder = (instance: ReactTestInstance) =>
return results;
};

export const getAllByDisplayValue = (instance: ReactTestInstance) =>
function getAllByDisplayValueFn(value: string | RegExp) {
const results = instance.findAll(node =>
getTextInputNodeByDisplayValue(node, value)
);
if (results.length === 0) {
throw new ErrorWithStack(
`No instances found with display value: ${String(value)}`,
getAllByDisplayValueFn
);
}
return results;
};

export const getAllByProps = (instance: ReactTestInstance) =>
function getAllByPropsFn(props: { [propName: string]: any }) {
const results = instance.findAllByProps(props);
Expand All @@ -179,10 +218,12 @@ export const getByAPI = (instance: ReactTestInstance) => ({
getByType: getByType(instance),
getByText: getByText(instance),
getByPlaceholder: getByPlaceholder(instance),
getByDisplayValue: getByDisplayValue(instance),
getByProps: getByProps(instance),
getAllByName: getAllByName(instance),
getAllByType: getAllByType(instance),
getAllByText: getAllByText(instance),
getAllByPlaceholder: getAllByPlaceholder(instance),
getAllByDisplayValue: getAllByDisplayValue(instance),
getAllByProps: getAllByProps(instance),
});
25 changes: 24 additions & 1 deletion src/helpers/queryByAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
getByType,
getByText,
getByPlaceholder,
getByDisplayValue,
getByProps,
getAllByName,
getAllByType,
getAllByText,
getAllByPlaceholder,
getAllByDisplayValue,
getAllByProps,
} from './getByAPI';
import { logDeprecationWarning, createQueryByError } from './errors';
Expand Down Expand Up @@ -48,7 +50,16 @@ export const queryByPlaceholder = (instance: ReactTestInstance) =>
try {
return getByPlaceholder(instance)(placeholder);
} catch (error) {
return createQueryByError(error, queryByPlaceholder);
return createQueryByError(error, queryByPlaceholderFn);
}
};

export const queryByDisplayValue = (instance: ReactTestInstance) =>
function queryByDisplayValueFn(value: string | RegExp) {
try {
return getByDisplayValue(instance)(value);
} catch (error) {
return createQueryByError(error, queryByDisplayValueFn);
}
};

Expand Down Expand Up @@ -111,6 +122,16 @@ export const queryAllByPlaceholder = (instance: ReactTestInstance) => (
}
};

export const queryAllByDisplayValue = (instance: ReactTestInstance) => (
value: string | RegExp
) => {
try {
return getAllByDisplayValue(instance)(value);
} catch (error) {
return [];
}
};

export const queryAllByProps = (instance: ReactTestInstance) => (props: {
[propName: string]: any,
}) => {
Expand All @@ -127,10 +148,12 @@ export const queryByAPI = (instance: ReactTestInstance) => ({
queryByType: queryByType(instance),
queryByText: queryByText(instance),
queryByPlaceholder: queryByPlaceholder(instance),
queryByDisplayValue: queryByDisplayValue(instance),
queryByProps: queryByProps(instance),
queryAllByName: queryAllByName(instance),
queryAllByType: queryAllByType(instance),
queryAllByText: queryAllByText(instance),
queryAllByPlaceholder: queryAllByPlaceholder(instance),
queryAllByDisplayValue: queryAllByDisplayValue(instance),
queryAllByProps: queryAllByProps(instance),
});
26 changes: 21 additions & 5 deletions typings/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const getByPlaceholderString: ReactTestInstance = tree.getByPlaceholder(
const getByPlaceholderRegExp: ReactTestInstance = tree.getByPlaceholder(
/placeholder/g
);
const getByDisplayValueString: ReactTestInstance = tree.getByDisplayValue(
'my value'
);
const getByDisplayValueRegExp: ReactTestInstance = tree.getByDisplayValue(
/value/g
);
const getByProps: ReactTestInstance = tree.getByProps({ value: 2 });
const getByTestId: ReactTestInstance = tree.getByTestId('test-id');
const getAllByNameString: Array<ReactTestInstance> = tree.getAllByName('View');
Expand All @@ -71,16 +77,20 @@ const queryByType: ReactTestInstance | null = tree.queryByType(View);
const queryByTypeWithRequiredProps: ReactTestInstance | null = tree.queryByType(
ElementWithRequiredProps
);
const queryByTextString: ReactTestInstance | null = tree.queryByText(
'<View />'
);
const queryByTextString: ReactTestInstance | null = tree.queryByText('View');
const queryByTextRegExp: ReactTestInstance | null = tree.queryByText(/View/g);
const queryByPlaceholderString: ReactTestInstance | null = tree.queryByText(
const queryByPlaceholderString: ReactTestInstance | null = tree.queryByPlaceholder(
'my placeholder'
);
const queryByPlaceholderRegExp: ReactTestInstance | null = tree.queryByText(
const queryByPlaceholderRegExp: ReactTestInstance | null = tree.queryByPlaceholder(
/placeholder/g
);
const queryByDisplayValueString: ReactTestInstance | null = tree.queryByDisplayValue(
'my value'
);
const queryByDisplayValueRegExp: ReactTestInstance | null = tree.queryByDisplayValue(
/value/g
);
const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 });
const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id');
const queryAllByNameString: Array<ReactTestInstance> = tree.queryAllByName(
Expand All @@ -99,6 +109,12 @@ const queryAllByTextString: Array<ReactTestInstance> = tree.queryAllByText(
const queryAllByTextRegExp: Array<ReactTestInstance> = tree.queryAllByText(
/View/g
);
const queryAllByDisplayValueString: Array<
ReactTestInstance
> = tree.queryAllByDisplayValue('View');
const queryAllByDisplayValueRegExp: Array<
ReactTestInstance
> = tree.queryAllByDisplayValue(/View/g);

// Accessibility queries
const getByA11yLabel: ReactTestInstance = tree.getByA11yLabel('label');
Expand Down
6 changes: 6 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface GetByAPI {
getByType: <P>(type: React.ComponentType<P>) => ReactTestInstance;
getByText: (text: string | RegExp) => ReactTestInstance;
getByPlaceholder: (placeholder: string | RegExp) => ReactTestInstance;
getByDisplayValue: (value: string | RegExp) => ReactTestInstance;
getByProps: (props: Record<string, any>) => ReactTestInstance;
getByTestId: (testID: string) => ReactTestInstance;
getAllByName: (name: React.ReactType | string) => Array<ReactTestInstance>;
Expand All @@ -14,6 +15,7 @@ export interface GetByAPI {
getAllByPlaceholder: (
placeholder: string | RegExp
) => Array<ReactTestInstance>;
getAllByDisplayValue: (value: string | RegExp) => Array<ReactTestInstance>;
getAllByProps: (props: Record<string, any>) => Array<ReactTestInstance>;
}

Expand All @@ -24,6 +26,7 @@ export interface QueryByAPI {
queryByPlaceholder: (
placeholder: string | RegExp
) => ReactTestInstance | null;
queryByDisplayValue: (value: string | RegExp) => ReactTestInstance | null;
queryByProps: (props: Record<string, any>) => ReactTestInstance | null;
queryByTestId: (testID: string) => ReactTestInstance | null;
queryAllByName: (
Expand All @@ -36,6 +39,9 @@ export interface QueryByAPI {
queryAllByPlaceholder: (
placeholder: string | RegExp
) => Array<ReactTestInstance> | [];
queryAllByDisplayValue: (
value: string | RegExp
) => Array<ReactTestInstance> | [];
queryAllByProps: (
props: Record<string, any>
) => Array<ReactTestInstance> | [];
Expand Down