1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactTestRendererFiber
10
+ * @preventMunge
11
+ * @flow
12
+ */
13
+
14
+'use strict';
15
+
16
+var ReactFiberReconciler = require('ReactFiberReconciler');
17
+var ReactGenericBatching = require('ReactGenericBatching');
18
+var emptyObject = require('emptyObject');
19
+
20
+import type { TestRendererOptions } from 'ReactTestMount';
21
+
22
+type ReactTestRendererJSON = {|
23
+ type : string,
24
+ props : {[propName: string] : string },
25
+ children : null | Array<ReactTestRendererNode>,
26
+ $$typeof ?: Symbol, // Optional because we add it with defineProperty().
27
+|};
28
+type ReactTestRendererNode = ReactTestRendererJSON | string;
29
+
30
+type Container = {|
31
+ children : Array<Instance | TextInstance>,
32
+ createNodeMock : Function,
33
+ tag : 'CONTAINER',
34
+|};
35
+
36
+type Props = Object;
37
+type Instance = {|
38
+ type : string,
39
+ props : Object,
40
+ children : Array<Instance | TextInstance>,
41
+ rootContainerInstance : Container,
42
+ tag : 'INSTANCE',
43
+|};
44
+
45
+type TextInstance = {|
46
+ text : string,
47
+ tag : 'TEXT',
48
+|};
49
+
50
+var TestRenderer = ReactFiberReconciler({
51
+ getRootHostContext() {
52
+ return emptyObject;
53
+ },
54
+
55
+ getChildHostContext() {
56
+ return emptyObject;
57
+ },
58
+
59
+ prepareForCommit() : void {
60
+ // noop
61
+ },
62
+
63
+ resetAfterCommit() : void {
64
+ // noop
65
+ },
66
+
67
+ createInstance(
68
+ type : string,
69
+ props : Props,
70
+ rootContainerInstance : Container,
71
+ hostContext : Object,
72
+ internalInstanceHandle : Object,
73
+ ) : Instance {
74
+ return {
75
+ type,
76
+ props,
77
+ children: [],
78
+ rootContainerInstance,
79
+ tag: 'INSTANCE',
80
+ };
81
+ },
82
+
83
+ appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
84
+ const index = parentInstance.children.indexOf(child);
85
+ if (index !== -1) {
86
+ parentInstance.children.splice(index, 1);
87
+ }
88
+ parentInstance.children.push(child);
89
+ },
90
+
91
+ finalizeInitialChildren(
92
+ testElement : Instance,
93
+ type : string,
94
+ props : Props,
95
+ rootContainerInstance : Container,
96
+ ) : boolean {
97
+ return false;
98
+ },
99
+
100
+ prepareUpdate(
101
+ testElement : Instance,
102
+ type : string,
103
+ oldProps : Props,
104
+ newProps : Props,
105
+ hostContext : Object,
106
+ ) : boolean {
107
+ return true;
108
+ },
109
+
110
+ commitUpdate(
111
+ instance : Instance,
112
+ type : string,
113
+ oldProps : Props,
114
+ newProps : Props,
115
+ rootContainerInstance : Container,
116
+ internalInstanceHandle : Object,
117
+ ) : void {
118
+ instance.type = type;
119
+ instance.props = newProps;
120
+ },
121
+
122
+ commitMount(
123
+ instance : Instance,
124
+ type : string,
125
+ newProps : Props,
126
+ rootContainerInstance : Object,
127
+ internalInstanceHandle : Object
128
+ ) : void {
129
+ // noop
130
+ },
131
+
132
+ shouldSetTextContent(props : Props) : boolean {
133
+ return false;
134
+ },
135
+
136
+ resetTextContent(testElement : Instance) : void {
137
+ // noop
138
+ },
139
+
140
+ createTextInstance(
141
+ text : string,
142
+ rootContainerInstance : Container,
143
+ hostContext : Object,
144
+ internalInstanceHandle : Object
145
+ ) : TextInstance {
146
+ return {
147
+ text,
148
+ tag: 'TEXT',
149
+ };
150
+ },
151
+
152
+ commitTextUpdate(textInstance : TextInstance, oldText : string, newText : string) : void {
153
+ textInstance.text = newText;
154
+ },
155
+
156
+ appendChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
157
+ const index = parentInstance.children.indexOf(child);
158
+ if (index !== -1) {
159
+ parentInstance.children.splice(index, 1);
160
+ }
161
+ parentInstance.children.push(child);
162
+ },
163
+
164
+ insertBefore(
165
+ parentInstance : Instance | Container,
166
+ child : Instance | TextInstance,
167
+ beforeChild : Instance | TextInstance
168
+ ) : void {
169
+ const index = parentInstance.children.indexOf(child);
170
+ if (index !== -1) {
171
+ parentInstance.children.splice(index, 1);
172
+ }
173
+ const beforeIndex = parentInstance.children.indexOf(beforeChild);
174
+ parentInstance.children.splice(beforeIndex, 0, child);
175
+ },
176
+
177
+ removeChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
178
+ const index = parentInstance.children.indexOf(child);
179
+ parentInstance.children.splice(index, 1);
180
+ },
181
+
182
+ scheduleAnimationCallback(fn : Function) : void {
183
+ setTimeout(fn);
184
+ },
185
+
186
+ scheduleDeferredCallback(fn : Function) : void {
187
+ setTimeout(fn, 0, {timeRemaining: Infinity});
188
+ },
189
+
190
+ useSyncScheduling: true,
191
+
192
+ getPublicInstance(inst) {
193
+ switch (inst.tag) {
194
+ case 'INSTANCE':
195
+ const createNodeMock = inst.rootContainerInstance.createNodeMock;
196
+ return createNodeMock({
197
+ type: inst.type,
198
+ props: inst.props,
199
+ });
200
+ default:
201
+ return inst;
202
+ }
203
+ },
204
+});
205
+
206
+var defaultTestOptions = {
207
+ createNodeMock: function() {
208
+ return null;
209
+ },
210
+};
211
+
212
+function toJSON(inst : Instance | TextInstance) : ReactTestRendererNode {
213
+ switch (inst.tag) {
214
+ case 'TEXT':
215
+ return inst.text;
216
+ case 'INSTANCE':
217
+ /* eslint-disable no-unused-vars */
218
+ // We don't include the `children` prop in JSON.
219
+ // Instead, we will include the actual rendered children.
220
+ const {children, ...props} = inst.props;
221
+ /* eslint-enable */
222
+ let renderedChildren = null;
223
+ if (inst.children && inst.children.length) {
224
+ renderedChildren = inst.children.map(toJSON);
225
+ }
226
+ const json : ReactTestRendererJSON = {
227
+ type: inst.type,
228
+ props: props,
229
+ children: renderedChildren,
230
+ };
231
+ Object.defineProperty(json, '$$typeof', {value: Symbol.for('react.test.json')});
232
+ return json;
233
+ default:
234
+ throw new Error(`Unexpected node type in toJSON: ${inst.tag}`);
235
+ }
236
+}
237
+
238
+var ReactTestFiberRenderer = {
239
+ create(element : ReactElement<any>, options : TestRendererOptions) {
240
+ var createNodeMock = defaultTestOptions.createNodeMock;
241
+ if (options && typeof options.createNodeMock === 'function') {
242
+ createNodeMock = options.createNodeMock;
243
+ }
244
+ var container = {
245
+ children: [],
246
+ createNodeMock,
247
+ tag: 'CONTAINER',
248
+ };
249
+ var root = TestRenderer.createContainer(container);
250
+ TestRenderer.updateContainer(element, root, null, null);
251
+
252
+ return {
253
+ toJSON() {
254
+ if (root == null || container == null) {
255
+ return null;
256
+ }
257
+ if (container.children.length === 0) {
258
+ return null;
259
+ }
260
+ if (container.children.length === 1) {
261
+ return toJSON(container.children[0]);
262
+ }
263
+ return container.children.map(toJSON);
264
+ },
265
+ update(newElement : ReactElement<any>) {
266
+ if (root == null) {
267
+ return;
268
+ }
269
+ TestRenderer.updateContainer(newElement, root, null, null);
270
+ },
271
+ unmount() {
272
+ if (root == null) {
273
+ return;
274
+ }
275
+ TestRenderer.updateContainer(null, root, null);
276
+ container = null;
277
+ root = null;
278
+ },
279
+ getInstance() {
280
+ if (root == null) {
281
+ return null;
282
+ }
283
+ return TestRenderer.getPublicRootInstance(root);
284
+ },
285
+ };
286
+ },
287
+
288
+ /* eslint-disable camelcase */
289
+ unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
290
+ /* eslint-enable camelcase */
291
+};
292
+
293
+module.exports = ReactTestFiberRenderer;