1
1
'use strict' ;
2
2
3
3
const assert = require ( 'assert' ) ;
4
+ const entries = require ( 'object.entries' ) ;
4
5
const eslint = require ( 'eslint' ) ;
6
+ const fromEntries = require ( 'object.fromentries' ) ;
5
7
const values = require ( 'object.values' ) ;
6
8
7
9
const Components = require ( '../../lib/util/Components' ) ;
@@ -19,12 +21,32 @@ const ruleTester = new eslint.RuleTester({
19
21
20
22
describe ( 'Components' , ( ) => {
21
23
describe ( 'static detect' , ( ) => {
22
- function testComponentsDetect ( test , done ) {
23
- const rule = Components . detect ( ( context , components , util ) => ( {
24
- 'Program:exit' ( ) {
25
- done ( context , components , util ) ;
26
- } ,
27
- } ) ) ;
24
+ function testComponentsDetect ( test , instructionsOrDone , orDone ) {
25
+ const done = orDone || instructionsOrDone ;
26
+ const instructions = orDone ? instructionsOrDone : instructionsOrDone ;
27
+
28
+ const rule = Components . detect ( ( _context , components , util ) => {
29
+ const instructionResults = [ ] ;
30
+
31
+ const augmentedInstructions = fromEntries (
32
+ entries ( instructions || { } ) . map ( ( nodeTypeAndHandler ) => {
33
+ const nodeType = nodeTypeAndHandler [ 0 ] ;
34
+ const handler = nodeTypeAndHandler [ 1 ] ;
35
+ return [ nodeType , ( node ) => {
36
+ instructionResults . push ( { type : nodeType , result : handler ( node , context , components , util ) } ) ;
37
+ } ] ;
38
+ } )
39
+ ) ;
40
+
41
+ return Object . assign ( { } , augmentedInstructions , {
42
+ 'Program:exit' ( node ) {
43
+ if ( augmentedInstructions [ 'Program:exit' ] ) {
44
+ augmentedInstructions [ 'Program:exit' ] ( node , context , components , util ) ;
45
+ }
46
+ done ( components , instructionResults ) ;
47
+ } ,
48
+ } ) ;
49
+ } ) ;
28
50
29
51
const tests = {
30
52
valid : parsers . all ( [ Object . assign ( { } , test , {
@@ -36,6 +58,7 @@ describe('Components', () => {
36
58
} ) ] ) ,
37
59
invalid : [ ] ,
38
60
} ;
61
+
39
62
ruleTester . run ( test . code , rule , tests ) ;
40
63
}
41
64
@@ -45,7 +68,7 @@ describe('Components', () => {
45
68
function MyStatelessComponent() {
46
69
return <React.Fragment />;
47
70
}` ,
48
- } , ( _context , components ) => {
71
+ } , ( components ) => {
49
72
assert . equal ( components . length ( ) , 1 , 'MyStatelessComponent should be detected component' ) ;
50
73
values ( components . list ( ) ) . forEach ( ( component ) => {
51
74
assert . equal (
@@ -65,7 +88,7 @@ describe('Components', () => {
65
88
return <React.Fragment />;
66
89
}
67
90
}` ,
68
- } , ( _context , components ) => {
91
+ } , ( components ) => {
69
92
assert ( components . length ( ) === 1 , 'MyClassComponent should be detected component' ) ;
70
93
values ( components . list ( ) ) . forEach ( ( component ) => {
71
94
assert . equal (
@@ -80,7 +103,7 @@ describe('Components', () => {
80
103
it ( 'should detect React Imports' , ( ) => {
81
104
testComponentsDetect ( {
82
105
code : 'import React, { useCallback, useState } from \'react\'' ,
83
- } , ( _context , components ) => {
106
+ } , ( components ) => {
84
107
assert . deepEqual (
85
108
components . getDefaultReactImports ( ) . map ( ( specifier ) => specifier . local . name ) ,
86
109
[ 'React' ] ,
@@ -94,5 +117,186 @@ describe('Components', () => {
94
117
) ;
95
118
} ) ;
96
119
} ) ;
120
+
121
+ describe ( 'utils' , ( ) => {
122
+ describe ( 'isReactHookCall' , ( ) => {
123
+ it ( 'should not identify hook-like call' , ( ) => {
124
+ testComponentsDetect ( {
125
+ code : `import { useRef } from 'react'
126
+ function useColor() {
127
+ return useState()
128
+ }` ,
129
+ } , {
130
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
131
+ } , ( _components , instructionResults ) => {
132
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : false } ] ) ;
133
+ } ) ;
134
+ } ) ;
135
+
136
+ it ( 'should identify hook call' , ( ) => {
137
+ testComponentsDetect ( {
138
+ code : `import { useState } from 'react'
139
+ function useColor() {
140
+ return useState()
141
+ }` ,
142
+ } , {
143
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
144
+ } , ( _components , instructionResults ) => {
145
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
146
+ } ) ;
147
+ } ) ;
148
+
149
+ it ( 'should identify aliased hook call' , ( ) => {
150
+ testComponentsDetect ( {
151
+ code : `import { useState as useStateAlternative } from 'react'
152
+ function useColor() {
153
+ return useStateAlternative()
154
+ }` ,
155
+ } , {
156
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
157
+ } , ( _components , instructionResults ) => {
158
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
159
+ } ) ;
160
+ } ) ;
161
+
162
+ it ( 'should identify aliased present named hook call' , ( ) => {
163
+ testComponentsDetect ( {
164
+ code : `import { useState as useStateAlternative } from 'react'
165
+ function useColor() {
166
+ return useStateAlternative()
167
+ }` ,
168
+ } , {
169
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node , [ 'useState' ] ) ,
170
+ } , ( _components , instructionResults ) => {
171
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
172
+ } ) ;
173
+ } ) ;
174
+
175
+ it ( 'should not identify shadowed hook call' , ( ) => {
176
+ testComponentsDetect ( {
177
+ code : `import { useState } from 'react'
178
+ function useColor() {
179
+ function useState() {
180
+ return null
181
+ }
182
+ return useState()
183
+ }` ,
184
+ } , {
185
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
186
+ } , ( _components , instructionResults ) => {
187
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : false } ] ) ;
188
+ } ) ;
189
+ } ) ;
190
+
191
+ it ( 'should not identify shadowed aliased present named hook call' , ( ) => {
192
+ testComponentsDetect ( {
193
+ code : `import { useState as useStateAlternative } from 'react'
194
+ function useColor() {
195
+ function useStateAlternative() {
196
+ return null
197
+ }
198
+ return useStateAlternative()
199
+ }` ,
200
+ } , {
201
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node , [ 'useState' ] ) ,
202
+ } , ( _components , instructionResults ) => {
203
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : false } ] ) ;
204
+ } ) ;
205
+ } ) ;
206
+
207
+ it ( 'should identify React hook call' , ( ) => {
208
+ testComponentsDetect ( {
209
+ code : `import React from 'react'
210
+ function useColor() {
211
+ return React.useState()
212
+ }` ,
213
+ } , {
214
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
215
+ } , ( _components , instructionResults ) => {
216
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
217
+ } ) ;
218
+ } ) ;
219
+
220
+ it ( 'should identify aliased React hook call' , ( ) => {
221
+ testComponentsDetect ( {
222
+ code : `import ReactAlternative from 'react'
223
+ function useColor() {
224
+ return ReactAlternative.useState()
225
+ }` ,
226
+ } , {
227
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
228
+ } , ( _components , instructionResults ) => {
229
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
230
+ } ) ;
231
+ } ) ;
232
+
233
+ it ( 'should not identify shadowed React hook call' , ( ) => {
234
+ testComponentsDetect ( {
235
+ code : `import React from 'react'
236
+ function useColor() {
237
+ const React = {
238
+ useState: () => null
239
+ }
240
+ return React.useState()
241
+ }` ,
242
+ } , {
243
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node ) ,
244
+ } , ( _components , instructionResults ) => {
245
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : false } ] ) ;
246
+ } ) ;
247
+ } ) ;
248
+
249
+ it ( 'should identify present named hook call' , ( ) => {
250
+ testComponentsDetect ( {
251
+ code : `import { useState } from 'react'
252
+ function useColor() {
253
+ return useState()
254
+ }` ,
255
+ } , {
256
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node , [ 'useState' ] ) ,
257
+ } , ( _components , instructionResults ) => {
258
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
259
+ } ) ;
260
+ } ) ;
261
+
262
+ it ( 'should identify present named React hook call' , ( ) => {
263
+ testComponentsDetect ( {
264
+ code : `import React from 'react'
265
+ function useColor() {
266
+ return React.useState()
267
+ }` ,
268
+ } , {
269
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node , [ 'useState' ] ) ,
270
+ } , ( _components , instructionResults ) => {
271
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : true } ] ) ;
272
+ } ) ;
273
+ } ) ;
274
+
275
+ it ( 'should not identify missing named hook call' , ( ) => {
276
+ testComponentsDetect ( {
277
+ code : `import { useState } from 'react'
278
+ function useColor() {
279
+ return useState()
280
+ }` ,
281
+ } , {
282
+ CallExpression : ( node , _context , _components , util ) => util . isReactHookCall ( node , [ 'useRef' ] ) ,
283
+ } , ( _components , instructionResults ) => {
284
+ assert . deepEqual ( instructionResults , [ { type : 'CallExpression' , result : false } ] ) ;
285
+ } ) ;
286
+ } ) ;
287
+ } ) ;
288
+ } ) ;
289
+
290
+ describe ( 'testComponentsDetect' , ( ) => {
291
+ it ( 'should log Program:exit instruction' , ( ) => {
292
+ testComponentsDetect ( {
293
+ code : '' ,
294
+ } , {
295
+ 'Program:exit' : ( ) => true ,
296
+ } , ( _components , instructionResults ) => {
297
+ assert . deepEqual ( instructionResults , [ { type : 'Program:exit' , result : true } ] ) ;
298
+ } ) ;
299
+ } ) ;
300
+ } ) ;
97
301
} ) ;
98
302
} ) ;
0 commit comments