Skip to content

Commit 97ab842

Browse files
eliperkinsEsemesek
authored andcommitted
improvement: Allow for text to match children who are composed as array (#132)
### Summary Given a component that renders text by composing together literal text with an inline expression for a dynamic variable, React Native will render this as a `<Text>` element with multiple children. For example: ```js const BananaCounter = ({ numBananas }) => ( <Text>There are {numBananas} bananas in the bunch</Text> ); const { toJSON, debug } = render(<BananaCounter numBananas={3} />); debug(); /* <Text> There are 3 bananas in the bunch </Text> */ expect(toJSON()).toMatchInlineSnapshot(` <Text> There are 3 bananas in the bunch </Text> `); ``` This makes sense, as the component is a: - literal string - a dynamic evaluation - literal string Unfortunately, this means that writing a test that finds an element based on that dynamic evaluation fails when using `getByText`. ```js const { getByText } = render(<BananaCounter numBananas={3} />); expect(getByText('There are 3 bananas in the bunch')).toBeTruthy(); // Fails ``` This is because we compare the given test string directly against `children`. https://github.com/callstack/react-native-testing-library/blob/ce3bf28f308728672bc5502e120048821c60218b/src/helpers/getByAPI.js#L23-L24 This results in comparing `'There are 3 bananas in the bunch' === ['There are ' 3, ' bananas in the bunch']`, which of course will fail. The solution is to join children and compare the joined result against the given text string. ### Test plan A test for this new functionality is provided!
1 parent 30fcce5 commit 97ab842

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

docs/API.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type ReactTestInstance = {
5050

5151
A method returning a `ReactTestInstance` with matching text – may be a string or regular expression. Throws when no matches.
5252

53+
This method will join `<Text>` siblings to find matches, similarly to [how React Native handles these components](https://facebook.github.io/react-native/docs/text#containers). This will allow for querying for strings that will be visually rendered together, but may be semantically separate React components.
54+
5355
### `getAllByText: (text: string | RegExp)`
5456

5557
A method returning an array of `ReactTestInstance`s with matching text – may be a string or regular expression.

src/__tests__/render.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,38 @@ test('getByText, queryByText', () => {
135135
expect(() => queryByText(/fresh/)).toThrow('Expected 1 but found 3');
136136
});
137137

138+
test('getByText, queryByText with children as Array', () => {
139+
const BananaCounter = ({ numBananas }) => (
140+
<Text>There are {numBananas} bananas in the bunch</Text>
141+
);
142+
143+
const BananaStore = () => (
144+
<View>
145+
<BananaCounter numBananas={3} />
146+
<BananaCounter numBananas={6} />
147+
<BananaCounter numBananas={5} />
148+
</View>
149+
);
150+
151+
const { getByText } = render(<BananaStore />);
152+
153+
const { toJSON } = render(<BananaCounter numBananas={3} />);
154+
expect(toJSON()).toMatchInlineSnapshot(`
155+
<Text>
156+
There are
157+
3
158+
bananas in the bunch
159+
</Text>
160+
`);
161+
162+
const threeBananaBunch = getByText('There are 3 bananas in the bunch');
163+
expect(threeBananaBunch.props.children).toEqual([
164+
'There are ',
165+
3,
166+
' bananas in the bunch',
167+
]);
168+
});
169+
138170
test('getAllByText, queryAllByText', () => {
139171
const { getAllByText, queryAllByText } = render(<Banana />);
140172
const buttons = getAllByText(/fresh/i);

src/helpers/getByAPI.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@ const getNodeByText = (node, text) => {
1717
try {
1818
// eslint-disable-next-line
1919
const { Text, TextInput } = require('react-native');
20-
return (
21-
(filterNodeByType(node, Text) || filterNodeByType(node, TextInput)) &&
22-
(typeof text === 'string'
23-
? text === node.props.children
24-
: text.test(node.props.children))
25-
);
20+
const isTextComponent =
21+
filterNodeByType(node, Text) || filterNodeByType(node, TextInput);
22+
if (isTextComponent) {
23+
const textChildren = React.Children.map(node.props.children, child =>
24+
child.toString()
25+
);
26+
if (textChildren) {
27+
const textToTest = textChildren.join('');
28+
return typeof text === 'string'
29+
? text === textToTest
30+
: text.test(textToTest);
31+
}
32+
}
33+
return false;
2634
} catch (error) {
2735
throw createLibraryNotSupportedError(error);
2836
}

0 commit comments

Comments
 (0)