Skip to content

Commit 791c5d5

Browse files
author
MattAgn
committed
refactor: use new query builders on byTestId
1 parent 9871677 commit 791c5d5

File tree

7 files changed

+232
-127
lines changed

7 files changed

+232
-127
lines changed

src/__tests__/byTestId.test.js

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// @flow
2+
import React from 'react';
3+
import { View, Text, TextInput, TouchableOpacity, Button } from 'react-native';
4+
import { render } from '..';
5+
6+
const PLACEHOLDER_FRESHNESS = 'Add custom freshness';
7+
const PLACEHOLDER_CHEF = 'Who inspected freshness?';
8+
const INPUT_FRESHNESS = 'Custom Freshie';
9+
const INPUT_CHEF = 'I inspected freshie';
10+
11+
class MyButton extends React.Component<any> {
12+
render() {
13+
return (
14+
<TouchableOpacity onPress={this.props.onPress}>
15+
<Text>{this.props.children}</Text>
16+
</TouchableOpacity>
17+
);
18+
}
19+
}
20+
21+
class Banana extends React.Component<any, any> {
22+
state = {
23+
fresh: false,
24+
};
25+
26+
componentDidUpdate() {
27+
if (this.props.onUpdate) {
28+
this.props.onUpdate();
29+
}
30+
}
31+
32+
componentWillUnmount() {
33+
if (this.props.onUnmount) {
34+
this.props.onUnmount();
35+
}
36+
}
37+
38+
changeFresh = () => {
39+
this.setState((state) => ({
40+
fresh: !state.fresh,
41+
}));
42+
};
43+
44+
render() {
45+
const test = 0;
46+
return (
47+
<View>
48+
<Text>Is the banana fresh?</Text>
49+
<Text testID="bananaFresh">
50+
{this.state.fresh ? 'fresh' : 'not fresh'}
51+
</Text>
52+
<TextInput
53+
testID="bananaCustomFreshness"
54+
placeholder={PLACEHOLDER_FRESHNESS}
55+
value={INPUT_FRESHNESS}
56+
/>
57+
<TextInput
58+
testID="bananaChef"
59+
placeholder={PLACEHOLDER_CHEF}
60+
value={INPUT_CHEF}
61+
/>
62+
<MyButton onPress={this.changeFresh} type="primary">
63+
Change freshness!
64+
</MyButton>
65+
<Text testID="duplicateText">First Text</Text>
66+
<Text testID="duplicateText">Second Text</Text>
67+
<Text>{test}</Text>
68+
</View>
69+
);
70+
}
71+
}
72+
73+
const MyComponent = () => {
74+
return <Text>My Component</Text>;
75+
};
76+
77+
test('getByTestId returns only native elements', () => {
78+
const { getByTestId, getAllByTestId } = render(
79+
<View>
80+
<Text testID="text">Text</Text>
81+
<TextInput testID="textInput" />
82+
<View testID="view" />
83+
<Button testID="button" title="Button" onPress={jest.fn()} />
84+
<MyComponent testID="myComponent" />
85+
</View>
86+
);
87+
88+
expect(getByTestId('text')).toBeTruthy();
89+
expect(getByTestId('textInput')).toBeTruthy();
90+
expect(getByTestId('view')).toBeTruthy();
91+
expect(getByTestId('button')).toBeTruthy();
92+
93+
expect(getAllByTestId('text')).toHaveLength(1);
94+
expect(getAllByTestId('textInput')).toHaveLength(1);
95+
expect(getAllByTestId('view')).toHaveLength(1);
96+
expect(getAllByTestId('button')).toHaveLength(1);
97+
98+
expect(() => getByTestId('myComponent')).toThrowError(
99+
'No instances found with testID: myComponent'
100+
);
101+
expect(() => getAllByTestId('myComponent')).toThrowError(
102+
'No instances found with testID: myComponent'
103+
);
104+
});
105+
106+
test('supports a regex matcher', () => {
107+
const { getByTestId, getAllByTestId } = render(
108+
<View>
109+
<Text testID="text">Text</Text>
110+
<TextInput testID="textInput" />
111+
<View testID="view" />
112+
<Button testID="button" title="Button" onPress={jest.fn()} />
113+
<MyComponent testID="myComponent" />
114+
</View>
115+
);
116+
117+
expect(getByTestId(/view/)).toBeTruthy();
118+
expect(getAllByTestId(/text/)).toHaveLength(2);
119+
});
120+
121+
test('getByTestId, queryByTestId', () => {
122+
const { getByTestId, queryByTestId } = render(<Banana />);
123+
const component = getByTestId('bananaFresh');
124+
125+
expect(component.props.children).toBe('not fresh');
126+
expect(() => getByTestId('InExistent')).toThrow('No instances found');
127+
128+
expect(getByTestId('bananaFresh')).toBe(component);
129+
expect(queryByTestId('InExistent')).toBeNull();
130+
});
131+
132+
test('getAllByTestId, queryAllByTestId', () => {
133+
const { getAllByTestId, queryAllByTestId } = render(<Banana />);
134+
const textElements = getAllByTestId('duplicateText');
135+
136+
expect(textElements.length).toBe(2);
137+
expect(textElements[0].props.children).toBe('First Text');
138+
expect(textElements[1].props.children).toBe('Second Text');
139+
expect(() => getAllByTestId('nonExistentTestId')).toThrow(
140+
'No instances found'
141+
);
142+
143+
const queriedTextElements = queryAllByTestId('duplicateText');
144+
145+
expect(queriedTextElements.length).toBe(2);
146+
expect(queriedTextElements[0]).toBe(textElements[0]);
147+
expect(queriedTextElements[1]).toBe(textElements[1]);
148+
expect(queryAllByTestId('nonExistentTestId')).toHaveLength(0);
149+
});
150+
151+
test('findByTestId and findAllByTestId work asynchronously', async () => {
152+
const options = { timeout: 10 }; // Short timeout so that this test runs quickly
153+
const { rerender, findByTestId, findAllByTestId } = render(<View />);
154+
await expect(findByTestId('aTestId', options)).rejects.toBeTruthy();
155+
await expect(findAllByTestId('aTestId', options)).rejects.toBeTruthy();
156+
157+
setTimeout(
158+
() =>
159+
rerender(
160+
<View testID="aTestId">
161+
<Text>Some Text</Text>
162+
<TextInput placeholder="Placeholder Text" />
163+
<TextInput value="Display Value" />
164+
</View>
165+
),
166+
20
167+
);
168+
169+
await expect(findByTestId('aTestId')).resolves.toBeTruthy();
170+
await expect(findAllByTestId('aTestId')).resolves.toHaveLength(1);
171+
}, 20000);

src/__tests__/findByApi.test.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@ test('findBy queries work asynchronously', async () => {
77
const options = { timeout: 10 }; // Short timeout so that this test runs quickly
88
const {
99
rerender,
10-
findByTestId,
11-
findAllByTestId,
1210
findByText,
1311
findAllByText,
1412
findByPlaceholderText,
1513
findAllByPlaceholderText,
1614
findByDisplayValue,
1715
findAllByDisplayValue,
1816
} = render(<View />);
19-
await expect(findByTestId('aTestId', options)).rejects.toBeTruthy();
20-
await expect(findAllByTestId('aTestId', options)).rejects.toBeTruthy();
2117
await expect(findByText('Some Text', options)).rejects.toBeTruthy();
2218
await expect(findAllByText('Some Text', options)).rejects.toBeTruthy();
2319
await expect(
@@ -45,8 +41,6 @@ test('findBy queries work asynchronously', async () => {
4541
20
4642
);
4743

48-
await expect(findByTestId('aTestId')).resolves.toBeTruthy();
49-
await expect(findAllByTestId('aTestId')).resolves.toHaveLength(1);
5044
await expect(findByText('Some Text')).resolves.toBeTruthy();
5145
await expect(findAllByText('Some Text')).resolves.toHaveLength(1);
5246
await expect(findByPlaceholderText('Placeholder Text')).resolves.toBeTruthy();

src/__tests__/render.test.js

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,36 +79,6 @@ class Banana extends React.Component<any, any> {
7979
}
8080
}
8181

82-
test('getByTestId, queryByTestId', () => {
83-
const { getByTestId, queryByTestId } = render(<Banana />);
84-
const component = getByTestId('bananaFresh');
85-
86-
expect(component.props.children).toBe('not fresh');
87-
expect(() => getByTestId('InExistent')).toThrow('No instances found');
88-
89-
expect(getByTestId('bananaFresh')).toBe(component);
90-
expect(queryByTestId('InExistent')).toBeNull();
91-
});
92-
93-
test('getAllByTestId, queryAllByTestId', () => {
94-
const { getAllByTestId, queryAllByTestId } = render(<Banana />);
95-
const textElements = getAllByTestId('duplicateText');
96-
97-
expect(textElements.length).toBe(2);
98-
expect(textElements[0].props.children).toBe('First Text');
99-
expect(textElements[1].props.children).toBe('Second Text');
100-
expect(() => getAllByTestId('nonExistentTestId')).toThrow(
101-
'No instances found'
102-
);
103-
104-
const queriedTextElements = queryAllByTestId('duplicateText');
105-
106-
expect(queriedTextElements.length).toBe(2);
107-
expect(queriedTextElements[0]).toBe(textElements[0]);
108-
expect(queriedTextElements[1]).toBe(textElements[1]);
109-
expect(queryAllByTestId('nonExistentTestId')).toHaveLength(0);
110-
});
111-
11282
test('UNSAFE_getAllByType, UNSAFE_queryAllByType', () => {
11383
const { UNSAFE_getAllByType, UNSAFE_queryAllByType } = render(<Banana />);
11484
const [text, status, button] = UNSAFE_getAllByType(Text);

src/helpers/byTestId.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// @flow
2+
import {
3+
makeGetAllQuery,
4+
makeSingleQuery,
5+
makeGetQuery,
6+
makeFindAllQuery,
7+
makeFindQuery,
8+
} from './makeQueries';
9+
10+
const getNodeByTestId = (node, testID) => {
11+
return typeof testID === 'string'
12+
? testID === node.props.testID
13+
: testID.test(node.props.testID);
14+
};
15+
16+
export const queryAllByTestId = (instance: ReactTestInstance) =>
17+
function getAllByTestIdFn(testID: string | RegExp): ReactTestInstance[] {
18+
const results = instance
19+
.findAll((node) => getNodeByTestId(node, testID))
20+
.filter((element) => typeof element.type === 'string');
21+
22+
return results;
23+
};
24+
25+
export const getAllByTestId = (instance: ReactTestInstance) =>
26+
makeGetAllQuery(
27+
queryAllByTestId,
28+
instance,
29+
(testId) => `No instances found with testID: ${String(testId)}`
30+
);
31+
32+
export const queryByTestId = (instance: ReactTestInstance) =>
33+
makeSingleQuery(
34+
queryAllByTestId,
35+
instance,
36+
(testId: string, nbResults: number) =>
37+
` Expected 1 but found ${nbResults} instances with testID: ${String(
38+
testId
39+
)}`
40+
);
41+
42+
export const getByTestId = (instance: ReactTestInstance) =>
43+
makeGetQuery(
44+
queryAllByTestId,
45+
instance,
46+
(testId: string, nbResults: number) =>
47+
nbResults > 1
48+
? ` Expected 1 but found ${nbResults} instances with testID: ${String(
49+
testId
50+
)}`
51+
: `No instances found with testID: ${String(testId)}`
52+
);
53+
54+
export const findAllByTestId = (instance: ReactTestInstance) =>
55+
makeFindAllQuery(getAllByTestId, instance);
56+
57+
export const findByTestId = (instance: ReactTestInstance) =>
58+
makeFindQuery(getByTestId, instance);

src/helpers/findByAPI.js

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
import waitFor from '../waitFor';
33
import type { WaitForOptions } from '../waitFor';
44
import {
5-
getByTestId,
6-
getAllByTestId,
75
getByText,
86
getAllByText,
97
getByPlaceholderText,
108
getAllByPlaceholderText,
119
getByDisplayValue,
1210
getAllByDisplayValue,
1311
} from './getByAPI';
12+
import { findAllByTestId, findByTestId } from './byTestId';
1413
import { throwRenamedFunctionError } from './errors';
1514

1615
export type FindByAPI = {|
@@ -57,26 +56,6 @@ const makeFindQuery = <Text, Result>(
5756
waitForOptions: WaitForOptions
5857
): Promise<Result> => waitFor(() => getQuery(instance)(text), waitForOptions);
5958

60-
export const findByTestId = (
61-
instance: ReactTestInstance
62-
): ((
63-
testId: string | RegExp,
64-
waitForOptions?: WaitForOptions
65-
) => Promise<ReactTestInstance>) => (
66-
testId: string | RegExp,
67-
waitForOptions: WaitForOptions = {}
68-
) => makeFindQuery(instance, getByTestId, testId, waitForOptions);
69-
70-
export const findAllByTestId = (
71-
instance: ReactTestInstance
72-
): ((
73-
testId: string | RegExp,
74-
waitForOptions?: WaitForOptions
75-
) => Promise<Array<ReactTestInstance>>) => (
76-
testId: string | RegExp,
77-
waitForOptions: WaitForOptions = {}
78-
) => makeFindQuery(instance, getAllByTestId, testId, waitForOptions);
79-
8059
export const findByText = (
8160
instance: ReactTestInstance
8261
): ((

0 commit comments

Comments
 (0)