Skip to content

Commit fead652

Browse files
OzzieOrcathymikee
authored andcommitted
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
1 parent 9369efe commit fead652

File tree

6 files changed

+106
-1
lines changed

6 files changed

+106
-1
lines changed

src/__tests__/__snapshots__/render.test.js.snap

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ exports[`debug 1`] = `
4545
Change freshness!
4646
</Text>
4747
</View>
48+
<Text
49+
testID=\\"duplicateText\\"
50+
>
51+
First Text
52+
</Text>
53+
<Text
54+
testID=\\"duplicateText\\"
55+
>
56+
Second Text
57+
</Text>
4858
</View>"
4959
`;
5060

@@ -93,6 +103,16 @@ exports[`debug changing component: bananaFresh button message should now be "fre
93103
Change freshness!
94104
</Text>
95105
</View>
106+
<Text
107+
testID=\\"duplicateText\\"
108+
>
109+
First Text
110+
</Text>
111+
<Text
112+
testID=\\"duplicateText\\"
113+
>
114+
Second Text
115+
</Text>
96116
</View>"
97117
`;
98118

@@ -128,6 +148,16 @@ exports[`debug: shallow 1`] = `
128148
>
129149
Change freshness!
130150
</Button>
151+
<Text
152+
testID=\\"duplicateText\\"
153+
>
154+
First Text
155+
</Text>
156+
<Text
157+
testID=\\"duplicateText\\"
158+
>
159+
Second Text
160+
</Text>
131161
</View>"
132162
`;
133163

@@ -165,6 +195,16 @@ exports[`debug: shallow with message 1`] = `
165195
>
166196
Change freshness!
167197
</Button>
198+
<Text
199+
testID=\\"duplicateText\\"
200+
>
201+
First Text
202+
</Text>
203+
<Text
204+
testID=\\"duplicateText\\"
205+
>
206+
Second Text
207+
</Text>
168208
</View>"
169209
`;
170210

@@ -215,6 +255,16 @@ exports[`debug: with message 1`] = `
215255
Change freshness!
216256
</Text>
217257
</View>
258+
<Text
259+
testID=\\"duplicateText\\"
260+
>
261+
First Text
262+
</Text>
263+
<Text
264+
testID=\\"duplicateText\\"
265+
>
266+
Second Text
267+
</Text>
218268
</View>"
219269
`;
220270

src/__tests__/render.test.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class Banana extends React.Component<*, *> {
7171
<Button onPress={this.changeFresh} type="primary">
7272
Change freshness!
7373
</Button>
74+
<Text testID="duplicateText">First Text</Text>
75+
<Text testID="duplicateText">Second Text</Text>
7476
</View>
7577
);
7678
}
@@ -87,6 +89,25 @@ test('getByTestId, queryByTestId', () => {
8789
expect(queryByTestId('InExistent')).toBeNull();
8890
});
8991

92+
test('getAllByTestId, queryAllByTestId', () => {
93+
const { getAllByTestId, queryAllByTestId } = render(<Banana />);
94+
const textElements = getAllByTestId('duplicateText');
95+
96+
expect(textElements.length).toBe(2);
97+
expect(textElements[0].props.children).toBe('First Text');
98+
expect(textElements[1].props.children).toBe('Second Text');
99+
expect(() => getAllByTestId('nonExistentTestId')).toThrow(
100+
'No instances found'
101+
);
102+
103+
const queriedTextElements = queryAllByTestId('duplicateText');
104+
105+
expect(queriedTextElements.length).toBe(2);
106+
expect(queriedTextElements[0]).toBe(textElements[0]);
107+
expect(queriedTextElements[1]).toBe(textElements[1]);
108+
expect(queryAllByTestId('nonExistentTestId')).toHaveLength(0);
109+
});
110+
90111
test('getByName, queryByName', () => {
91112
const { getByTestId, getByName, queryByName } = render(<Banana />);
92113
const bananaFresh = getByTestId('bananaFresh');
@@ -101,7 +122,7 @@ test('getByName, queryByName', () => {
101122

102123
expect(bananaFresh.props.children).toBe('not fresh');
103124
expect(() => getByName('InExistent')).toThrow('No instances found');
104-
expect(() => getByName(Text)).toThrow('Expected 1 but found 3');
125+
expect(() => getByName(Text)).toThrow('Expected 1 but found 5');
105126

106127
expect(queryByName('Button')).toBe(button);
107128
expect(queryByName('InExistent')).toBeNull();

src/helpers/getByAPI.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,21 @@ export const getAllByProps = (instance: ReactTestInstance) =>
212212
return results;
213213
};
214214

215+
export const getAllByTestId = (instance: ReactTestInstance) =>
216+
function getAllByTestIdFn(testID: string): ReactTestInstance[] {
217+
const results = instance
218+
.findAllByProps({ testID })
219+
.filter(element => typeof element.type === 'string');
220+
221+
if (results.length === 0) {
222+
throw new ErrorWithStack(
223+
`No instances found with testID: ${String(testID)}`,
224+
getAllByTestIdFn
225+
);
226+
}
227+
return results;
228+
};
229+
215230
export const getByAPI = (instance: ReactTestInstance) => ({
216231
getByTestId: getByTestId(instance),
217232
getByName: getByName(instance),
@@ -220,6 +235,7 @@ export const getByAPI = (instance: ReactTestInstance) => ({
220235
getByPlaceholder: getByPlaceholder(instance),
221236
getByDisplayValue: getByDisplayValue(instance),
222237
getByProps: getByProps(instance),
238+
getAllByTestId: getAllByTestId(instance),
223239
getAllByName: getAllByName(instance),
224240
getAllByType: getAllByType(instance),
225241
getAllByText: getAllByText(instance),

src/helpers/queryByAPI.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
getByPlaceholder,
99
getByDisplayValue,
1010
getByProps,
11+
getAllByTestId,
1112
getAllByName,
1213
getAllByType,
1314
getAllByText,
@@ -142,6 +143,16 @@ export const queryAllByProps = (instance: ReactTestInstance) => (props: {
142143
}
143144
};
144145

146+
export const queryAllByTestId = (instance: ReactTestInstance) => (
147+
testID: string
148+
) => {
149+
try {
150+
return getAllByTestId(instance)(testID);
151+
} catch (error) {
152+
return [];
153+
}
154+
};
155+
145156
export const queryByAPI = (instance: ReactTestInstance) => ({
146157
queryByTestId: queryByTestId(instance),
147158
queryByName: queryByName(instance),
@@ -150,6 +161,7 @@ export const queryByAPI = (instance: ReactTestInstance) => ({
150161
queryByPlaceholder: queryByPlaceholder(instance),
151162
queryByDisplayValue: queryByDisplayValue(instance),
152163
queryByProps: queryByProps(instance),
164+
queryAllByTestId: queryAllByTestId(instance),
153165
queryAllByName: queryAllByName(instance),
154166
queryAllByType: queryAllByType(instance),
155167
queryAllByText: queryAllByText(instance),

typings/__tests__/index.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const getByDisplayValueRegExp: ReactTestInstance = tree.getByDisplayValue(
5454
);
5555
const getByProps: ReactTestInstance = tree.getByProps({ value: 2 });
5656
const getByTestId: ReactTestInstance = tree.getByTestId('test-id');
57+
const getAllByTestId: ReactTestInstance[] = tree.getAllByTestId('test-id');
5758
const getAllByNameString: Array<ReactTestInstance> = tree.getAllByName('View');
5859
const getAllByNameConstructor: Array<ReactTestInstance> = tree.getAllByName(
5960
View
@@ -93,6 +94,9 @@ const queryByDisplayValueRegExp: ReactTestInstance | null = tree.queryByDisplayV
9394
);
9495
const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 });
9596
const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id');
97+
const queryAllByTestId: ReactTestInstance[] | null = tree.queryAllByTestId(
98+
'test-id'
99+
);
96100
const queryAllByNameString: Array<ReactTestInstance> = tree.queryAllByName(
97101
'View'
98102
);

typings/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface GetByAPI {
99
getByDisplayValue: (value: string | RegExp) => ReactTestInstance;
1010
getByProps: (props: Record<string, any>) => ReactTestInstance;
1111
getByTestId: (testID: string) => ReactTestInstance;
12+
getAllByTestId: (testID: string) => Array<ReactTestInstance>;
1213
getAllByName: (name: React.ReactType | string) => Array<ReactTestInstance>;
1314
getAllByType: <P>(type: React.ComponentType<P>) => Array<ReactTestInstance>;
1415
getAllByText: (text: string | RegExp) => Array<ReactTestInstance>;
@@ -29,6 +30,7 @@ export interface QueryByAPI {
2930
queryByDisplayValue: (value: string | RegExp) => ReactTestInstance | null;
3031
queryByProps: (props: Record<string, any>) => ReactTestInstance | null;
3132
queryByTestId: (testID: string) => ReactTestInstance | null;
33+
queryAllByTestId: (testID: string) => Array<ReactTestInstance> | null;
3234
queryAllByName: (
3335
name: React.ReactType | string
3436
) => Array<ReactTestInstance> | [];

0 commit comments

Comments
 (0)