Skip to content

Commit 2b5d0d2

Browse files
mdjastrzebskipierrezimmermann
and
pierrezimmermann
authored
fix: get host component names on render (modified) (#1306)
Co-authored-by: pierrezimmermann <[email protected]>
1 parent 15e2db7 commit 2b5d0d2

File tree

3 files changed

+77
-16
lines changed

3 files changed

+77
-16
lines changed

src/__tests__/host-component-names.test.tsx

+52-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import * as React from 'react';
12
import { View } from 'react-native';
23
import TestRenderer from 'react-test-renderer';
34
import { configureInternal, getConfig } from '../config';
4-
import { getHostComponentNames } from '../helpers/host-component-names';
5+
import {
6+
getHostComponentNames,
7+
configureHostComponentNamesIfNeeded,
8+
} from '../helpers/host-component-names';
59
import * as within from '../within';
10+
import { act, render } from '..';
611

712
const mockCreate = jest.spyOn(TestRenderer, 'create') as jest.Mock;
813
const mockGetQueriesForElements = jest.spyOn(
@@ -11,10 +16,48 @@ const mockGetQueriesForElements = jest.spyOn(
1116
) as jest.Mock;
1217

1318
describe('getHostComponentNames', () => {
19+
test('returns host component names from internal config', () => {
20+
configureInternal({
21+
hostComponentNames: { text: 'banana', textInput: 'banana' },
22+
});
23+
24+
expect(getHostComponentNames()).toEqual({
25+
text: 'banana',
26+
textInput: 'banana',
27+
});
28+
});
29+
30+
test('detects host component names if not present in internal config', () => {
31+
expect(getConfig().hostComponentNames).toBeUndefined();
32+
33+
const hostComponentNames = getHostComponentNames();
34+
35+
expect(hostComponentNames).toEqual({
36+
text: 'Text',
37+
textInput: 'TextInput',
38+
});
39+
expect(getConfig().hostComponentNames).toBe(hostComponentNames);
40+
});
41+
42+
// Repro test for case when user indirectly triggers `getHostComponentNames` calls from
43+
// explicit `act` wrapper.
44+
// See: https://github.com/callstack/react-native-testing-library/issues/1302
45+
// and https://github.com/callstack/react-native-testing-library/issues/1305
46+
test('does not throw when wrapped in act after render has been called', () => {
47+
render(<View />);
48+
expect(() =>
49+
act(() => {
50+
getHostComponentNames();
51+
})
52+
).not.toThrow();
53+
});
54+
});
55+
56+
describe('configureHostComponentNamesIfNeeded', () => {
1457
test('updates internal config with host component names when they are not defined', () => {
1558
expect(getConfig().hostComponentNames).toBeUndefined();
1659

17-
getHostComponentNames();
60+
configureHostComponentNamesIfNeeded();
1861

1962
expect(getConfig().hostComponentNames).toEqual({
2063
text: 'Text',
@@ -27,7 +70,7 @@ describe('getHostComponentNames', () => {
2770
hostComponentNames: { text: 'banana', textInput: 'banana' },
2871
});
2972

30-
getHostComponentNames();
73+
configureHostComponentNamesIfNeeded();
3174

3275
expect(getConfig().hostComponentNames).toEqual({
3376
text: 'banana',
@@ -40,14 +83,14 @@ describe('getHostComponentNames', () => {
4083
root: { type: View, children: [], props: {} },
4184
});
4285

43-
expect(() => getHostComponentNames()).toThrowErrorMatchingInlineSnapshot(`
86+
expect(() => configureHostComponentNamesIfNeeded())
87+
.toThrowErrorMatchingInlineSnapshot(`
4488
"Trying to detect host component names triggered the following error:
4589
4690
Unable to find an element with testID: text
4791
4892
There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
49-
Please check if you are using compatible versions of React Native and React Native Testing Library.
50-
"
93+
Please check if you are using compatible versions of React Native and React Native Testing Library."
5194
`);
5295
});
5396

@@ -58,14 +101,14 @@ describe('getHostComponentNames', () => {
58101
},
59102
});
60103

61-
expect(() => getHostComponentNames()).toThrowErrorMatchingInlineSnapshot(`
104+
expect(() => configureHostComponentNamesIfNeeded())
105+
.toThrowErrorMatchingInlineSnapshot(`
62106
"Trying to detect host component names triggered the following error:
63107
64108
getByTestId returned non-host component
65109
66110
There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
67-
Please check if you are using compatible versions of React Native and React Native Testing Library.
68-
"
111+
Please check if you are using compatible versions of React Native and React Native Testing Library."
69112
`);
70113
});
71114
});

src/helpers/host-component-names.tsx

+22-7
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,38 @@ import TestRenderer from 'react-test-renderer';
44
import { configureInternal, getConfig, HostComponentNames } from '../config';
55
import { getQueriesForElement } from '../within';
66

7-
const defaultErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
7+
const userConfigErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
88
Please check if you are using compatible versions of React Native and React Native Testing Library.`;
99

1010
export function getHostComponentNames(): HostComponentNames {
11+
let hostComponentNames = getConfig().hostComponentNames;
12+
if (!hostComponentNames) {
13+
hostComponentNames = detectHostComponentNames();
14+
configureInternal({ hostComponentNames });
15+
}
16+
17+
return hostComponentNames;
18+
}
19+
20+
export function configureHostComponentNamesIfNeeded() {
1121
const configHostComponentNames = getConfig().hostComponentNames;
1222
if (configHostComponentNames) {
13-
return configHostComponentNames;
23+
return;
1424
}
1525

26+
const hostComponentNames = detectHostComponentNames();
27+
configureInternal({ hostComponentNames });
28+
}
29+
30+
function detectHostComponentNames(): HostComponentNames {
1631
try {
1732
const renderer = TestRenderer.create(
1833
<View>
1934
<Text testID="text">Hello</Text>
2035
<TextInput testID="textInput" />
2136
</View>
2237
);
38+
2339
const { getByTestId } = getQueriesForElement(renderer.root);
2440
const textHostName = getByTestId('text').type;
2541
const textInputHostName = getByTestId('textInput').type;
@@ -32,19 +48,18 @@ export function getHostComponentNames(): HostComponentNames {
3248
throw new Error('getByTestId returned non-host component');
3349
}
3450

35-
const hostComponentNames = {
51+
return {
3652
text: textHostName,
3753
textInput: textInputHostName,
3854
};
39-
configureInternal({ hostComponentNames });
40-
return hostComponentNames;
4155
} catch (error) {
4256
const errorMessage =
4357
error && typeof error === 'object' && 'message' in error
4458
? error.message
4559
: null;
4660

47-
throw new Error(`Trying to detect host component names triggered the following error:\n\n${errorMessage}\n\n${defaultErrorMessage}
48-
`);
61+
throw new Error(
62+
`Trying to detect host component names triggered the following error:\n\n${errorMessage}\n\n${userConfigErrorMessage}`
63+
);
4964
}
5065
}

src/render.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getQueriesForElement } from './within';
1010
import { setRenderResult, screen } from './screen';
1111
import { validateStringsRenderedWithinText } from './helpers/stringValidation';
1212
import { getConfig } from './config';
13+
import { configureHostComponentNamesIfNeeded } from './helpers/host-component-names';
1314

1415
export type RenderOptions = {
1516
wrapper?: React.ComponentType<any>;
@@ -35,6 +36,8 @@ export default function render<T>(
3536
unstable_validateStringsRenderedWithinText,
3637
}: RenderOptions = {}
3738
) {
39+
configureHostComponentNamesIfNeeded();
40+
3841
if (unstable_validateStringsRenderedWithinText) {
3942
return renderWithStringValidation(component, {
4043
wrapper: Wrapper,

0 commit comments

Comments
 (0)