diff --git a/src/__tests__/host-component-names.test.tsx b/src/__tests__/host-component-names.test.tsx
index 0a1fd203b..8d10c658d 100644
--- a/src/__tests__/host-component-names.test.tsx
+++ b/src/__tests__/host-component-names.test.tsx
@@ -1,8 +1,13 @@
+import * as React from 'react';
import { View } from 'react-native';
import TestRenderer from 'react-test-renderer';
import { configureInternal, getConfig } from '../config';
-import { getHostComponentNames } from '../helpers/host-component-names';
+import {
+ getHostComponentNames,
+ configureHostComponentNamesIfNeeded,
+} from '../helpers/host-component-names';
import * as within from '../within';
+import { act, render } from '..';
const mockCreate = jest.spyOn(TestRenderer, 'create') as jest.Mock;
const mockGetQueriesForElements = jest.spyOn(
@@ -11,10 +16,48 @@ const mockGetQueriesForElements = jest.spyOn(
) as jest.Mock;
describe('getHostComponentNames', () => {
+ test('returns host component names from internal config', () => {
+ configureInternal({
+ hostComponentNames: { text: 'banana', textInput: 'banana' },
+ });
+
+ expect(getHostComponentNames()).toEqual({
+ text: 'banana',
+ textInput: 'banana',
+ });
+ });
+
+ test('detects host component names if not present in internal config', () => {
+ expect(getConfig().hostComponentNames).toBeUndefined();
+
+ const hostComponentNames = getHostComponentNames();
+
+ expect(hostComponentNames).toEqual({
+ text: 'Text',
+ textInput: 'TextInput',
+ });
+ expect(getConfig().hostComponentNames).toBe(hostComponentNames);
+ });
+
+ // Repro test for case when user indirectly triggers `getHostComponentNames` calls from
+ // explicit `act` wrapper.
+ // See: https://github.com/callstack/react-native-testing-library/issues/1302
+ // and https://github.com/callstack/react-native-testing-library/issues/1305
+ test('does not throw when wrapped in act after render has been called', () => {
+ render();
+ expect(() =>
+ act(() => {
+ getHostComponentNames();
+ })
+ ).not.toThrow();
+ });
+});
+
+describe('configureHostComponentNamesIfNeeded', () => {
test('updates internal config with host component names when they are not defined', () => {
expect(getConfig().hostComponentNames).toBeUndefined();
- getHostComponentNames();
+ configureHostComponentNamesIfNeeded();
expect(getConfig().hostComponentNames).toEqual({
text: 'Text',
@@ -27,7 +70,7 @@ describe('getHostComponentNames', () => {
hostComponentNames: { text: 'banana', textInput: 'banana' },
});
- getHostComponentNames();
+ configureHostComponentNamesIfNeeded();
expect(getConfig().hostComponentNames).toEqual({
text: 'banana',
@@ -40,14 +83,14 @@ describe('getHostComponentNames', () => {
root: { type: View, children: [], props: {} },
});
- expect(() => getHostComponentNames()).toThrowErrorMatchingInlineSnapshot(`
+ expect(() => configureHostComponentNamesIfNeeded())
+ .toThrowErrorMatchingInlineSnapshot(`
"Trying to detect host component names triggered the following error:
Unable to find an element with testID: text
There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
- Please check if you are using compatible versions of React Native and React Native Testing Library.
- "
+ Please check if you are using compatible versions of React Native and React Native Testing Library."
`);
});
@@ -58,14 +101,14 @@ describe('getHostComponentNames', () => {
},
});
- expect(() => getHostComponentNames()).toThrowErrorMatchingInlineSnapshot(`
+ expect(() => configureHostComponentNamesIfNeeded())
+ .toThrowErrorMatchingInlineSnapshot(`
"Trying to detect host component names triggered the following error:
getByTestId returned non-host component
There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
- Please check if you are using compatible versions of React Native and React Native Testing Library.
- "
+ Please check if you are using compatible versions of React Native and React Native Testing Library."
`);
});
});
diff --git a/src/helpers/host-component-names.tsx b/src/helpers/host-component-names.tsx
index 5872c2417..b6b06a008 100644
--- a/src/helpers/host-component-names.tsx
+++ b/src/helpers/host-component-names.tsx
@@ -4,15 +4,30 @@ import TestRenderer from 'react-test-renderer';
import { configureInternal, getConfig, HostComponentNames } from '../config';
import { getQueriesForElement } from '../within';
-const defaultErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
+const userConfigErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
Please check if you are using compatible versions of React Native and React Native Testing Library.`;
export function getHostComponentNames(): HostComponentNames {
+ let hostComponentNames = getConfig().hostComponentNames;
+ if (!hostComponentNames) {
+ hostComponentNames = detectHostComponentNames();
+ configureInternal({ hostComponentNames });
+ }
+
+ return hostComponentNames;
+}
+
+export function configureHostComponentNamesIfNeeded() {
const configHostComponentNames = getConfig().hostComponentNames;
if (configHostComponentNames) {
- return configHostComponentNames;
+ return;
}
+ const hostComponentNames = detectHostComponentNames();
+ configureInternal({ hostComponentNames });
+}
+
+function detectHostComponentNames(): HostComponentNames {
try {
const renderer = TestRenderer.create(
@@ -20,6 +35,7 @@ export function getHostComponentNames(): HostComponentNames {
);
+
const { getByTestId } = getQueriesForElement(renderer.root);
const textHostName = getByTestId('text').type;
const textInputHostName = getByTestId('textInput').type;
@@ -32,19 +48,18 @@ export function getHostComponentNames(): HostComponentNames {
throw new Error('getByTestId returned non-host component');
}
- const hostComponentNames = {
+ return {
text: textHostName,
textInput: textInputHostName,
};
- configureInternal({ hostComponentNames });
- return hostComponentNames;
} catch (error) {
const errorMessage =
error && typeof error === 'object' && 'message' in error
? error.message
: null;
- throw new Error(`Trying to detect host component names triggered the following error:\n\n${errorMessage}\n\n${defaultErrorMessage}
-`);
+ throw new Error(
+ `Trying to detect host component names triggered the following error:\n\n${errorMessage}\n\n${userConfigErrorMessage}`
+ );
}
}
diff --git a/src/render.tsx b/src/render.tsx
index a25828985..b60d15139 100644
--- a/src/render.tsx
+++ b/src/render.tsx
@@ -10,6 +10,7 @@ import { getQueriesForElement } from './within';
import { setRenderResult, screen } from './screen';
import { validateStringsRenderedWithinText } from './helpers/stringValidation';
import { getConfig } from './config';
+import { configureHostComponentNamesIfNeeded } from './helpers/host-component-names';
export type RenderOptions = {
wrapper?: React.ComponentType;
@@ -35,6 +36,8 @@ export default function render(
unstable_validateStringsRenderedWithinText,
}: RenderOptions = {}
) {
+ configureHostComponentNamesIfNeeded();
+
if (unstable_validateStringsRenderedWithinText) {
return renderWithStringValidation(component, {
wrapper: Wrapper,