@@ -19,9 +19,11 @@ var emptyObject = require('fbjs/lib/emptyObject');
19
19
var ReactTypeOfWork = require ( 'ReactTypeOfWork' ) ;
20
20
var invariant = require ( 'fbjs/lib/invariant' ) ;
21
21
var {
22
+ IndeterminateComponent,
22
23
FunctionalComponent,
23
24
ClassComponent,
24
25
HostComponent,
26
+ Fragment,
25
27
HostText,
26
28
HostRoot,
27
29
} = ReactTypeOfWork ;
@@ -41,6 +43,7 @@ type ReactTestRendererNode = ReactTestRendererJSON | string;
41
43
type Container = { |
42
44
children : Array < Instance | TextInstance > ,
43
45
createNodeMock : Function ,
46
+ createComponentMock : Function ,
44
47
tag : 'CONTAINER' ,
45
48
| } ;
46
49
@@ -217,6 +220,29 @@ var TestRenderer = ReactFiberReconciler({
217
220
setTimeout ( fn , 0 , { timeRemaining : Infinity } ) ;
218
221
} ,
219
222
223
+ mockComponent ( component : Fiber , rootContainer : Container ) {
224
+ invariant (
225
+ component . _unmockedType === null ,
226
+ 'Trying to mock an already mocked component' ,
227
+ ) ;
228
+ const mockedFn = rootContainer . createComponentMock ( {
229
+ type : component . type ,
230
+ props : component . pendingProps ,
231
+ } ) ;
232
+ invariant (
233
+ typeof mockedFn === 'function' ,
234
+ 'createComponentMock() must return a function. Found %s instead.' ,
235
+ typeof mockedFn ,
236
+ ) ;
237
+ if ( mockedFn !== component . type ) {
238
+ component . _unmockedType = component . type ;
239
+ component . type = mockedFn ;
240
+ // force the fiber to be indeterminate so that users can mock a class component
241
+ // into a functional component and vice versa
242
+ component . tag = IndeterminateComponent ;
243
+ }
244
+ } ,
245
+
220
246
useSyncScheduling: true ,
221
247
222
248
getPublicInstance ( inst ) {
@@ -237,6 +263,9 @@ var defaultTestOptions = {
237
263
createNodeMock : function ( ) {
238
264
return null ;
239
265
} ,
266
+ createComponentMock : function ( component : { type : Function , props : any } ) {
267
+ return component . type ;
268
+ } ,
240
269
} ;
241
270
242
271
function toJSON ( inst : Instance | TextInstance ) : ReactTestRendererNode {
@@ -277,6 +306,46 @@ function nodeAndSiblingsArray(nodeWithSibling: ?Fiber) {
277
306
return array ;
278
307
}
279
308
309
+ function childrenToTree ( node ) {
310
+ if ( ! node ) {
311
+ return null ;
312
+ }
313
+ const children = nodeAndSiblingsArray ( node ) ;
314
+ if ( children . length === 0 ) {
315
+ return null ;
316
+ } else if ( children . length === 1 ) {
317
+ return toTree ( children [ 0 ] ) ;
318
+ } else {
319
+ return flatten ( children . map ( toTree ) ) ;
320
+ }
321
+ }
322
+
323
+ function flatten ( arr ) {
324
+ const result = [ ] ;
325
+ const stack = [ { i : 0 , array : arr } ] ;
326
+ while ( stack . length ) {
327
+ let n = stack . pop ( ) ;
328
+ while ( n . i < n . array . length ) {
329
+ const el = n . array [ n . i ] ;
330
+ n . i += 1 ;
331
+ if ( Array . isArray ( el ) ) {
332
+ stack . push ( n ) ;
333
+ stack . push ( { i : 0 , array : el } ) ;
334
+ break ;
335
+ }
336
+ result . push ( el ) ;
337
+ }
338
+ }
339
+ return result ;
340
+ }
341
+
342
+ function publicType ( node : Fiber ) {
343
+ if ( node . _unmockedType !== null ) {
344
+ return node . _unmockedType ;
345
+ }
346
+ return node . type ;
347
+ }
348
+
280
349
function toTree ( node : ?Fiber ) {
281
350
if ( node == null ) {
282
351
return null ;
@@ -287,26 +356,28 @@ function toTree(node: ?Fiber) {
287
356
case ClassComponent :
288
357
return {
289
358
nodeType : 'component ',
290
- type : node . type ,
359
+ type : publicType ( node ) ,
291
360
props : { ...node . memoizedProps } ,
292
361
instance : node . stateNode ,
293
- rendered : toTree ( node . child ) ,
362
+ rendered : childrenToTree ( node . child ) ,
294
363
} ;
364
+ case Fragment : // 10
365
+ return childrenToTree ( node . child ) ;
295
366
case FunctionalComponent : // 1
296
367
return {
297
368
nodeType : 'component' ,
298
- type : node . type ,
369
+ type : publicType ( node ) ,
299
370
props : { ...node . memoizedProps } ,
300
371
instance : null ,
301
- rendered : toTree ( node . child ) ,
372
+ rendered : childrenToTree ( node . child ) ,
302
373
} ;
303
374
case HostComponent : // 5
304
375
return {
305
376
nodeType : 'host' ,
306
377
type : node . type ,
307
378
props : { ...node . memoizedProps } ,
308
379
instance : null , // TODO: use createNodeMock here somehow?
309
- rendered : nodeAndSiblingsArray ( node . child ) . map ( toTree ) ,
380
+ rendered : flatten ( nodeAndSiblingsArray ( node . child ) . map ( toTree ) ) ,
310
381
} ;
311
382
case HostText : // 6
312
383
return node . stateNode . text ;
@@ -325,9 +396,14 @@ var ReactTestFiberRenderer = {
325
396
if ( options && typeof options . createNodeMock === 'function' ) {
326
397
createNodeMock = options . createNodeMock ;
327
398
}
399
+ var createComponentMock = defaultTestOptions . createComponentMock ;
400
+ if ( options && typeof options . createComponentMock === 'function' ) {
401
+ createComponentMock = options . createComponentMock ;
402
+ }
328
403
var container = {
329
404
children : [ ] ,
330
405
createNodeMock,
406
+ createComponentMock,
331
407
tag : 'CONTAINER' ,
332
408
} ;
333
409
var root : ?FiberRoot = TestRenderer . createContainer ( container ) ;
0 commit comments