Skip to content

Commit ab34712

Browse files
feat: add findBy* queries (#304)
* Basic async findBy queries * Refactored findBy queries to be built with makeFindQuery * findBy queries for A11y selectors * Custom findByTestId implementation that avoids current getByTestId issues * Fixed prettier issue * Updates Queries.md with findBy queries * Trying to fix test timeout error appearing only on CI * Code review changes * Added typescript types & tests * prettier did reformat some code arround * Removed export of GetByAPI, QueryByAPI and FindByAPI interfaces in TS * Reversed prettier run on typings * Added overlapping pieces with `within` operator * Small cleanup * Update website/docs/Queries.md * Moved async/await tests to the end of test methods Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
1 parent 1809825 commit ab34712

File tree

13 files changed

+609
-162
lines changed

13 files changed

+609
-162
lines changed

src/__tests__/a11yAPI.test.js

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const Typography = ({ children, ...rest }: any) => {
1818
return <Text {...rest}>{children}</Text>;
1919
};
2020

21+
const waitForOptions = { timeout: 10 };
22+
2123
class Button extends React.Component<any> {
2224
render() {
2325
return (
@@ -62,74 +64,133 @@ function Section() {
6264
);
6365
}
6466

65-
test('getByA11yLabel, queryByA11yLabel', () => {
66-
const { getByA11yLabel, queryByA11yLabel } = render(<Section />);
67+
test('getByA11yLabel, queryByA11yLabel, findByA11yLabel', async () => {
68+
const { getByA11yLabel, queryByA11yLabel, findByA11yLabel } = render(
69+
<Section />
70+
);
6771

6872
expect(getByA11yLabel(BUTTON_LABEL).props.accessibilityLabel).toEqual(
6973
BUTTON_LABEL
7074
);
7175
const button = queryByA11yLabel(/button/g);
7276
expect(button && button.props.accessibilityLabel).toEqual(BUTTON_LABEL);
77+
7378
expect(() => getByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
7479
expect(queryByA11yLabel(NO_MATCHES_TEXT)).toBeNull();
7580

7681
expect(() => getByA11yLabel(TEXT_LABEL)).toThrow(FOUND_TWO_INSTANCES);
7782
expect(() => queryByA11yLabel(TEXT_LABEL)).toThrow(FOUND_TWO_INSTANCES);
83+
84+
const asyncButton = await findByA11yLabel(BUTTON_LABEL);
85+
expect(asyncButton.props.accessibilityLabel).toEqual(BUTTON_LABEL);
86+
await expect(
87+
findByA11yLabel(NO_MATCHES_TEXT, waitForOptions)
88+
).rejects.toThrow(NO_INSTANCES_FOUND);
89+
90+
await expect(findByA11yLabel(TEXT_LABEL, waitForOptions)).rejects.toThrow(
91+
FOUND_TWO_INSTANCES
92+
);
7893
});
7994

80-
test('getAllByA11yLabel, queryAllByA11yLabel', () => {
81-
const { getAllByA11yLabel, queryAllByA11yLabel } = render(<Section />);
95+
test('getAllByA11yLabel, queryAllByA11yLabel', async () => {
96+
const { getAllByA11yLabel, queryAllByA11yLabel, findAllByA11yLabel } = render(
97+
<Section />
98+
);
8299

83100
expect(getAllByA11yLabel(TEXT_LABEL)).toHaveLength(2);
84101
expect(queryAllByA11yLabel(/cool/g)).toHaveLength(3);
102+
85103
expect(() => getAllByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
86104
expect(queryAllByA11yLabel(NO_MATCHES_TEXT)).toEqual([]);
105+
106+
await expect(findAllByA11yLabel(TEXT_LABEL)).resolves.toHaveLength(2);
107+
await expect(findAllByA11yLabel(NO_MATCHES_TEXT)).rejects.toThrow(
108+
NO_INSTANCES_FOUND
109+
);
87110
});
88111

89-
test('getByA11yHint, queryByA11yHint', () => {
90-
const { getByA11yHint, queryByA11yHint } = render(<Section />);
112+
test('getByA11yHint, queryByA11yHint, findByA11yHint', async () => {
113+
const { getByA11yHint, queryByA11yHint, findByA11yHint } = render(
114+
<Section />
115+
);
91116

92117
expect(getByA11yHint(BUTTON_HINT).props.accessibilityHint).toEqual(
93118
BUTTON_HINT
94119
);
95120
const button = queryByA11yHint(/button/g);
96121
expect(button && button.props.accessibilityHint).toEqual(BUTTON_HINT);
122+
97123
expect(() => getByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
98124
expect(queryByA11yHint(NO_MATCHES_TEXT)).toBeNull();
99125

100126
expect(() => getByA11yHint(TEXT_HINT)).toThrow(FOUND_TWO_INSTANCES);
101127
expect(() => queryByA11yHint(TEXT_HINT)).toThrow(FOUND_TWO_INSTANCES);
128+
129+
const asyncButton = await findByA11yHint(BUTTON_HINT);
130+
expect(asyncButton.props.accessibilityHint).toEqual(BUTTON_HINT);
131+
await expect(findByA11yHint(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow(
132+
NO_INSTANCES_FOUND
133+
);
134+
await expect(findByA11yHint(TEXT_HINT, waitForOptions)).rejects.toThrow(
135+
FOUND_TWO_INSTANCES
136+
);
102137
});
103138

104-
test('getAllByA11yHint, queryAllByA11yHint', () => {
105-
const { getAllByA11yHint, queryAllByA11yHint } = render(<Section />);
139+
test('getAllByA11yHint, queryAllByA11yHint', async () => {
140+
const { getAllByA11yHint, queryAllByA11yHint, findAllByA11yHint } = render(
141+
<Section />
142+
);
106143

107144
expect(getAllByA11yHint(TEXT_HINT)).toHaveLength(2);
108145
expect(queryAllByA11yHint(/static/g)).toHaveLength(2);
146+
109147
expect(() => getAllByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
110148
expect(queryAllByA11yHint(NO_MATCHES_TEXT)).toEqual([]);
149+
150+
await expect(findAllByA11yHint(TEXT_HINT)).resolves.toHaveLength(2);
151+
await expect(findAllByA11yHint(NO_MATCHES_TEXT)).rejects.toThrow(
152+
NO_INSTANCES_FOUND
153+
);
111154
});
112155

113-
test('getByA11yRole, queryByA11yRole', () => {
114-
const { getByA11yRole, queryByA11yRole } = render(<Section />);
156+
test('getByA11yRole, queryByA11yRole, findByA11yRole', async () => {
157+
const { getByA11yRole, queryByA11yRole, findByA11yRole } = render(
158+
<Section />
159+
);
115160

116161
expect(getByA11yRole('button').props.accessibilityRole).toEqual('button');
117162
const button = queryByA11yRole(/button/g);
118163
expect(button && button.props.accessibilityRole).toEqual('button');
164+
119165
expect(() => getByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
120166
expect(queryByA11yRole(NO_MATCHES_TEXT)).toBeNull();
121167

122168
expect(() => getByA11yRole('link')).toThrow(FOUND_TWO_INSTANCES);
123169
expect(() => queryByA11yRole('link')).toThrow(FOUND_TWO_INSTANCES);
170+
171+
const asyncButton = await findByA11yRole('button');
172+
expect(asyncButton.props.accessibilityRole).toEqual('button');
173+
await expect(findByA11yRole(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow(
174+
NO_INSTANCES_FOUND
175+
);
176+
await expect(findByA11yRole('link')).rejects.toThrow(FOUND_TWO_INSTANCES);
124177
});
125178

126-
test('getAllByA11yRole, queryAllByA11yRole', () => {
127-
const { getAllByA11yRole, queryAllByA11yRole } = render(<Section />);
179+
test('getAllByA11yRole, queryAllByA11yRole, findAllByA11yRole', async () => {
180+
const { getAllByA11yRole, queryAllByA11yRole, findAllByA11yRole } = render(
181+
<Section />
182+
);
128183

129184
expect(getAllByA11yRole('link')).toHaveLength(2);
130185
expect(queryAllByA11yRole(/ink/g)).toHaveLength(2);
186+
131187
expect(() => getAllByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
132188
expect(queryAllByA11yRole(NO_MATCHES_TEXT)).toEqual([]);
189+
190+
await expect(findAllByA11yRole('link')).resolves.toHaveLength(2);
191+
await expect(
192+
findAllByA11yRole(NO_MATCHES_TEXT, waitForOptions)
193+
).rejects.toThrow(NO_INSTANCES_FOUND);
133194
});
134195

135196
// TODO: accessibilityStates was removed from RN 0.62
@@ -169,8 +230,10 @@ test.skip('getAllByA11yStates, queryAllByA11yStates', () => {
169230
expect(queryAllByA11yStates(NO_MATCHES_TEXT)).toEqual([]);
170231
});
171232

172-
test('getByA11yState, queryByA11yState', () => {
173-
const { getByA11yState, queryByA11yState } = render(<Section />);
233+
test('getByA11yState, queryByA11yState, findByA11yState', async () => {
234+
const { getByA11yState, queryByA11yState, findByA11yState } = render(
235+
<Section />
236+
);
174237

175238
expect(getByA11yState({ selected: true }).props.accessibilityState).toEqual({
176239
selected: true,
@@ -192,25 +255,49 @@ test('getByA11yState, queryByA11yState', () => {
192255
expect(() => queryByA11yState({ expanded: false })).toThrow(
193256
FOUND_TWO_INSTANCES
194257
);
258+
259+
const asyncButton = await findByA11yState({ selected: true });
260+
expect(asyncButton.props.accessibilityState).toEqual({
261+
selected: true,
262+
expanded: false,
263+
});
264+
await expect(
265+
findByA11yState({ disabled: true }, waitForOptions)
266+
).rejects.toThrow(NO_INSTANCES_FOUND);
267+
await expect(
268+
findByA11yState({ expanded: false }, waitForOptions)
269+
).rejects.toThrow(FOUND_TWO_INSTANCES);
195270
});
196271

197-
test('getAllByA11yState, queryAllByA11yState', () => {
198-
const { getAllByA11yState, queryAllByA11yState } = render(<Section />);
272+
test('getAllByA11yState, queryAllByA11yState, findAllByA11yState', async () => {
273+
const { getAllByA11yState, queryAllByA11yState, findAllByA11yState } = render(
274+
<Section />
275+
);
199276

200-
expect(getAllByA11yState({ selected: true }).length).toEqual(1);
201-
expect(queryAllByA11yState({ selected: true }).length).toEqual(1);
277+
expect(getAllByA11yState({ selected: true })).toHaveLength(1);
278+
expect(queryAllByA11yState({ selected: true })).toHaveLength(1);
202279

203280
expect(() => getAllByA11yState({ disabled: true })).toThrow(
204281
NO_INSTANCES_FOUND
205282
);
206283
expect(queryAllByA11yState({ disabled: true })).toEqual([]);
207284

208-
expect(getAllByA11yState({ expanded: false }).length).toEqual(2);
209-
expect(queryAllByA11yState({ expanded: false }).length).toEqual(2);
285+
expect(getAllByA11yState({ expanded: false })).toHaveLength(2);
286+
expect(queryAllByA11yState({ expanded: false })).toHaveLength(2);
287+
288+
await expect(findAllByA11yState({ selected: true })).resolves.toHaveLength(1);
289+
await expect(
290+
findAllByA11yState({ disabled: true }, waitForOptions)
291+
).rejects.toThrow(NO_INSTANCES_FOUND);
292+
await expect(findAllByA11yState({ expanded: false })).resolves.toHaveLength(
293+
2
294+
);
210295
});
211296

212-
test('getByA11yValue, queryByA11yValue', () => {
213-
const { getByA11yValue, queryByA11yValue } = render(<Section />);
297+
test('getByA11yValue, queryByA11yValue, findByA11yValue', async () => {
298+
const { getByA11yValue, queryByA11yValue, findByA11yValue } = render(
299+
<Section />
300+
);
214301

215302
expect(getByA11yValue({ min: 40 }).props.accessibilityValue).toEqual({
216303
min: 40,
@@ -226,17 +313,37 @@ test('getByA11yValue, queryByA11yValue', () => {
226313

227314
expect(() => getByA11yValue({ max: 60 })).toThrow(FOUND_TWO_INSTANCES);
228315
expect(() => queryByA11yValue({ max: 60 })).toThrow(FOUND_TWO_INSTANCES);
316+
317+
const asyncElement = await findByA11yValue({ min: 40 });
318+
expect(asyncElement.props.accessibilityValue).toEqual({
319+
min: 40,
320+
max: 60,
321+
});
322+
await expect(findByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow(
323+
NO_INSTANCES_FOUND
324+
);
325+
await expect(findByA11yValue({ max: 60 }, waitForOptions)).rejects.toThrow(
326+
FOUND_TWO_INSTANCES
327+
);
229328
});
230329

231-
test('getAllByA11yValue, queryAllByA11yValue', () => {
232-
const { getAllByA11yValue, queryAllByA11yValue } = render(<Section />);
330+
test('getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue', async () => {
331+
const { getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue } = render(
332+
<Section />
333+
);
233334

234-
expect(getAllByA11yValue({ min: 40 }).length).toEqual(1);
235-
expect(queryAllByA11yValue({ min: 40 }).length).toEqual(1);
335+
expect(getAllByA11yValue({ min: 40 })).toHaveLength(1);
336+
expect(queryAllByA11yValue({ min: 40 })).toHaveLength(1);
236337

237338
expect(() => getAllByA11yValue({ min: 50 })).toThrow(NO_INSTANCES_FOUND);
238339
expect(queryAllByA11yValue({ min: 50 })).toEqual([]);
239340

240-
expect(getAllByA11yValue({ max: 60 }).length).toEqual(2);
241-
expect(queryAllByA11yValue({ max: 60 }).length).toEqual(2);
341+
expect(queryAllByA11yValue({ max: 60 })).toHaveLength(2);
342+
expect(getAllByA11yValue({ max: 60 })).toHaveLength(2);
343+
344+
await expect(findAllByA11yValue({ min: 40 })).resolves.toHaveLength(1);
345+
await expect(findAllByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow(
346+
NO_INSTANCES_FOUND
347+
);
348+
await expect(findAllByA11yValue({ max: 60 })).resolves.toHaveLength(2);
242349
});

src/__tests__/findByApi.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// @flow
2+
import React from 'react';
3+
import { View, Text, TextInput } from 'react-native';
4+
import { render } from '..';
5+
6+
test('findBy queries work asynchronously', async () => {
7+
const options = { timeout: 10 }; // Short timeout so that this test runs quickly
8+
const {
9+
rerender,
10+
findByTestId,
11+
findAllByTestId,
12+
findByText,
13+
findAllByText,
14+
findByPlaceholder,
15+
findAllByPlaceholder,
16+
findByDisplayValue,
17+
findAllByDisplayValue,
18+
} = render(<View />);
19+
await expect(findByTestId('aTestId', options)).rejects.toBeTruthy();
20+
await expect(findAllByTestId('aTestId', options)).rejects.toBeTruthy();
21+
await expect(findByText('Some Text', options)).rejects.toBeTruthy();
22+
await expect(findAllByText('Some Text', options)).rejects.toBeTruthy();
23+
await expect(
24+
findByPlaceholder('Placeholder Text', options)
25+
).rejects.toBeTruthy();
26+
await expect(
27+
findAllByPlaceholder('Placeholder Text', options)
28+
).rejects.toBeTruthy();
29+
await expect(
30+
findByDisplayValue('Display Value', options)
31+
).rejects.toBeTruthy();
32+
await expect(
33+
findAllByDisplayValue('Display Value', options)
34+
).rejects.toBeTruthy();
35+
36+
setTimeout(
37+
() =>
38+
rerender(
39+
<View testID="aTestId">
40+
<Text>Some Text</Text>
41+
<TextInput placeholder="Placeholder Text" />
42+
<TextInput value="Display Value" />
43+
</View>
44+
),
45+
20
46+
);
47+
48+
await expect(findByTestId('aTestId')).resolves.toBeTruthy();
49+
await expect(findAllByTestId('aTestId')).resolves.toHaveLength(1);
50+
await expect(findByText('Some Text')).resolves.toBeTruthy();
51+
await expect(findAllByText('Some Text')).resolves.toHaveLength(1);
52+
await expect(findByPlaceholder('Placeholder Text')).resolves.toBeTruthy();
53+
await expect(findAllByPlaceholder('Placeholder Text')).resolves.toHaveLength(
54+
1
55+
);
56+
await expect(findByDisplayValue('Display Value')).resolves.toBeTruthy();
57+
await expect(findAllByDisplayValue('Display Value')).resolves.toHaveLength(1);
58+
}, 10000);

src/__tests__/within.test.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33
import { View, Text, TextInput } from 'react-native';
44
import { render, within } from '..';
55

6-
test('within() exposes basic queries', () => {
6+
test('within() exposes basic queries', async () => {
77
const rootQueries = render(
88
<View>
99
<View accessibilityHint="first">
@@ -28,6 +28,12 @@ test('within() exposes basic queries', () => {
2828
expect(firstQueries.queryByText('Same Text')).toBeTruthy();
2929
expect(firstQueries.getByDisplayValue('Same Value')).toBeTruthy();
3030
expect(firstQueries.getByPlaceholder('Same Placeholder')).toBeTruthy();
31+
await expect(
32+
firstQueries.findByDisplayValue('Same Value')
33+
).resolves.toBeTruthy();
34+
await expect(
35+
firstQueries.findAllByPlaceholder('Same Placeholder')
36+
).resolves.toHaveLength(1);
3137

3238
const secondQueries = within(rootQueries.getByA11yHint('second'));
3339
expect(secondQueries.getAllByText('Same Text')).toHaveLength(1);
@@ -38,7 +44,7 @@ test('within() exposes basic queries', () => {
3844
expect(secondQueries.getByPlaceholder('Same Placeholder')).toBeTruthy();
3945
});
4046

41-
test('within() exposes a11y queries', () => {
47+
test('within() exposes a11y queries', async () => {
4248
const rootQueries = render(
4349
<View>
4450
<View accessibilityHint="first">
@@ -64,8 +70,22 @@ test('within() exposes a11y queries', () => {
6470
const firstQueries = within(rootQueries.getByA11yHint('first'));
6571
expect(firstQueries.getByA11yLabel('Same Label')).toBeTruthy();
6672
expect(firstQueries.getByA11yHint('Same Hint')).toBeTruthy();
73+
expect(firstQueries.queryByA11yLabel('Same Label')).toBeTruthy();
74+
expect(firstQueries.queryByA11yHint('Same Hint')).toBeTruthy();
75+
await expect(
76+
firstQueries.findByA11yLabel('Same Label')
77+
).resolves.toBeTruthy();
78+
await expect(firstQueries.findByA11yHint('Same Hint')).resolves.toBeTruthy();
6779

6880
const secondQueries = within(rootQueries.getByA11yHint('second'));
6981
expect(secondQueries.getAllByA11yLabel('Same Label')).toHaveLength(1);
7082
expect(secondQueries.getAllByA11yHint('Same Hint')).toHaveLength(1);
83+
expect(secondQueries.queryAllByA11yLabel('Same Label')).toHaveLength(1);
84+
expect(secondQueries.queryAllByA11yHint('Same Hint')).toHaveLength(1);
85+
await expect(
86+
secondQueries.findAllByA11yLabel('Same Label')
87+
).resolves.toHaveLength(1);
88+
await expect(
89+
secondQueries.findAllByA11yHint('Same Hint')
90+
).resolves.toHaveLength(1);
7191
});

0 commit comments

Comments
 (0)