Skip to content

Commit 571f3dc

Browse files
committed
feat: allow checking only host elements or composite Text/TextInput
1 parent e815954 commit 571f3dc

File tree

4 files changed

+68
-36
lines changed

4 files changed

+68
-36
lines changed

src/__tests__/to-be-visible.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ describe('.toBeVisible', () => {
124124
expect(() => expect(null).toBeVisible()).toThrowErrorMatchingInlineSnapshot(`
125125
"expect(received).toBeVisible()
126126
127-
received value must be a React Element.
127+
received value must be a host element or composite Text/TextInput element
128128
Received has value: null"
129129
`);
130130
});

src/__tests__/to-have-text-content.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ describe('.toHaveTextContent', () => {
7474
test('can handle multiple levels with no explicit children prop', () => {
7575
const NoChildren = ({ text }: { text: string }) => <Text>{text}</Text>;
7676
const answer = 'Answer';
77-
const { container } = render(
78-
<View>
77+
const { getByTestId } = render(
78+
<View testID="subject">
7979
<Text>
8080
{answer}
8181
{': '}
@@ -86,7 +86,7 @@ describe('.toHaveTextContent', () => {
8686
</View>,
8787
);
8888

89-
expect(container).toHaveTextContent(/^Answer: 42$/);
89+
expect(getByTestId('subject')).toHaveTextContent(/^Answer: 42$/);
9090
});
9191

9292
test('throws when no match is found', () => {

src/__tests__/utils.ts

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,67 @@
1+
import { View, Text, TextInput, Pressable, TouchableOpacity } from 'react-native';
12
import { checkReactElement, isEmpty } from '../utils';
23

34
describe('checkReactElement', () => {
4-
test('it does not throw an error for valid native primitives', () => {
5-
expect(() => {
6-
// @ts-expect-error Argument of type '{ type: "text"; }' is not assignable to parameter of type 'ReactTestInstance'. Type '{ type: "text"; }' is missing the following properties from type 'ReactTestInstance': instance, props, parent, children, and 6 more.ts(2345)
7-
checkReactElement({ type: 'Text' }, () => {}, null);
8-
}).not.toThrow();
5+
test('ReactTestInstance does not throw for host elements', () => {
6+
expect(() =>
7+
// @ts-expect-error Passing incorrect Jest Matcher data
8+
checkReactElement({ type: 'View' }, () => {}, {}),
9+
).not.toThrow();
10+
expect(() =>
11+
// @ts-expect-error Passing incorrect Jest Matcher data
12+
checkReactElement({ type: 'TextInput' }, () => {}, {}),
13+
).not.toThrow();
14+
expect(() =>
15+
// @ts-expect-error Passing incorrect Jest Matcher data
16+
checkReactElement({ type: 'View' }, () => {}, {}),
17+
).not.toThrow();
918
});
1019

11-
test('ReactTestInstance does not throw', () => {
12-
expect(() => {
13-
// @ts-expect-error Argument of type '{ _fiber: {}; }' is not assignable to parameter of type 'ReactTestInstance'. Object literal may only specify known properties, and '_fiber' does not exist in type 'ReactTestInstance'.ts(2345)
14-
checkReactElement({ _fiber: {} }, () => {}, null);
15-
}).not.toThrow();
20+
test('ReactTestInstance does not throw for composite Text elements', () => {
21+
expect(() =>
22+
// @ts-expect-error Passing incorrect Jest Matcher data
23+
checkReactElement({ type: Text }, () => {}, {}),
24+
).not.toThrow();
1625
});
1726

18-
test('it does throw an error for invalid native primitives', () => {
19-
expect(() => {
20-
// @ts-expect-error Argument of type '{ type: "button"; }' is not assignable to parameter of type 'ReactTestInstance'. Type '{ type: "button"; }' is missing the following properties from type 'ReactTestInstance': instance, props, parent, children, and 6 more.ts(2345)
21-
checkReactElement({ type: 'Button' }, () => {}, null);
22-
}).toThrow();
27+
test('ReactTestInstance does not throw for composite TextInput elements', () => {
28+
expect(() =>
29+
// @ts-expect-error Passing incorrect Jest Matcher data
30+
checkReactElement({ type: TextInput }, () => {}, {}),
31+
).not.toThrow();
32+
});
33+
34+
test('it does throw for composite elements', () => {
35+
expect(() =>
36+
// @ts-expect-error Incorrect Test Renderer typings
37+
checkReactElement({ type: View }, () => {}, {}),
38+
).toThrowErrorMatchingInlineSnapshot(`
39+
"expect(received).()
40+
41+
received value must be a host element or composite Text/TextInput element
42+
Received has type: object
43+
Received has value: {"type": [Function Component]}"
44+
`);
45+
expect(() =>
46+
// @ts-expect-error Incorrect Test Renderer typings
47+
checkReactElement({ type: Pressable }, () => {}, {}),
48+
).toThrowErrorMatchingInlineSnapshot(`
49+
"expect(received).()
50+
51+
received value must be a host element or composite Text/TextInput element
52+
Received has type: object
53+
Received has value: {"type": {"$$typeof": Symbol(react.memo), "compare": null, "type": {"$$typeof": Symbol(react.forward_ref), "render": [Function Pressable]}}}"
54+
`);
55+
expect(() =>
56+
// @ts-expect-error Incorrect Test Renderer typings
57+
checkReactElement({ type: TouchableOpacity }, () => {}, {}),
58+
).toThrowErrorMatchingInlineSnapshot(`
59+
"expect(received).()
60+
61+
received value must be a host element or composite Text/TextInput element
62+
Received has type: object
63+
Received has value: {"type": {"$$typeof": Symbol(react.forward_ref), "render": [Function anonymous]}}"
64+
`);
2365
});
2466
});
2567

src/utils.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Text, TextInput } from 'react-native';
2+
import type { ReactTestInstance } from 'react-test-renderer';
13
import redent from 'redent';
24
import {
35
RECEIVED_COLOR as receivedColor,
@@ -8,23 +10,10 @@ import {
810
stringify,
911
} from 'jest-matcher-utils';
1012
import prettyFormat, { plugins } from 'pretty-format';
11-
import type { ReactTestInstance } from 'react-test-renderer';
13+
import { isHostElement } from './component-tree';
1214

1315
const { ReactTestComponent, ReactElement } = plugins;
1416

15-
const VALID_ELEMENTS = [
16-
'Image',
17-
'Text',
18-
'TextInput',
19-
'Modal',
20-
'View',
21-
'RefreshControl',
22-
'ScrollView',
23-
'ActivityIndicator',
24-
'ListView',
25-
'ListViewDataSource',
26-
];
27-
2817
class ReactElementTypeError extends Error {
2918
constructor(received: unknown, matcherFn: jest.CustomMatcher, context: jest.MatcherContext) {
3019
super();
@@ -44,7 +33,9 @@ class ReactElementTypeError extends Error {
4433
this.message = [
4534
matcherHint(`${context.isNot ? '.not' : ''}.${matcherFn.name}`, 'received', ''),
4635
'',
47-
`${receivedColor('received')} value must be a React Element.`,
36+
`${receivedColor(
37+
'received',
38+
)} value must be a host element or composite Text/TextInput element`,
4839
withType,
4940
].join('\n');
5041
}
@@ -59,8 +50,7 @@ function checkReactElement(
5950
throw new ReactElementTypeError(element, matcherFn, context);
6051
}
6152

62-
// @ts-expect-error internal _fiber property of ReactTestInstance
63-
if (!element._fiber && !VALID_ELEMENTS.includes(element.type.toString())) {
53+
if (!isHostElement(element) && element.type !== Text && element.type !== TextInput) {
6454
throw new ReactElementTypeError(element, matcherFn, context);
6555
}
6656
}

0 commit comments

Comments
 (0)