1
- import type { ReactTestInstance } from 'react-test-renderer' ;
1
+ import type {
2
+ ReactTestInstance ,
3
+ ReactTestRenderer ,
4
+ ReactTestRendererTree ,
5
+ } from 'react-test-renderer' ;
2
6
import * as React from 'react' ;
3
7
import { matches , TextMatch } from '../matches' ;
4
8
import type { NormalizerFn } from '../matches' ;
5
9
import { makeQueries } from './makeQueries' ;
6
10
import type { Queries } from './makeQueries' ;
7
- import { filterNodeByType } from './filterNodeByType' ;
8
11
import { createLibraryNotSupportedError } from './errors' ;
9
12
10
13
export type TextMatchOptions = {
@@ -13,75 +16,117 @@ export type TextMatchOptions = {
13
16
} ;
14
17
15
18
const getChildrenAsText = (
16
- children : React . ReactChild [ ] ,
19
+ children : ReactTestRendererTree | ReactTestRendererTree [ ] | null ,
17
20
TextComponent : React . ComponentType
18
- ) => {
19
- const textContent : string [ ] = [ ] ;
20
- React . Children . forEach ( children , ( child ) => {
21
- if ( typeof child === 'string' ) {
22
- textContent . push ( child ) ;
23
- return ;
24
- }
21
+ ) : string [ ] => {
22
+ if ( ! children ) {
23
+ return [ ] ;
24
+ }
25
25
26
- if ( typeof child === 'number' ) {
27
- textContent . push ( child . toString ( ) ) ;
28
- return ;
26
+ if ( typeof children === 'string' ) {
27
+ return [ children ] ;
28
+ }
29
+
30
+ const textContent : string [ ] = [ ] ;
31
+ if ( ! Array . isArray ( children ) ) {
32
+ // Bail on traversing text children down the tree if current node (child)
33
+ // has no text. In such situations, react-test-renderer will traverse down
34
+ // this tree in a separate call and run this query again. As a result, the
35
+ // query will match the deepest text node that matches requested text.
36
+ if ( children . type === 'Text' ) {
37
+ return [ ] ;
29
38
}
30
39
31
- if ( child ?. props ?. children ) {
32
- // Bail on traversing text children down the tree if current node (child)
33
- // has no text. In such situations, react-test-renderer will traverse down
34
- // this tree in a separate call and run this query again. As a result, the
35
- // query will match the deepest text node that matches requested text.
36
- if ( filterNodeByType ( child , TextComponent ) ) {
37
- return ;
38
- }
40
+ return getChildrenAsText ( children . rendered , TextComponent ) ;
41
+ }
39
42
40
- if ( filterNodeByType ( child , React . Fragment ) ) {
41
- textContent . push (
42
- ...getChildrenAsText ( child . props . children , TextComponent )
43
- ) ;
44
- }
45
- }
46
- } ) ;
43
+ children . forEach ( ( child ) =>
44
+ textContent . push ( ...getChildrenAsText ( child , TextComponent ) )
45
+ ) ;
47
46
48
47
return textContent ;
49
48
} ;
50
49
51
- const getNodeByText = (
52
- node : ReactTestInstance ,
50
+ const getInstancesByText = (
51
+ tree : ReactTestRendererTree ,
53
52
text : TextMatch ,
54
53
options : TextMatchOptions = { }
55
- ) => {
54
+ ) : Omit < ReactTestInstance , 'parent' > [ ] => {
55
+ const instances : Omit < ReactTestInstance , 'parent' > [ ] = [ ] ;
56
+
56
57
try {
58
+ if ( ! tree . rendered ) {
59
+ return [ ] ;
60
+ }
61
+
62
+ if ( ! tree . instance ) {
63
+ if ( ! Array . isArray ( tree . rendered ) ) {
64
+ return [ ...getInstancesByText ( tree . rendered , text , options ) ] ;
65
+ }
66
+
67
+ tree . rendered . forEach ( ( rendered ) => {
68
+ instances . push ( ...getInstancesByText ( rendered , text , options ) ) ;
69
+ } ) ;
70
+ return instances ;
71
+ }
72
+
73
+ const textChildren : string [ ] = [ ] ;
57
74
const { Text } = require ( 'react-native' ) ;
58
- const isTextComponent = filterNodeByType ( node , Text ) ;
59
- if ( isTextComponent ) {
60
- const textChildren = getChildrenAsText ( node . props . children , Text ) ;
61
- if ( textChildren ) {
62
- const textToTest = textChildren . join ( '' ) ;
63
- const { exact, normalizer } = options ;
64
- return matches ( text , textToTest , normalizer , exact ) ;
75
+ if ( ! Array . isArray ( tree . rendered ) ) {
76
+ if ( tree . rendered . type === 'Text' ) {
77
+ textChildren . push ( ...getChildrenAsText ( tree . rendered . rendered , Text ) ) ;
65
78
}
79
+ instances . push ( ...getInstancesByText ( tree . rendered , text , options ) ) ;
80
+ } else {
81
+ tree . rendered . forEach ( ( child ) => {
82
+ if ( child . type === 'Text' ) {
83
+ textChildren . push ( ...getChildrenAsText ( child , Text ) ) ;
84
+ } else {
85
+ instances . push ( ...getInstancesByText ( child , text , options ) ) ;
86
+ }
87
+ } ) ;
66
88
}
67
- return false ;
89
+
90
+ if ( textChildren . length ) {
91
+ const textToTest = textChildren . join ( '' ) ;
92
+ const { exact, normalizer } = options ;
93
+ if ( matches ( text , textToTest , normalizer , exact ) ) {
94
+ instances . push ( tree . instance ) ;
95
+ }
96
+ }
97
+
98
+ return instances ;
68
99
} catch ( error ) {
69
100
throw createLibraryNotSupportedError ( error ) ;
70
101
}
71
102
} ;
72
103
73
104
const queryAllByText = (
74
- instance : ReactTestInstance
105
+ renderer : ReactTestRenderer
75
106
) : ( (
76
107
text : TextMatch ,
77
108
queryOptions ?: TextMatchOptions
78
109
) => Array < ReactTestInstance > ) =>
79
110
function queryAllByTextFn ( text , queryOptions ) {
80
- const results = instance . findAll ( ( node ) =>
81
- getNodeByText ( node , text , queryOptions )
82
- ) ;
111
+ const tree = renderer . toTree ( ) ;
112
+ const treeInstance = renderer . root ;
113
+
114
+ if ( ! tree ) {
115
+ return [ ] ;
116
+ }
117
+
118
+ const instancesFound = getInstancesByText ( tree , text , queryOptions ) ;
83
119
84
- return results ;
120
+ // Instances in the tree are not of type ReactTestInstance because they do not have parent
121
+ // This is problematic when firing events so we find in the root nodes the one that matches
122
+ return instancesFound . map ( ( instanceFound ) => {
123
+ return treeInstance . find ( ( treeInstance ) => {
124
+ return (
125
+ treeInstance . instance &&
126
+ treeInstance . instance . _reactInternals . stateNode === instanceFound
127
+ ) ;
128
+ } ) ;
129
+ } ) ;
85
130
} ;
86
131
87
132
const getMultipleError = ( text : TextMatch ) =>
0 commit comments