Skip to content

Commit 2a0477e

Browse files
pierrezimmermannbampierrezimmermannmdjastrzebski
authored
refactor: type all queries to return host components (#1426)
* refactor: type all queries to return host components * refactor: use brackets syntax for array types over Array * refactor: change order of checks in findAll for perf * refactor: hide host instance from public API * refactor: make sure all queries return host elements * Revert "refactor: make sure all queries return host elements" This reverts commit 979f92a83e43b04a30c979445633896ec3496769. * chore: rename to HostTestInstance * refactor: remove flierNodeByType * chore: fix codecov --------- Co-authored-by: pierrezimmermann <pierrez@nam.tech> Co-authored-by: Maciej Jastrzębski <mdjastrzebski@gmail.com>
1 parent 62a9b57 commit 2a0477e

16 files changed

+66
-92
lines changed

src/fireEvent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {
77
ScrollViewProps,
88
} from 'react-native';
99
import act from './act';
10-
import { isPointerEventEnabled } from './helpers/pointer-events';
1110
import { isHostElement } from './helpers/component-tree';
1211
import { isHostTextInput } from './helpers/host-component-names';
12+
import { isPointerEventEnabled } from './helpers/pointer-events';
1313

1414
type EventHandler = (...args: unknown[]) => unknown;
1515

src/helpers/component-tree.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22

3+
/**
4+
* ReactTestInstance referring to host element.
5+
*/
6+
export type HostTestInstance = ReactTestInstance & { type: string };
7+
38
/**
49
* Checks if the given element is a host element.
510
* @param element The element to check.
611
*/
7-
export function isHostElement(element?: ReactTestInstance | null) {
12+
export function isHostElement(
13+
element?: ReactTestInstance | null
14+
): element is HostTestInstance {
815
return typeof element?.type === 'string';
916
}
1017

@@ -14,7 +21,7 @@ export function isHostElement(element?: ReactTestInstance | null) {
1421
*/
1522
export function getHostParent(
1623
element: ReactTestInstance | null
17-
): ReactTestInstance | null {
24+
): HostTestInstance | null {
1825
if (element == null) {
1926
return null;
2027
}
@@ -37,12 +44,12 @@ export function getHostParent(
3744
*/
3845
export function getHostChildren(
3946
element: ReactTestInstance | null
40-
): ReactTestInstance[] {
47+
): HostTestInstance[] {
4148
if (element == null) {
4249
return [];
4350
}
4451

45-
const hostChildren: ReactTestInstance[] = [];
52+
const hostChildren: HostTestInstance[] = [];
4653

4754
element.children.forEach((child) => {
4855
if (typeof child !== 'object') {
@@ -68,10 +75,8 @@ export function getHostChildren(
6875
*/
6976
export function getHostSelves(
7077
element: ReactTestInstance | null
71-
): ReactTestInstance[] {
72-
return typeof element?.type === 'string'
73-
? [element]
74-
: getHostChildren(element);
78+
): HostTestInstance[] {
79+
return isHostElement(element) ? [element] : getHostChildren(element);
7580
}
7681

7782
/**
@@ -80,7 +85,7 @@ export function getHostSelves(
8085
*/
8186
export function getHostSiblings(
8287
element: ReactTestInstance | null
83-
): ReactTestInstance[] {
88+
): HostTestInstance[] {
8489
const hostParent = getHostParent(element);
8590
const hostSelves = getHostSelves(element);
8691
return getHostChildren(hostParent).filter(

src/helpers/filterNodeByType.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/helpers/findAll.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { getConfig } from '../config';
33
import { isHiddenFromAccessibility } from './accessiblity';
4+
import { HostTestInstance, isHostElement } from './component-tree';
45

56
interface FindAllOptions {
67
/** Match elements hidden from accessibility */
@@ -17,7 +18,7 @@ export function findAll(
1718
root: ReactTestInstance,
1819
predicate: (element: ReactTestInstance) => boolean,
1920
options?: FindAllOptions
20-
) {
21+
): HostTestInstance[] {
2122
const results = findAllInternal(root, predicate, options);
2223

2324
const includeHiddenElements =
@@ -41,11 +42,11 @@ function findAllInternal(
4142
root: ReactTestInstance,
4243
predicate: (element: ReactTestInstance) => boolean,
4344
options?: FindAllOptions
44-
): Array<ReactTestInstance> {
45-
const results: ReactTestInstance[] = [];
45+
): HostTestInstance[] {
46+
const results: HostTestInstance[] = [];
4647

4748
// Match descendants first but do not add them to results yet.
48-
const matchingDescendants: ReactTestInstance[] = [];
49+
const matchingDescendants: HostTestInstance[] = [];
4950
root.children.forEach((child) => {
5051
if (typeof child === 'string') {
5152
return;
@@ -56,6 +57,7 @@ function findAllInternal(
5657
if (
5758
// When matchDeepestOnly = true: add current element only if no descendants match
5859
(!options?.matchDeepestOnly || matchingDescendants.length === 0) &&
60+
isHostElement(root) &&
5961
predicate(root)
6062
) {
6163
results.push(root);

src/helpers/host-component-names.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ReactTestInstance } from 'react-test-renderer';
33
import { Switch, Text, TextInput, View } from 'react-native';
44
import { configureInternal, getConfig, HostComponentNames } from '../config';
55
import { renderWithAct } from '../render-act';
6+
import { HostTestInstance } from './component-tree';
67

78
const userConfigErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly.
89
Please check if you are using compatible versions of React Native and React Native Testing Library.`;
@@ -66,10 +67,22 @@ function getByTestId(instance: ReactTestInstance, testID: string) {
6667
return nodes[0];
6768
}
6869

69-
export function isHostText(element?: ReactTestInstance) {
70+
/**
71+
* Checks if the given element is a host Text.
72+
* @param element The element to check.
73+
*/
74+
export function isHostText(
75+
element?: ReactTestInstance | null
76+
): element is HostTestInstance {
7077
return element?.type === getHostComponentNames().text;
7178
}
7279

73-
export function isHostTextInput(element?: ReactTestInstance) {
80+
/**
81+
* Checks if the given element is a host TextInput.
82+
* @param element The element to check.
83+
*/
84+
export function isHostTextInput(
85+
element?: ReactTestInstance | null
86+
): element is HostTestInstance {
7487
return element?.type === getHostComponentNames().textInput;
7588
}

src/helpers/matchers/matchLabelText.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ function matchAccessibilityLabelledBy(
4343
findAll(
4444
root,
4545
(node) =>
46-
typeof node.type === 'string' &&
4746
node.props.nativeID === nativeId &&
4847
matchTextContent(node, text, options)
4948
).length > 0

src/queries/a11yState.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ import { CommonQueryOptions } from './options';
1919

2020
const queryAllByA11yState = (
2121
instance: ReactTestInstance
22-
): ((
23-
matcher: AccessibilityStateMatcher,
24-
queryOptions?: CommonQueryOptions
25-
) => Array<ReactTestInstance>) =>
22+
): QueryAllByQuery<AccessibilityStateMatcher, CommonQueryOptions> =>
2623
function queryAllByA11yStateFn(matcher, queryOptions) {
2724
return findAll(
2825
instance,
29-
(node) =>
30-
typeof node.type === 'string' && matchAccessibilityState(node, matcher),
26+
(node) => matchAccessibilityState(node, matcher),
3127
queryOptions
3228
);
3329
};

src/queries/a11yValue.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ import { CommonQueryOptions } from './options';
1919

2020
const queryAllByA11yValue = (
2121
instance: ReactTestInstance
22-
): ((
23-
value: AccessibilityValueMatcher,
24-
queryOptions?: CommonQueryOptions
25-
) => Array<ReactTestInstance>) =>
22+
): QueryAllByQuery<AccessibilityValueMatcher, CommonQueryOptions> =>
2623
function queryAllByA11yValueFn(value, queryOptions) {
2724
return findAll(
2825
instance,
29-
(node) =>
30-
typeof node.type === 'string' && matchAccessibilityValue(node, value),
26+
(node) => matchAccessibilityValue(node, value),
3127
queryOptions
3228
);
3329
};

src/queries/displayValue.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
2-
import { filterNodeByType } from '../helpers/filterNodeByType';
32
import { findAll } from '../helpers/findAll';
3+
import { isHostTextInput } from '../helpers/host-component-names';
44
import { matches, TextMatch, TextMatchOptions } from '../matches';
5-
import { getHostComponentNames } from '../helpers/host-component-names';
65
import { makeQueries } from './makeQueries';
76
import type {
87
FindAllByQuery,
@@ -16,32 +15,26 @@ import type { CommonQueryOptions } from './options';
1615

1716
type ByDisplayValueOptions = CommonQueryOptions & TextMatchOptions;
1817

19-
const getTextInputNodeByDisplayValue = (
18+
const matchDisplayValue = (
2019
node: ReactTestInstance,
2120
value: TextMatch,
2221
options: TextMatchOptions = {}
2322
) => {
2423
const { exact, normalizer } = options;
25-
const nodeValue =
26-
node.props.value !== undefined ? node.props.value : node.props.defaultValue;
24+
const nodeValue = node.props.value ?? node.props.defaultValue;
2725

28-
return (
29-
filterNodeByType(node, getHostComponentNames().textInput) &&
30-
matches(value, nodeValue, normalizer, exact)
31-
);
26+
return matches(value, nodeValue, normalizer, exact);
3227
};
3328

3429
const queryAllByDisplayValue = (
3530
instance: ReactTestInstance
36-
): ((
37-
displayValue: TextMatch,
38-
queryOptions?: ByDisplayValueOptions
39-
) => Array<ReactTestInstance>) =>
31+
): QueryAllByQuery<TextMatch, ByDisplayValueOptions> =>
4032
function queryAllByDisplayValueFn(displayValue, queryOptions) {
4133
return findAll(
4234
instance,
4335
(node) =>
44-
getTextInputNodeByDisplayValue(node, displayValue, queryOptions),
36+
isHostTextInput(node) &&
37+
matchDisplayValue(node, displayValue, queryOptions),
4538
queryOptions
4639
);
4740
};

src/queries/hintText.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,11 @@ const getNodeByHintText = (
2525

2626
const queryAllByHintText = (
2727
instance: ReactTestInstance
28-
): ((
29-
hint: TextMatch,
30-
queryOptions?: ByHintTextOptions
31-
) => Array<ReactTestInstance>) =>
28+
): QueryAllByQuery<TextMatch, ByHintTextOptions> =>
3229
function queryAllByA11yHintFn(hint, queryOptions) {
3330
return findAll(
3431
instance,
35-
(node) =>
36-
typeof node.type === 'string' &&
37-
getNodeByHintText(node, hint, queryOptions),
32+
(node) => getNodeByHintText(node, hint, queryOptions),
3833
queryOptions
3934
);
4035
};

src/queries/labelText.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ function queryAllByLabelText(instance: ReactTestInstance) {
1919
return (text: TextMatch, queryOptions?: ByLabelTextOptions) => {
2020
return findAll(
2121
instance,
22-
(node) =>
23-
typeof node.type === 'string' &&
24-
matchLabelText(instance, node, text, queryOptions),
22+
(node) => matchLabelText(instance, node, text, queryOptions),
2523
queryOptions
2624
);
2725
};

src/queries/placeholderText.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22
import { findAll } from '../helpers/findAll';
33
import { matches, TextMatch, TextMatchOptions } from '../matches';
4-
import { filterNodeByType } from '../helpers/filterNodeByType';
5-
import { getHostComponentNames } from '../helpers/host-component-names';
4+
import { isHostTextInput } from '../helpers/host-component-names';
65
import { makeQueries } from './makeQueries';
76
import type {
87
FindAllByQuery,
@@ -16,30 +15,24 @@ import type { CommonQueryOptions } from './options';
1615

1716
type ByPlaceholderTextOptions = CommonQueryOptions & TextMatchOptions;
1817

19-
const getTextInputNodeByPlaceholderText = (
18+
const matchPlaceholderText = (
2019
node: ReactTestInstance,
2120
placeholder: TextMatch,
2221
options: TextMatchOptions = {}
2322
) => {
2423
const { exact, normalizer } = options;
25-
26-
return (
27-
filterNodeByType(node, getHostComponentNames().textInput) &&
28-
matches(placeholder, node.props.placeholder, normalizer, exact)
29-
);
24+
return matches(placeholder, node.props.placeholder, normalizer, exact);
3025
};
3126

3227
const queryAllByPlaceholderText = (
3328
instance: ReactTestInstance
34-
): ((
35-
placeholder: TextMatch,
36-
queryOptions?: ByPlaceholderTextOptions
37-
) => Array<ReactTestInstance>) =>
29+
): QueryAllByQuery<TextMatch, ByPlaceholderTextOptions> =>
3830
function queryAllByPlaceholderFn(placeholder, queryOptions) {
3931
return findAll(
4032
instance,
4133
(node) =>
42-
getTextInputNodeByPlaceholderText(node, placeholder, queryOptions),
34+
isHostTextInput(node) &&
35+
matchPlaceholderText(node, placeholder, queryOptions),
4336
queryOptions
4437
);
4538
};

src/queries/role.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,12 @@ const matchAccessibilityValueIfNeeded = (
6161

6262
const queryAllByRole = (
6363
instance: ReactTestInstance
64-
): ((role: TextMatch, options?: ByRoleOptions) => Array<ReactTestInstance>) =>
64+
): QueryAllByQuery<TextMatch, ByRoleOptions> =>
6565
function queryAllByRoleFn(role, options) {
6666
return findAll(
6767
instance,
6868
(node) =>
6969
// run the cheapest checks first, and early exit to avoid unneeded computations
70-
typeof node.type === 'string' &&
7170
isAccessibilityElement(node) &&
7271
matchStringProp(node.props.accessibilityRole, role) &&
7372
matchAccessibleStateIfNeeded(node, options) &&

src/queries/testId.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,22 @@ import type { CommonQueryOptions } from './options';
1414

1515
type ByTestIdOptions = CommonQueryOptions & TextMatchOptions;
1616

17-
const getNodeByTestId = (
17+
const matchTestId = (
1818
node: ReactTestInstance,
19-
testID: TextMatch,
19+
testId: TextMatch,
2020
options: TextMatchOptions = {}
2121
) => {
2222
const { exact, normalizer } = options;
23-
return matches(testID, node.props.testID, normalizer, exact);
23+
return matches(testId, node.props.testID, normalizer, exact);
2424
};
2525

2626
const queryAllByTestId = (
2727
instance: ReactTestInstance
28-
): ((
29-
testId: TextMatch,
30-
queryOptions?: ByTestIdOptions
31-
) => Array<ReactTestInstance>) =>
28+
): QueryAllByQuery<TextMatch, ByTestIdOptions> =>
3229
function queryAllByTestIdFn(testId, queryOptions) {
3330
return findAll(
3431
instance,
35-
(node) =>
36-
typeof node.type === 'string' &&
37-
getNodeByTestId(node, testId, queryOptions),
32+
(node) => matchTestId(node, testId, queryOptions),
3833
queryOptions
3934
);
4035
};

src/queries/text.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
2-
import { filterNodeByType } from '../helpers/filterNodeByType';
32
import { findAll } from '../helpers/findAll';
4-
import { getHostComponentNames } from '../helpers/host-component-names';
3+
import { isHostText } from '../helpers/host-component-names';
54
import { matchTextContent } from '../helpers/matchers/matchTextContent';
65
import { TextMatch, TextMatchOptions } from '../matches';
76
import { makeQueries } from './makeQueries';
@@ -19,13 +18,11 @@ type ByTextOptions = CommonQueryOptions & TextMatchOptions;
1918

2019
const queryAllByText = (
2120
instance: ReactTestInstance
22-
): ((text: TextMatch, options?: ByTextOptions) => Array<ReactTestInstance>) =>
21+
): QueryAllByQuery<TextMatch, ByTextOptions> =>
2322
function queryAllByTextFn(text, options = {}) {
2423
return findAll(
2524
instance,
26-
(node) =>
27-
filterNodeByType(node, getHostComponentNames().text) &&
28-
matchTextContent(node, text, options),
25+
(node) => isHostText(node) && matchTextContent(node, text, options),
2926
{
3027
...options,
3128
matchDeepestOnly: true,

0 commit comments

Comments
 (0)