@nyby/detox-component-testing 1.4.2 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/DebugTree.tsx DELETED
@@ -1,280 +0,0 @@
1
- import React, {useCallback, useRef, useState} from 'react';
2
- import {Text, TextInput, View} from 'react-native';
3
-
4
- type Fiber = {
5
- type: any;
6
- memoizedProps: Record<string, any>;
7
- child: Fiber | null;
8
- sibling: Fiber | null;
9
- return: Fiber | null;
10
- tag: number;
11
- stateNode: any;
12
- };
13
-
14
- type TreeNode = {
15
- name: string;
16
- props?: Record<string, any>;
17
- text?: string;
18
- children?: TreeNode[];
19
- };
20
-
21
- // React fiber tags
22
- const FunctionComponent = 0;
23
- const ClassComponent = 1;
24
- const HostComponent = 5;
25
- const HostText = 6;
26
- const ForwardRef = 11;
27
- const MemoComponent = 14;
28
- const SimpleMemoComponent = 15;
29
-
30
- const DEFAULT_USEFUL_PROPS = new Set([
31
- 'testID',
32
- 'accessibilityLabel',
33
- 'accessibilityRole',
34
- 'title',
35
- 'name',
36
- 'variant',
37
- 'size',
38
- 'value',
39
- 'placeholder',
40
- 'disabled',
41
- 'selected',
42
- 'checked',
43
- 'visible',
44
- ]);
45
-
46
- const DEFAULT_SKIP_NAMES = new Set([
47
- 'StaticContainer',
48
- 'EnsureSingleNavigator',
49
- 'PreventRemoveProvider',
50
- 'NavigationContent',
51
- 'NavigationStateContext',
52
- 'ScreenStackHeaderConfig',
53
- 'RenderErrorBoundary',
54
- 'DebugTree',
55
- 'PressabilityDebugView',
56
- ]);
57
-
58
- const DEFAULT_NATIVE_DUPLICATES = new Set([
59
- 'RCTText',
60
- 'RCTView',
61
- 'RCTScrollView',
62
- 'RCTCustomScrollView',
63
- 'RCTScrollContentView',
64
- 'RCTSinglelineTextInputView',
65
- 'RCTUITextField',
66
- ]);
67
-
68
- function findFiberFromRef(ref: any): Fiber | null {
69
- if (!ref) return null;
70
-
71
- // React Native: __internalInstanceHandle is the fiber node
72
- if (ref.__internalInstanceHandle) {
73
- return ref.__internalInstanceHandle;
74
- }
75
-
76
- // React DEV builds
77
- if (ref._internalFiberInstanceHandleDEV) {
78
- return ref._internalFiberInstanceHandleDEV;
79
- }
80
-
81
- // React DOM style: __reactFiber$ key on the node
82
- for (const key of Object.getOwnPropertyNames(ref)) {
83
- if (key.startsWith('__reactFiber$')) {
84
- return ref[key];
85
- }
86
- }
87
-
88
- return null;
89
- }
90
-
91
- function getComponentName(fiber: Fiber): string | null {
92
- const {type, tag} = fiber;
93
-
94
- if (tag === HostText) return null;
95
-
96
- if (tag === HostComponent) {
97
- return typeof type === 'string' ? type : null;
98
- }
99
-
100
- if (tag === FunctionComponent || tag === ClassComponent || tag === SimpleMemoComponent) {
101
- return type?.displayName || type?.name || null;
102
- }
103
-
104
- if (tag === ForwardRef) {
105
- return type?.displayName || type?.render?.displayName || type?.render?.name || null;
106
- }
107
-
108
- if (tag === MemoComponent) {
109
- return type?.displayName || type?.type?.displayName || type?.type?.name || null;
110
- }
111
-
112
- return null;
113
- }
114
-
115
- function isUserComponent(fiber: Fiber): boolean {
116
- const {tag} = fiber;
117
- return (
118
- tag === FunctionComponent ||
119
- tag === ClassComponent ||
120
- tag === ForwardRef ||
121
- tag === MemoComponent ||
122
- tag === SimpleMemoComponent
123
- );
124
- }
125
-
126
- function extractProps(fiber: Fiber, usefulProps: Set<string>): Record<string, any> | undefined {
127
- const {memoizedProps} = fiber;
128
- if (!memoizedProps) return undefined;
129
-
130
- const result: Record<string, any> = {};
131
- let hasProps = false;
132
-
133
- for (const key of Object.keys(memoizedProps)) {
134
- if (key === 'children' || key === 'style') continue;
135
- if (usefulProps.has(key)) {
136
- const val = memoizedProps[key];
137
- if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
138
- result[key] = val;
139
- hasProps = true;
140
- }
141
- }
142
- }
143
-
144
- if (memoizedProps.onPress) {
145
- result.onPress = true;
146
- hasProps = true;
147
- }
148
-
149
- return hasProps ? result : undefined;
150
- }
151
-
152
- function getTextContent(fiber: Fiber): string | undefined {
153
- if (fiber.tag === HostText) {
154
- return typeof fiber.memoizedProps === 'string' ? fiber.memoizedProps : undefined;
155
- }
156
- if (typeof fiber.memoizedProps?.children === 'string') {
157
- return fiber.memoizedProps.children;
158
- }
159
- return undefined;
160
- }
161
-
162
- function shouldShow(fiber: Fiber, skipNames: Set<string>, nativeDuplicates: Set<string>): boolean {
163
- const name = getComponentName(fiber);
164
- if (!name) return false;
165
- if (skipNames.has(name)) return false;
166
- if (nativeDuplicates.has(name)) return false;
167
-
168
- if (isUserComponent(fiber)) return true;
169
-
170
- if (fiber.tag === HostComponent) {
171
- const props = fiber.memoizedProps;
172
- if (props?.testID || props?.accessibilityLabel) return true;
173
- if (typeof props?.children === 'string') return true;
174
- }
175
-
176
- return false;
177
- }
178
-
179
- function walkFiber(
180
- fiber: Fiber | null,
181
- collect: TreeNode[],
182
- usefulProps: Set<string>,
183
- skipNames: Set<string>,
184
- nativeDuplicates: Set<string>,
185
- ): void {
186
- if (!fiber) return;
187
-
188
- const name = getComponentName(fiber);
189
- const show = fiber.tag !== HostText && shouldShow(fiber, skipNames, nativeDuplicates);
190
-
191
- if (show && name) {
192
- const node: TreeNode = {name};
193
- const props = extractProps(fiber, usefulProps);
194
- if (props) node.props = props;
195
- const text = getTextContent(fiber);
196
- if (text) node.text = text;
197
-
198
- const childNodes: TreeNode[] = [];
199
- walkFiber(fiber.child, childNodes, usefulProps, skipNames, nativeDuplicates);
200
- if (childNodes.length > 0) node.children = childNodes;
201
-
202
- collect.push(node);
203
- } else {
204
- walkFiber(fiber.child, collect, usefulProps, skipNames, nativeDuplicates);
205
- }
206
-
207
- walkFiber(fiber.sibling, collect, usefulProps, skipNames, nativeDuplicates);
208
- }
209
-
210
- export function captureTree(
211
- ref: any,
212
- usefulProps: Set<string> = DEFAULT_USEFUL_PROPS,
213
- skipNames: Set<string> = DEFAULT_SKIP_NAMES,
214
- nativeDuplicates: Set<string> = DEFAULT_NATIVE_DUPLICATES,
215
- ): string {
216
- const fiber = findFiberFromRef(ref);
217
- if (!fiber) return '[]';
218
-
219
- const tree: TreeNode[] = [];
220
- walkFiber(fiber.child, tree, usefulProps, skipNames, nativeDuplicates);
221
-
222
- return JSON.stringify(tree);
223
- }
224
-
225
- export interface DebugTreeProps {
226
- children: React.ReactNode;
227
- /** Props to include in tree output. Defaults to testID, accessibilityLabel, etc. */
228
- usefulProps?: string[];
229
- /** Component names to skip (internal wrappers that add noise). */
230
- skipNames?: string[];
231
- /** Native component names that duplicate their parent (e.g. RCTText inside Text). */
232
- nativeDuplicates?: string[];
233
- }
234
-
235
- /**
236
- * Debug component that captures the React component tree on demand.
237
- * Wrap your test content with this. Trigger via Detox:
238
- *
239
- * await element(by.id('debug-tree-control')).replaceText('dump');
240
- * const attrs = await element(by.id('debug-tree-output')).getAttributes();
241
- */
242
- export function DebugTree({children, usefulProps, skipNames, nativeDuplicates}: DebugTreeProps) {
243
- const rootRef = useRef<View>(null);
244
- const [tree, setTree] = useState('');
245
-
246
- const usefulPropsSet = usefulProps ? new Set(usefulProps) : DEFAULT_USEFUL_PROPS;
247
- const skipNamesSet = skipNames ? new Set(skipNames) : DEFAULT_SKIP_NAMES;
248
- const nativeDuplicatesSet = nativeDuplicates
249
- ? new Set(nativeDuplicates)
250
- : DEFAULT_NATIVE_DUPLICATES;
251
-
252
- const handleCommand = useCallback(
253
- (text: string) => {
254
- if (text === 'dump' && rootRef.current) {
255
- const result = captureTree(
256
- rootRef.current,
257
- usefulPropsSet,
258
- skipNamesSet,
259
- nativeDuplicatesSet,
260
- );
261
- setTree(result);
262
- }
263
- },
264
- [usefulPropsSet, skipNamesSet, nativeDuplicatesSet],
265
- );
266
-
267
- return (
268
- <>
269
- <TextInput testID="debug-tree-control" onChangeText={handleCommand} style={{height: 1}} />
270
- <View ref={rootRef} style={{flex: 1}} collapsable={false}>
271
- {children}
272
- </View>
273
- {tree ? (
274
- <Text testID="debug-tree-output" style={{height: 1}}>
275
- {tree}
276
- </Text>
277
- ) : null}
278
- </>
279
- );
280
- }