-
Notifications
You must be signed in to change notification settings - Fork 272
/
Copy pathcomponent-tree.ts
149 lines (127 loc) · 3.69 KB
/
component-tree.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { ReactTestInstance } from 'react-test-renderer';
/**
* Checks if the given element is a host element.
* @param element The element to check.
*/
export function isHostElement(element?: ReactTestInstance | null): boolean {
return typeof element?.type === 'string';
}
/**
* Returns first host ancestor for given element.
* @param element The element start traversing from.
*/
export function getHostParent(
element: ReactTestInstance | null
): ReactTestInstance | null {
if (element == null) {
return null;
}
let current = element.parent;
while (current) {
if (isHostElement(current)) {
return current;
}
current = current.parent;
}
return null;
}
/**
* Returns host children for given element.
* @param element The element start traversing from.
*/
export function getHostChildren(
element: ReactTestInstance | null
): ReactTestInstance[] {
if (element == null) {
return [];
}
const hostChildren: ReactTestInstance[] = [];
element.children.forEach((child) => {
if (typeof child !== 'object') {
return;
}
if (isHostElement(child)) {
hostChildren.push(child);
} else {
hostChildren.push(...getHostChildren(child));
}
});
return hostChildren;
}
/**
* Return a single host element that represent the passed host or composite element.
*
* @param element The element start traversing from.
* @throws Error if the passed element is a composite element and has no host children or has more than one host child.
* @returns If the passed element is a host element, it will return itself, if the passed element is a composite
* element, it will return a single host descendant.
*/
export function getHostSelf(
element: ReactTestInstance | null
): ReactTestInstance {
const hostSelves = getHostSelves(element);
if (hostSelves.length === 0) {
throw new Error(`Expected exactly one host element, but found none.`);
}
if (hostSelves.length > 1) {
throw new Error(
`Expected exactly one host element, but found ${hostSelves.length}.`
);
}
return hostSelves[0];
}
/**
* Return the array of host elements that represent the passed element.
*
* @param element The element start traversing from.
* @returns If the passed element is a host element, it will return an array containing only that element,
* if the passed element is a composite element, it will return an array containing its host children (zero, one or many).
*/
export function getHostSelves(
element: ReactTestInstance | null
): ReactTestInstance[] {
return typeof element?.type === 'string'
? [element]
: getHostChildren(element);
}
/**
* Returns host siblings for given element.
* @param element The element start traversing from.
*/
export function getHostSiblings(
element: ReactTestInstance | null
): ReactTestInstance[] {
const hostParent = getHostParent(element);
const hostSelves = getHostSelves(element);
return getHostChildren(hostParent).filter(
(sibling) => !hostSelves.includes(sibling)
);
}
export function getCompositeParentOfType(
element: ReactTestInstance,
type: React.ComponentType
) {
let current = element.parent;
while (!isHostElement(current)) {
// We're at the root of the tree
if (!current) {
return null;
}
if (current.type === type) {
return current;
}
current = current.parent;
}
return null;
}
/**
* Note: this function should be generally used for core React Native types like `View`, `Text`, `TextInput`, etc.
*/
export function isHostElementForType(
element: ReactTestInstance,
type: React.ComponentType
) {
// Not a host element
if (!isHostElement(element)) return false;
return getCompositeParentOfType(element, type) !== null;
}