diff --git a/src/__tests__/byText.test.tsx b/src/__tests__/byText.test.tsx
index 22edd1f7e..0602f6ab8 100644
--- a/src/__tests__/byText.test.tsx
+++ b/src/__tests__/byText.test.tsx
@@ -473,3 +473,21 @@ test('getByText and queryByText work properly with multiple nested fragments', (
expect(getByText('Hello')).toBeTruthy();
expect(queryByText('Hello')).not.toBeNull();
});
+
+const TranslationText = ({
+ translationKey,
+}: {
+ translationKey: string;
+}): React.ReactElement => {
+ return <>{translationKey}>;
+};
+
+test('it should be abdle to find text nested in components that render as string', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('hello')).toBeTruthy();
+});
diff --git a/src/helpers/byDisplayValue.ts b/src/helpers/byDisplayValue.ts
index da89a6390..4aa6a4e0d 100644
--- a/src/helpers/byDisplayValue.ts
+++ b/src/helpers/byDisplayValue.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import { matches, TextMatch } from '../matches';
import { makeQueries } from './makeQueries';
import type { Queries } from './makeQueries';
@@ -28,13 +28,13 @@ const getTextInputNodeByDisplayValue = (
};
const queryAllByDisplayValue = (
- instance: ReactTestInstance
+ renderer: ReactTestRenderer
): ((
displayValue: TextMatch,
queryOptions?: TextMatchOptions
) => Array) =>
function queryAllByDisplayValueFn(displayValue, queryOptions) {
- return instance.findAll((node) =>
+ return renderer.root.findAll((node) =>
getTextInputNodeByDisplayValue(node, displayValue, queryOptions)
);
};
diff --git a/src/helpers/byPlaceholderText.ts b/src/helpers/byPlaceholderText.ts
index 2145ef176..7ba24ea9e 100644
--- a/src/helpers/byPlaceholderText.ts
+++ b/src/helpers/byPlaceholderText.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import { matches, TextMatch } from '../matches';
import { makeQueries } from './makeQueries';
import type { Queries } from './makeQueries';
@@ -24,13 +24,13 @@ const getTextInputNodeByPlaceholderText = (
};
const queryAllByPlaceholderText = (
- instance: ReactTestInstance
+ renderer: ReactTestRenderer
): ((
placeholder: TextMatch,
queryOptions?: TextMatchOptions
) => Array) =>
function queryAllByPlaceholderFn(placeholder, queryOptions) {
- return instance.findAll((node) =>
+ return renderer.root.findAll((node) =>
getTextInputNodeByPlaceholderText(node, placeholder, queryOptions)
);
};
diff --git a/src/helpers/byTestId.ts b/src/helpers/byTestId.ts
index e14e833e2..0d078c75e 100644
--- a/src/helpers/byTestId.ts
+++ b/src/helpers/byTestId.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import { matches, TextMatch } from '../matches';
import { makeQueries } from './makeQueries';
import type { Queries } from './makeQueries';
@@ -14,13 +14,13 @@ const getNodeByTestId = (
};
const queryAllByTestId = (
- instance: ReactTestInstance
+ renderer: ReactTestRenderer
): ((
testId: TextMatch,
queryOptions?: TextMatchOptions
) => Array) =>
function queryAllByTestIdFn(testId, queryOptions) {
- const results = instance
+ const results = renderer.root
.findAll((node) => getNodeByTestId(node, testId, queryOptions))
.filter((element) => typeof element.type === 'string');
diff --git a/src/helpers/byText.ts b/src/helpers/byText.ts
index 1d9249798..d000bd2c8 100644
--- a/src/helpers/byText.ts
+++ b/src/helpers/byText.ts
@@ -1,10 +1,13 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type {
+ ReactTestInstance,
+ ReactTestRenderer,
+ ReactTestRendererTree,
+} from 'react-test-renderer';
import * as React from 'react';
import { matches, TextMatch } from '../matches';
import type { NormalizerFn } from '../matches';
import { makeQueries } from './makeQueries';
import type { Queries } from './makeQueries';
-import { filterNodeByType } from './filterNodeByType';
import { createLibraryNotSupportedError } from './errors';
export type TextMatchOptions = {
@@ -13,75 +16,117 @@ export type TextMatchOptions = {
};
const getChildrenAsText = (
- children: React.ReactChild[],
+ children: ReactTestRendererTree | ReactTestRendererTree[] | null,
TextComponent: React.ComponentType
-) => {
- const textContent: string[] = [];
- React.Children.forEach(children, (child) => {
- if (typeof child === 'string') {
- textContent.push(child);
- return;
- }
+): string[] => {
+ if (!children) {
+ return [];
+ }
- if (typeof child === 'number') {
- textContent.push(child.toString());
- return;
+ if (typeof children === 'string') {
+ return [children];
+ }
+
+ const textContent: string[] = [];
+ if (!Array.isArray(children)) {
+ // Bail on traversing text children down the tree if current node (child)
+ // has no text. In such situations, react-test-renderer will traverse down
+ // this tree in a separate call and run this query again. As a result, the
+ // query will match the deepest text node that matches requested text.
+ if (children.type === 'Text') {
+ return [];
}
- if (child?.props?.children) {
- // Bail on traversing text children down the tree if current node (child)
- // has no text. In such situations, react-test-renderer will traverse down
- // this tree in a separate call and run this query again. As a result, the
- // query will match the deepest text node that matches requested text.
- if (filterNodeByType(child, TextComponent)) {
- return;
- }
+ return getChildrenAsText(children.rendered, TextComponent);
+ }
- if (filterNodeByType(child, React.Fragment)) {
- textContent.push(
- ...getChildrenAsText(child.props.children, TextComponent)
- );
- }
- }
- });
+ children.forEach((child) =>
+ textContent.push(...getChildrenAsText(child, TextComponent))
+ );
return textContent;
};
-const getNodeByText = (
- node: ReactTestInstance,
+const getInstancesByText = (
+ tree: ReactTestRendererTree,
text: TextMatch,
options: TextMatchOptions = {}
-) => {
+): Omit[] => {
+ const instances: Omit[] = [];
+
try {
+ if (!tree.rendered) {
+ return [];
+ }
+
+ if (!tree.instance) {
+ if (!Array.isArray(tree.rendered)) {
+ return [...getInstancesByText(tree.rendered, text, options)];
+ }
+
+ tree.rendered.forEach((rendered) => {
+ instances.push(...getInstancesByText(rendered, text, options));
+ });
+ return instances;
+ }
+
+ const textChildren: string[] = [];
const { Text } = require('react-native');
- const isTextComponent = filterNodeByType(node, Text);
- if (isTextComponent) {
- const textChildren = getChildrenAsText(node.props.children, Text);
- if (textChildren) {
- const textToTest = textChildren.join('');
- const { exact, normalizer } = options;
- return matches(text, textToTest, normalizer, exact);
+ if (!Array.isArray(tree.rendered)) {
+ if (tree.rendered.type === 'Text') {
+ textChildren.push(...getChildrenAsText(tree.rendered.rendered, Text));
}
+ instances.push(...getInstancesByText(tree.rendered, text, options));
+ } else {
+ tree.rendered.forEach((child) => {
+ if (child.type === 'Text') {
+ textChildren.push(...getChildrenAsText(child, Text));
+ } else {
+ instances.push(...getInstancesByText(child, text, options));
+ }
+ });
}
- return false;
+
+ if (textChildren.length) {
+ const textToTest = textChildren.join('');
+ const { exact, normalizer } = options;
+ if (matches(text, textToTest, normalizer, exact)) {
+ instances.push(tree.instance);
+ }
+ }
+
+ return instances;
} catch (error) {
throw createLibraryNotSupportedError(error);
}
};
const queryAllByText = (
- instance: ReactTestInstance
+ renderer: ReactTestRenderer
): ((
text: TextMatch,
queryOptions?: TextMatchOptions
) => Array) =>
function queryAllByTextFn(text, queryOptions) {
- const results = instance.findAll((node) =>
- getNodeByText(node, text, queryOptions)
- );
+ const tree = renderer.toTree();
+ const treeInstance = renderer.root;
+
+ if (!tree) {
+ return [];
+ }
+
+ const instancesFound = getInstancesByText(tree, text, queryOptions);
- return results;
+ // Instances in the tree are not of type ReactTestInstance because they do not have parent
+ // This is problematic when firing events so we find in the root nodes the one that matches
+ return instancesFound.map((instanceFound) => {
+ return treeInstance.find((treeInstance) => {
+ return (
+ treeInstance.instance &&
+ treeInstance.instance._reactInternals.stateNode === instanceFound
+ );
+ });
+ });
};
const getMultipleError = (text: TextMatch) =>
diff --git a/src/helpers/findByAPI.ts b/src/helpers/findByAPI.ts
index 551008137..47f87d949 100644
--- a/src/helpers/findByAPI.ts
+++ b/src/helpers/findByAPI.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import type { WaitForOptions } from '../waitFor';
import type { TextMatch } from '../matches';
import type { TextMatchOptions } from './byText';
@@ -56,15 +56,15 @@ export type FindByAPI = {
) => Promise;
};
-export const findByAPI = (instance: ReactTestInstance): FindByAPI => ({
- findByTestId: findByTestId(instance),
- findByText: findByText(instance),
- findByPlaceholderText: findByPlaceholderText(instance),
- findByDisplayValue: findByDisplayValue(instance),
- findAllByTestId: findAllByTestId(instance),
- findAllByText: findAllByText(instance),
- findAllByPlaceholderText: findAllByPlaceholderText(instance),
- findAllByDisplayValue: findAllByDisplayValue(instance),
+export const findByAPI = (renderer: ReactTestRenderer): FindByAPI => ({
+ findByTestId: findByTestId(renderer),
+ findByText: findByText(renderer),
+ findByPlaceholderText: findByPlaceholderText(renderer),
+ findByDisplayValue: findByDisplayValue(renderer),
+ findAllByTestId: findAllByTestId(renderer),
+ findAllByText: findAllByText(renderer),
+ findAllByPlaceholderText: findAllByPlaceholderText(renderer),
+ findAllByDisplayValue: findAllByDisplayValue(renderer),
// Renamed
findByPlaceholder: () =>
diff --git a/src/helpers/getByAPI.ts b/src/helpers/getByAPI.ts
index 0c816e9df..294be497c 100644
--- a/src/helpers/getByAPI.ts
+++ b/src/helpers/getByAPI.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import * as React from 'react';
import prettyFormat from 'pretty-format';
import type { TextMatch } from '../matches';
@@ -104,19 +104,19 @@ export const UNSAFE_getAllByProps = (
return results;
};
-export const getByAPI = (instance: ReactTestInstance): GetByAPI => ({
- getByText: getByText(instance),
- getByPlaceholderText: getByPlaceholderText(instance),
- getByDisplayValue: getByDisplayValue(instance),
- getByTestId: getByTestId(instance),
- getAllByText: getAllByText(instance),
- getAllByPlaceholderText: getAllByPlaceholderText(instance),
- getAllByDisplayValue: getAllByDisplayValue(instance),
- getAllByTestId: getAllByTestId(instance),
+export const getByAPI = (renderer: ReactTestRenderer): GetByAPI => ({
+ getByText: getByText(renderer),
+ getByPlaceholderText: getByPlaceholderText(renderer),
+ getByDisplayValue: getByDisplayValue(renderer),
+ getByTestId: getByTestId(renderer),
+ getAllByText: getAllByText(renderer),
+ getAllByPlaceholderText: getAllByPlaceholderText(renderer),
+ getAllByDisplayValue: getAllByDisplayValue(renderer),
+ getAllByTestId: getAllByTestId(renderer),
// Unsafe
- UNSAFE_getByType: UNSAFE_getByType(instance),
- UNSAFE_getAllByType: UNSAFE_getAllByType(instance),
- UNSAFE_getByProps: UNSAFE_getByProps(instance),
- UNSAFE_getAllByProps: UNSAFE_getAllByProps(instance),
+ UNSAFE_getByType: UNSAFE_getByType(renderer.root),
+ UNSAFE_getAllByType: UNSAFE_getAllByType(renderer.root),
+ UNSAFE_getByProps: UNSAFE_getByProps(renderer.root),
+ UNSAFE_getAllByProps: UNSAFE_getAllByProps(renderer.root),
});
diff --git a/src/helpers/makeQueries.ts b/src/helpers/makeQueries.ts
index 78dd61661..e117b849f 100644
--- a/src/helpers/makeQueries.ts
+++ b/src/helpers/makeQueries.ts
@@ -1,15 +1,15 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import waitFor from '../waitFor';
import type { WaitForOptions } from '../waitFor';
import { ErrorWithStack } from './errors';
import type { TextMatchOptions } from './byText';
type QueryFunction = (
- instance: ReactTestInstance
+ tree: ReactTestRenderer
) => (args: ArgType, queryOptions?: TextMatchOptions) => ReturnType;
type FindQueryFunction = (
- instance: ReactTestInstance
+ tree: ReactTestRenderer
) => (
args: ArgType,
queryOptions?: TextMatchOptions & WaitForOptions,
@@ -77,9 +77,9 @@ export function makeQueries(
getMissingError: (args: QueryArg) => string,
getMultipleError: (args: QueryArg) => string
): Queries {
- function getAllByQuery(instance: ReactTestInstance) {
+ function getAllByQuery(tree: ReactTestRenderer) {
return function getAllFn(args: QueryArg, queryOptions?: TextMatchOptions) {
- const results = queryAllByQuery(instance)(args, queryOptions);
+ const results = queryAllByQuery(tree)(args, queryOptions);
if (results.length === 0) {
throw new ErrorWithStack(getMissingError(args), getAllFn);
@@ -89,12 +89,12 @@ export function makeQueries(
};
}
- function queryByQuery(instance: ReactTestInstance) {
+ function queryByQuery(tree: ReactTestRenderer) {
return function singleQueryFn(
args: QueryArg,
queryOptions?: TextMatchOptions
) {
- const results = queryAllByQuery(instance)(args, queryOptions);
+ const results = queryAllByQuery(tree)(args, queryOptions);
if (results.length > 1) {
throw new ErrorWithStack(getMultipleError(args), singleQueryFn);
@@ -108,9 +108,9 @@ export function makeQueries(
};
}
- function getByQuery(instance: ReactTestInstance) {
+ function getByQuery(tree: ReactTestRenderer) {
return function getFn(args: QueryArg, queryOptions?: TextMatchOptions) {
- const results = queryAllByQuery(instance)(args, queryOptions);
+ const results = queryAllByQuery(tree)(args, queryOptions);
if (results.length > 1) {
throw new ErrorWithStack(getMultipleError(args), getFn);
@@ -124,7 +124,7 @@ export function makeQueries(
};
}
- function findAllByQuery(instance: ReactTestInstance) {
+ function findAllByQuery(tree: ReactTestRenderer) {
return function findAllFn(
args: QueryArg,
queryOptions?: TextMatchOptions & WaitForOptions,
@@ -133,14 +133,14 @@ export function makeQueries(
const deprecatedWaitForOptions = extractDeprecatedWaitForOptionUsage(
queryOptions
);
- return waitFor(() => getAllByQuery(instance)(args, queryOptions), {
+ return waitFor(() => getAllByQuery(tree)(args, queryOptions), {
...deprecatedWaitForOptions,
...waitForOptions,
});
};
}
- function findByQuery(instance: ReactTestInstance) {
+ function findByQuery(tree: ReactTestRenderer) {
return function findFn(
args: QueryArg,
queryOptions?: TextMatchOptions & WaitForOptions,
@@ -149,7 +149,7 @@ export function makeQueries(
const deprecatedWaitForOptions = extractDeprecatedWaitForOptionUsage(
queryOptions
);
- return waitFor(() => getByQuery(instance)(args, queryOptions), {
+ return waitFor(() => getByQuery(tree)(args, queryOptions), {
...deprecatedWaitForOptions,
...waitForOptions,
});
diff --git a/src/helpers/queryByAPI.ts b/src/helpers/queryByAPI.ts
index 12f73ecc5..c28b73931 100644
--- a/src/helpers/queryByAPI.ts
+++ b/src/helpers/queryByAPI.ts
@@ -1,4 +1,4 @@
-import type { ReactTestInstance } from 'react-test-renderer';
+import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import * as React from 'react';
import type { TextMatch } from '../matches';
import type { TextMatchOptions } from './byText';
@@ -119,21 +119,21 @@ export const UNSAFE_queryAllByProps = (
}
};
-export const queryByAPI = (instance: ReactTestInstance): QueryByAPI => ({
- queryByTestId: queryByTestId(instance),
- queryByText: queryByText(instance),
- queryByPlaceholderText: queryByPlaceholderText(instance),
- queryByDisplayValue: queryByDisplayValue(instance),
- queryAllByTestId: queryAllByTestId(instance),
- queryAllByText: queryAllByText(instance),
- queryAllByPlaceholderText: queryAllByPlaceholderText(instance),
- queryAllByDisplayValue: queryAllByDisplayValue(instance),
+export const queryByAPI = (renderer: ReactTestRenderer): QueryByAPI => ({
+ queryByTestId: queryByTestId(renderer),
+ queryByText: queryByText(renderer),
+ queryByPlaceholderText: queryByPlaceholderText(renderer),
+ queryByDisplayValue: queryByDisplayValue(renderer),
+ queryAllByTestId: queryAllByTestId(renderer),
+ queryAllByText: queryAllByText(renderer),
+ queryAllByPlaceholderText: queryAllByPlaceholderText(renderer),
+ queryAllByDisplayValue: queryAllByDisplayValue(renderer),
// Unsafe
- UNSAFE_queryByType: UNSAFE_queryByType(instance),
- UNSAFE_queryAllByType: UNSAFE_queryAllByType(instance),
- UNSAFE_queryByProps: UNSAFE_queryByProps(instance),
- UNSAFE_queryAllByProps: UNSAFE_queryAllByProps(instance),
+ UNSAFE_queryByType: UNSAFE_queryByType(renderer.root),
+ UNSAFE_queryAllByType: UNSAFE_queryAllByType(renderer.root),
+ UNSAFE_queryByProps: UNSAFE_queryByProps(renderer.root),
+ UNSAFE_queryAllByProps: UNSAFE_queryAllByProps(renderer.root),
// Removed
queryByName: () =>
diff --git a/src/render.tsx b/src/render.tsx
index 1f446900a..8491811e2 100644
--- a/src/render.tsx
+++ b/src/render.tsx
@@ -44,9 +44,9 @@ export default function render(
addToCleanupQueue(unmount);
return {
- ...getByAPI(instance),
- ...queryByAPI(instance),
- ...findByAPI(instance),
+ ...getByAPI(renderer),
+ ...queryByAPI(renderer),
+ ...findByAPI(renderer),
...a11yAPI(instance),
update,
unmount,