feat: add getAllByTestId and queryAllByTestId queries (#198)

* feat: add getAllByTestId and queryAllByTestId queries

* Add test for getAllByTestId and queryAllByTestId

- Filter out intermediate components in getAllByTestId. testIDs may be passed down to child components and appearing multiple times in the render tree. Some React Native components render multiple anyways. <Text> is an example.

* Update TypeScript typings

* Fix flow types

* Update TS typings test
This commit is contained in:
Scotty Waggoner
2019-07-03 01:17:32 -07:00
committed by Michał Pierzchała
parent 9369efee73
commit fead652013
6 changed files with 106 additions and 1 deletions
@@ -45,6 +45,16 @@ exports[`debug 1`] = `
Change freshness! Change freshness!
</Text> </Text>
</View> </View>
<Text
testID=\\"duplicateText\\"
>
First Text
</Text>
<Text
testID=\\"duplicateText\\"
>
Second Text
</Text>
</View>" </View>"
`; `;
@@ -93,6 +103,16 @@ exports[`debug changing component: bananaFresh button message should now be "fre
Change freshness! Change freshness!
</Text> </Text>
</View> </View>
<Text
testID=\\"duplicateText\\"
>
First Text
</Text>
<Text
testID=\\"duplicateText\\"
>
Second Text
</Text>
</View>" </View>"
`; `;
@@ -128,6 +148,16 @@ exports[`debug: shallow 1`] = `
> >
Change freshness! Change freshness!
</Button> </Button>
<Text
testID=\\"duplicateText\\"
>
First Text
</Text>
<Text
testID=\\"duplicateText\\"
>
Second Text
</Text>
</View>" </View>"
`; `;
@@ -165,6 +195,16 @@ exports[`debug: shallow with message 1`] = `
> >
Change freshness! Change freshness!
</Button> </Button>
<Text
testID=\\"duplicateText\\"
>
First Text
</Text>
<Text
testID=\\"duplicateText\\"
>
Second Text
</Text>
</View>" </View>"
`; `;
@@ -215,6 +255,16 @@ exports[`debug: with message 1`] = `
Change freshness! Change freshness!
</Text> </Text>
</View> </View>
<Text
testID=\\"duplicateText\\"
>
First Text
</Text>
<Text
testID=\\"duplicateText\\"
>
Second Text
</Text>
</View>" </View>"
`; `;
+22 -1
View File
@@ -71,6 +71,8 @@ class Banana extends React.Component<*, *> {
<Button onPress={this.changeFresh} type="primary"> <Button onPress={this.changeFresh} type="primary">
Change freshness! Change freshness!
</Button> </Button>
<Text testID="duplicateText">First Text</Text>
<Text testID="duplicateText">Second Text</Text>
</View> </View>
); );
} }
@@ -87,6 +89,25 @@ test('getByTestId, queryByTestId', () => {
expect(queryByTestId('InExistent')).toBeNull(); expect(queryByTestId('InExistent')).toBeNull();
}); });
test('getAllByTestId, queryAllByTestId', () => {
const { getAllByTestId, queryAllByTestId } = render(<Banana />);
const textElements = getAllByTestId('duplicateText');
expect(textElements.length).toBe(2);
expect(textElements[0].props.children).toBe('First Text');
expect(textElements[1].props.children).toBe('Second Text');
expect(() => getAllByTestId('nonExistentTestId')).toThrow(
'No instances found'
);
const queriedTextElements = queryAllByTestId('duplicateText');
expect(queriedTextElements.length).toBe(2);
expect(queriedTextElements[0]).toBe(textElements[0]);
expect(queriedTextElements[1]).toBe(textElements[1]);
expect(queryAllByTestId('nonExistentTestId')).toHaveLength(0);
});
test('getByName, queryByName', () => { test('getByName, queryByName', () => {
const { getByTestId, getByName, queryByName } = render(<Banana />); const { getByTestId, getByName, queryByName } = render(<Banana />);
const bananaFresh = getByTestId('bananaFresh'); const bananaFresh = getByTestId('bananaFresh');
@@ -101,7 +122,7 @@ test('getByName, queryByName', () => {
expect(bananaFresh.props.children).toBe('not fresh'); expect(bananaFresh.props.children).toBe('not fresh');
expect(() => getByName('InExistent')).toThrow('No instances found'); expect(() => getByName('InExistent')).toThrow('No instances found');
expect(() => getByName(Text)).toThrow('Expected 1 but found 3'); expect(() => getByName(Text)).toThrow('Expected 1 but found 5');
expect(queryByName('Button')).toBe(button); expect(queryByName('Button')).toBe(button);
expect(queryByName('InExistent')).toBeNull(); expect(queryByName('InExistent')).toBeNull();
+16
View File
@@ -212,6 +212,21 @@ export const getAllByProps = (instance: ReactTestInstance) =>
return results; return results;
}; };
export const getAllByTestId = (instance: ReactTestInstance) =>
function getAllByTestIdFn(testID: string): ReactTestInstance[] {
const results = instance
.findAllByProps({ testID })
.filter(element => typeof element.type === 'string');
if (results.length === 0) {
throw new ErrorWithStack(
`No instances found with testID: ${String(testID)}`,
getAllByTestIdFn
);
}
return results;
};
export const getByAPI = (instance: ReactTestInstance) => ({ export const getByAPI = (instance: ReactTestInstance) => ({
getByTestId: getByTestId(instance), getByTestId: getByTestId(instance),
getByName: getByName(instance), getByName: getByName(instance),
@@ -220,6 +235,7 @@ export const getByAPI = (instance: ReactTestInstance) => ({
getByPlaceholder: getByPlaceholder(instance), getByPlaceholder: getByPlaceholder(instance),
getByDisplayValue: getByDisplayValue(instance), getByDisplayValue: getByDisplayValue(instance),
getByProps: getByProps(instance), getByProps: getByProps(instance),
getAllByTestId: getAllByTestId(instance),
getAllByName: getAllByName(instance), getAllByName: getAllByName(instance),
getAllByType: getAllByType(instance), getAllByType: getAllByType(instance),
getAllByText: getAllByText(instance), getAllByText: getAllByText(instance),
+12
View File
@@ -8,6 +8,7 @@ import {
getByPlaceholder, getByPlaceholder,
getByDisplayValue, getByDisplayValue,
getByProps, getByProps,
getAllByTestId,
getAllByName, getAllByName,
getAllByType, getAllByType,
getAllByText, getAllByText,
@@ -142,6 +143,16 @@ export const queryAllByProps = (instance: ReactTestInstance) => (props: {
} }
}; };
export const queryAllByTestId = (instance: ReactTestInstance) => (
testID: string
) => {
try {
return getAllByTestId(instance)(testID);
} catch (error) {
return [];
}
};
export const queryByAPI = (instance: ReactTestInstance) => ({ export const queryByAPI = (instance: ReactTestInstance) => ({
queryByTestId: queryByTestId(instance), queryByTestId: queryByTestId(instance),
queryByName: queryByName(instance), queryByName: queryByName(instance),
@@ -150,6 +161,7 @@ export const queryByAPI = (instance: ReactTestInstance) => ({
queryByPlaceholder: queryByPlaceholder(instance), queryByPlaceholder: queryByPlaceholder(instance),
queryByDisplayValue: queryByDisplayValue(instance), queryByDisplayValue: queryByDisplayValue(instance),
queryByProps: queryByProps(instance), queryByProps: queryByProps(instance),
queryAllByTestId: queryAllByTestId(instance),
queryAllByName: queryAllByName(instance), queryAllByName: queryAllByName(instance),
queryAllByType: queryAllByType(instance), queryAllByType: queryAllByType(instance),
queryAllByText: queryAllByText(instance), queryAllByText: queryAllByText(instance),
+4
View File
@@ -54,6 +54,7 @@ const getByDisplayValueRegExp: ReactTestInstance = tree.getByDisplayValue(
); );
const getByProps: ReactTestInstance = tree.getByProps({ value: 2 }); const getByProps: ReactTestInstance = tree.getByProps({ value: 2 });
const getByTestId: ReactTestInstance = tree.getByTestId('test-id'); const getByTestId: ReactTestInstance = tree.getByTestId('test-id');
const getAllByTestId: ReactTestInstance[] = tree.getAllByTestId('test-id');
const getAllByNameString: Array<ReactTestInstance> = tree.getAllByName('View'); const getAllByNameString: Array<ReactTestInstance> = tree.getAllByName('View');
const getAllByNameConstructor: Array<ReactTestInstance> = tree.getAllByName( const getAllByNameConstructor: Array<ReactTestInstance> = tree.getAllByName(
View View
@@ -93,6 +94,9 @@ const queryByDisplayValueRegExp: ReactTestInstance | null = tree.queryByDisplayV
); );
const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 }); const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 });
const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id'); const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id');
const queryAllByTestId: ReactTestInstance[] | null = tree.queryAllByTestId(
'test-id'
);
const queryAllByNameString: Array<ReactTestInstance> = tree.queryAllByName( const queryAllByNameString: Array<ReactTestInstance> = tree.queryAllByName(
'View' 'View'
); );
+2
View File
@@ -9,6 +9,7 @@ export interface GetByAPI {
getByDisplayValue: (value: string | RegExp) => ReactTestInstance; getByDisplayValue: (value: string | RegExp) => ReactTestInstance;
getByProps: (props: Record<string, any>) => ReactTestInstance; getByProps: (props: Record<string, any>) => ReactTestInstance;
getByTestId: (testID: string) => ReactTestInstance; getByTestId: (testID: string) => ReactTestInstance;
getAllByTestId: (testID: string) => Array<ReactTestInstance>;
getAllByName: (name: React.ReactType | string) => Array<ReactTestInstance>; getAllByName: (name: React.ReactType | string) => Array<ReactTestInstance>;
getAllByType: <P>(type: React.ComponentType<P>) => Array<ReactTestInstance>; getAllByType: <P>(type: React.ComponentType<P>) => Array<ReactTestInstance>;
getAllByText: (text: string | RegExp) => Array<ReactTestInstance>; getAllByText: (text: string | RegExp) => Array<ReactTestInstance>;
@@ -29,6 +30,7 @@ export interface QueryByAPI {
queryByDisplayValue: (value: string | RegExp) => ReactTestInstance | null; queryByDisplayValue: (value: string | RegExp) => ReactTestInstance | null;
queryByProps: (props: Record<string, any>) => ReactTestInstance | null; queryByProps: (props: Record<string, any>) => ReactTestInstance | null;
queryByTestId: (testID: string) => ReactTestInstance | null; queryByTestId: (testID: string) => ReactTestInstance | null;
queryAllByTestId: (testID: string) => Array<ReactTestInstance> | null;
queryAllByName: ( queryAllByName: (
name: React.ReactType | string name: React.ReactType | string
) => Array<ReactTestInstance> | []; ) => Array<ReactTestInstance> | [];