Skip to content

Commit ee70416

Browse files
Add createComponentMock option to test renderer
1 parent 77c7792 commit ee70416

File tree

6 files changed

+326
-7
lines changed

6 files changed

+326
-7
lines changed

scripts/fiber/tests-passing.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,8 @@ src/renderers/testing/__tests__/ReactTestRenderer-test.js
16901690
* toTree() renders complicated trees of composites and hosts
16911691
* can update text nodes when rendered as root
16921692
* can render and update root fragments
1693+
* allows createComponentMock option to be passed in, simulating shallow
1694+
* createComponentMock can mock out specific components
16931695

16941696
src/shared/utils/__tests__/KeyEscapeUtils-test.js
16951697
* should properly escape and wrap user defined keys

src/renderers/shared/fiber/ReactFiberBeginWork.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
9292
resumeMountClassInstance,
9393
updateClassInstance,
9494
} = ReactFiberClassComponent(
95+
config,
96+
hostContext,
9597
scheduleUpdate,
9698
getPriorityContext,
9799
memoizeProps,
98100
memoizeState,
99101
);
100102

103+
var hasMockingBehavior = typeof config.mockComponent === 'function';
104+
var mockComponent = config.mockComponent || (c => c.type);
105+
101106
function markChildAsProgressed(current, workInProgress, priorityLevel) {
102107
// We now have clones. Let's store them as the currently progressed work.
103108
workInProgress.progressedChild = workInProgress.child;
@@ -200,7 +205,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
200205
}
201206

202207
function updateFunctionalComponent(current, workInProgress) {
203-
var fn = workInProgress.type;
208+
var fn = hasMockingBehavior
209+
? mockComponent(workInProgress, hostContext.getRootHostContainer())
210+
: workInProgress.type;
204211
var nextProps = workInProgress.pendingProps;
205212

206213
const memoizedProps = workInProgress.memoizedProps;
@@ -450,7 +457,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
450457
'An indeterminate component should never have mounted. This error is ' +
451458
'likely caused by a bug in React. Please file an issue.'
452459
);
453-
var fn = workInProgress.type;
460+
var fn = hasMockingBehavior
461+
? mockComponent(workInProgress, hostContext.getRootHostContainer())
462+
: workInProgress.type;
454463
var props = workInProgress.pendingProps;
455464
var unmaskedContext = getUnmaskedContext(workInProgress);
456465
var context = getMaskedContext(workInProgress, unmaskedContext);

src/renderers/shared/fiber/ReactFiberClassComponent.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import type { Fiber } from 'ReactFiber';
1616
import type { PriorityLevel } from 'ReactPriorityLevel';
17+
import type { HostConfig } from 'ReactFiberReconciler';
18+
import type { HostContext } from 'ReactFiberHostContext';
1719

1820
var {
1921
Update,
@@ -40,13 +42,18 @@ var invariant = require('invariant');
4042

4143
const isArray = Array.isArray;
4244

43-
module.exports = function(
45+
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
46+
config : HostConfig<T, P, I, TI, PI, C, CX, PL>,
47+
hostContext : HostContext<C, CX>,
4448
scheduleUpdate : (fiber : Fiber, priorityLevel : PriorityLevel) => void,
4549
getPriorityContext : () => PriorityLevel,
4650
memoizeProps: (workInProgress : Fiber, props : any) => void,
4751
memoizeState: (workInProgress : Fiber, state : any) => void,
4852
) {
4953

54+
const hasMockingBehavior = typeof config.mockComponent === 'function';
55+
const mockComponent = config.mockComponent || (c => c.type);
56+
5057
// Class component state updater
5158
const updater = {
5259
isMounted,
@@ -232,7 +239,9 @@ module.exports = function(
232239
}
233240

234241
function constructClassInstance(workInProgress : Fiber) : any {
235-
const ctor = workInProgress.type;
242+
const ctor = hasMockingBehavior
243+
? mockComponent(workInProgress, hostContext.getRootHostContainer())
244+
: workInProgress.type;
236245
const props = workInProgress.pendingProps;
237246
const unmaskedContext = getUnmaskedContext(workInProgress);
238247
const needsContext = isContextConsumer(workInProgress);

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
100100
resetAfterCommit() : void,
101101

102102
useSyncScheduling ?: boolean,
103+
104+
mockComponent ?: Function,
103105
};
104106

105107
export type Reconciler<C, I, TI> = {

src/renderers/testing/ReactTestRendererFiber.js

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var {
2222
FunctionalComponent,
2323
ClassComponent,
2424
HostComponent,
25+
Fragment,
2526
HostText,
2627
HostRoot,
2728
} = ReactTypeOfWork;
@@ -41,6 +42,7 @@ type ReactTestRendererNode = ReactTestRendererJSON | string;
4142
type Container = {|
4243
children : Array<Instance | TextInstance>,
4344
createNodeMock : Function,
45+
createComponentMock: Function,
4446
tag : 'CONTAINER',
4547
|};
4648

@@ -200,6 +202,16 @@ var TestRenderer = ReactFiberReconciler({
200202
setTimeout(fn, 0, {timeRemaining: Infinity});
201203
},
202204

205+
mockComponent(component: Fiber, rootContainer: Container) : any {
206+
const mockedFn = rootContainer.createComponentMock(component);
207+
invariant(
208+
typeof mockedFn === 'function',
209+
'createComponentMock() must return a function. Found %s instead.',
210+
typeof mockedFn
211+
);
212+
return mockedFn;
213+
},
214+
203215
useSyncScheduling: true,
204216

205217
getPublicInstance(inst) {
@@ -220,6 +232,9 @@ var defaultTestOptions = {
220232
createNodeMock: function() {
221233
return null;
222234
},
235+
createComponentMock: function(component: Fiber) {
236+
return component.type;
237+
},
223238
};
224239

225240
function toJSON(inst : Instance | TextInstance) : ReactTestRendererNode {
@@ -258,6 +273,39 @@ function nodeAndSiblingsArray(nodeWithSibling: ?Fiber) {
258273
return array;
259274
}
260275

276+
function childrenToTree(node) {
277+
if (!node) {
278+
return null;
279+
}
280+
const children = nodeAndSiblingsArray(node);
281+
if (children.length === 0) {
282+
return null;
283+
} else if (children.length === 1) {
284+
return toTree(children[0]);
285+
} else {
286+
return flatten(children.map(toTree));
287+
}
288+
}
289+
290+
function flatten(arr) {
291+
const result = [];
292+
const stack = [{ i: 0, array: arr }];
293+
while (stack.length) {
294+
let n = stack.pop();
295+
while (n.i < n.array.length) {
296+
const el = n.array[n.i];
297+
n.i += 1;
298+
if (Array.isArray(el)) {
299+
stack.push(n);
300+
stack.push({ i: 0, array: el });
301+
break;
302+
}
303+
result.push(el);
304+
}
305+
}
306+
return result;
307+
}
308+
261309
function toTree(node: ?Fiber) {
262310
if (node == null) {
263311
return null;
@@ -271,23 +319,25 @@ function toTree(node: ?Fiber) {
271319
type: node.type,
272320
props: { ...node.memoizedProps },
273321
instance: node.stateNode,
274-
rendered: toTree(node.child),
322+
rendered: childrenToTree(node.child),
275323
};
324+
case Fragment: // 10
325+
return childrenToTree(node.child);
276326
case FunctionalComponent: // 1
277327
return {
278328
nodeType: 'component',
279329
type: node.type,
280330
props: { ...node.memoizedProps },
281331
instance: null,
282-
rendered: toTree(node.child),
332+
rendered: childrenToTree(node.child),
283333
};
284334
case HostComponent: // 5
285335
return {
286336
nodeType: 'host',
287337
type: node.type,
288338
props: { ...node.memoizedProps },
289339
instance: null, // TODO: use createNodeMock here somehow?
290-
rendered: nodeAndSiblingsArray(node.child).map(toTree),
340+
rendered: flatten(nodeAndSiblingsArray(node.child).map(toTree)),
291341
};
292342
case HostText: // 6
293343
return node.stateNode.text;
@@ -306,9 +356,14 @@ var ReactTestFiberRenderer = {
306356
if (options && typeof options.createNodeMock === 'function') {
307357
createNodeMock = options.createNodeMock;
308358
}
359+
var createComponentMock = defaultTestOptions.createComponentMock;
360+
if (options && typeof options.createComponentMock === 'function') {
361+
createComponentMock = options.createComponentMock;
362+
}
309363
var container = {
310364
children: [],
311365
createNodeMock,
366+
createComponentMock,
312367
tag: 'CONTAINER',
313368
};
314369
var root: ?FiberRoot = TestRenderer.createContainer(container);

0 commit comments

Comments
 (0)