main
js 623 lines 14.5 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {ReactContext} from 'shared/ReactTypes';
11 import type {TransitionTypes} from 'react/src/ReactTransitionType';
12
13 import isArray from 'shared/isArray';
14 import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
15 import {
16 DefaultEventPriority,
17 NoEventPriority,
18 type EventPriority,
19 } from 'react-reconciler/src/ReactEventPriorities';
20 import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
21
22 export {default as rendererVersion} from 'shared/ReactVersion'; // TODO: Consider exporting the react-native version.
23 export const rendererPackageName = 'react-test-renderer';
24 export const extraDevToolsConfig = null;
25
26 export type Type = string;
27 export type Props = Object;
28 export type Container = {
29 children: Array<Instance | TextInstance>,
30 createNodeMock: Function,
31 tag: 'CONTAINER',
32 };
33 export type Instance = {
34 type: string,
35 props: Object,
36 isHidden: boolean,
37 children: Array<Instance | TextInstance>,
38 internalInstanceHandle: Object,
39 rootContainerInstance: Container,
40 tag: 'INSTANCE',
41 };
42 export type TextInstance = {
43 text: string,
44 isHidden: boolean,
45 tag: 'TEXT',
46 };
47 export type HydratableInstance = Instance | TextInstance;
48 export type PublicInstance = Instance | TextInstance;
49 export type HostContext = Object;
50 export type UpdatePayload = Object;
51 export type ChildSet = void; // Unused
52 export type TimeoutHandle = TimeoutID;
53 export type NoTimeout = -1;
54 export type EventResponder = any;
55
56 export type RendererInspectionConfig = $ReadOnly<{}>;
57 export type TransitionStatus = mixed;
58
59 export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
60 export * from 'react-reconciler/src/ReactFiberConfigWithNoHydration';
61 export * from 'react-reconciler/src/ReactFiberConfigWithNoTestSelectors';
62 export * from 'react-reconciler/src/ReactFiberConfigWithNoMicrotasks';
63 export * from 'react-reconciler/src/ReactFiberConfigWithNoResources';
64 export * from 'react-reconciler/src/ReactFiberConfigWithNoSingletons';
65
66 const NO_CONTEXT = {};
67 const nodeToInstanceMap = new WeakMap<any, Instance>();
68
69 if (__DEV__) {
70 Object.freeze(NO_CONTEXT);
71 }
72
73 export function getPublicInstance(inst: Instance | TextInstance): $FlowFixMe {
74 switch (inst.tag) {
75 case 'INSTANCE':
76 const createNodeMock = inst.rootContainerInstance.createNodeMock;
77 const mockNode = createNodeMock({
78 type: inst.type,
79 props: inst.props,
80 });
81 if (typeof mockNode === 'object' && mockNode !== null) {
82 nodeToInstanceMap.set(mockNode, inst);
83 }
84 return mockNode;
85 default:
86 return inst;
87 }
88 }
89
90 export function appendChild(
91 parentInstance: Instance | Container,
92 child: Instance | TextInstance,
93 ): void {
94 if (__DEV__) {
95 if (!isArray(parentInstance.children)) {
96 console.error(
97 'An invalid container has been provided. ' +
98 'This may indicate that another renderer is being used in addition to the test renderer. ' +
99 '(For example, ReactDOM.createPortal inside of a ReactTestRenderer tree.) ' +
100 'This is not supported.',
101 );
102 }
103 }
104 const index = parentInstance.children.indexOf(child);
105 if (index !== -1) {
106 parentInstance.children.splice(index, 1);
107 }
108 parentInstance.children.push(child);
109 }
110
111 export function insertBefore(
112 parentInstance: Instance | Container,
113 child: Instance | TextInstance,
114 beforeChild: Instance | TextInstance,
115 ): void {
116 const index = parentInstance.children.indexOf(child);
117 if (index !== -1) {
118 parentInstance.children.splice(index, 1);
119 }
120 const beforeIndex = parentInstance.children.indexOf(beforeChild);
121 parentInstance.children.splice(beforeIndex, 0, child);
122 }
123
124 export function removeChild(
125 parentInstance: Instance | Container,
126 child: Instance | TextInstance,
127 ): void {
128 const index = parentInstance.children.indexOf(child);
129 parentInstance.children.splice(index, 1);
130 }
131
132 export function clearContainer(container: Container): void {
133 container.children.splice(0);
134 }
135
136 export function getRootHostContext(
137 rootContainerInstance: Container,
138 ): HostContext {
139 return NO_CONTEXT;
140 }
141
142 export function getChildHostContext(
143 parentHostContext: HostContext,
144 type: string,
145 ): HostContext {
146 return NO_CONTEXT;
147 }
148
149 export function prepareForCommit(containerInfo: Container): null | Object {
150 // noop
151 return null;
152 }
153
154 export function resetAfterCommit(containerInfo: Container): void {
155 // noop
156 }
157
158 export function createInstance(
159 type: string,
160 props: Props,
161 rootContainerInstance: Container,
162 hostContext: Object,
163 internalInstanceHandle: Object,
164 ): Instance {
165 return {
166 type,
167 props,
168 isHidden: false,
169 children: [],
170 internalInstanceHandle,
171 rootContainerInstance,
172 tag: 'INSTANCE',
173 };
174 }
175
176 export function cloneMutableInstance(
177 instance: Instance,
178 keepChildren: boolean,
179 ): Instance {
180 return {
181 type: instance.type,
182 props: instance.props,
183 isHidden: instance.isHidden,
184 children: keepChildren ? instance.children : [],
185 internalInstanceHandle: null,
186 rootContainerInstance: instance.rootContainerInstance,
187 tag: 'INSTANCE',
188 };
189 }
190
191 export function appendInitialChild(
192 parentInstance: Instance,
193 child: Instance | TextInstance,
194 ): void {
195 const index = parentInstance.children.indexOf(child);
196 if (index !== -1) {
197 parentInstance.children.splice(index, 1);
198 }
199 parentInstance.children.push(child);
200 }
201
202 export function finalizeInitialChildren(
203 testElement: Instance,
204 type: string,
205 props: Props,
206 rootContainerInstance: Container,
207 hostContext: Object,
208 ): boolean {
209 return false;
210 }
211
212 export function shouldSetTextContent(type: string, props: Props): boolean {
213 return false;
214 }
215
216 export function createTextInstance(
217 text: string,
218 rootContainerInstance: Container,
219 hostContext: Object,
220 internalInstanceHandle: Object,
221 ): TextInstance {
222 return {
223 text,
224 isHidden: false,
225 tag: 'TEXT',
226 };
227 }
228
229 export function cloneMutableTextInstance(
230 textInstance: TextInstance,
231 ): TextInstance {
232 return {
233 text: textInstance.text,
234 isHidden: textInstance.isHidden,
235 tag: 'TEXT',
236 };
237 }
238
239 let currentUpdatePriority: EventPriority = NoEventPriority;
240 export function setCurrentUpdatePriority(newPriority: EventPriority): void {
241 currentUpdatePriority = newPriority;
242 }
243
244 export function getCurrentUpdatePriority(): EventPriority {
245 return currentUpdatePriority;
246 }
247
248 export function resolveUpdatePriority(): EventPriority {
249 if (currentUpdatePriority !== NoEventPriority) {
250 return currentUpdatePriority;
251 }
252 return DefaultEventPriority;
253 }
254
255 export function trackSchedulerEvent(): void {}
256 export function resolveEventType(): null | string {
257 return null;
258 }
259 export function resolveEventTimeStamp(): number {
260 return -1.1;
261 }
262 export function shouldAttemptEagerTransition(): boolean {
263 return false;
264 }
265
266 export const isPrimaryRenderer = false;
267 export const warnsIfNotActing = true;
268
269 export const scheduleTimeout = setTimeout;
270 export const cancelTimeout = clearTimeout;
271
272 export const noTimeout: -1 = -1;
273
274 // -------------------
275 // Mutation
276 // -------------------
277
278 export const supportsMutation = true;
279
280 export function commitUpdate(
281 instance: Instance,
282 type: string,
283 oldProps: Props,
284 newProps: Props,
285 internalInstanceHandle: Object,
286 ): void {
287 instance.type = type;
288 instance.props = newProps;
289 }
290
291 export function commitMount(
292 instance: Instance,
293 type: string,
294 newProps: Props,
295 internalInstanceHandle: Object,
296 ): void {
297 // noop
298 }
299
300 export function commitTextUpdate(
301 textInstance: TextInstance,
302 oldText: string,
303 newText: string,
304 ): void {
305 textInstance.text = newText;
306 }
307
308 export function resetTextContent(testElement: Instance): void {
309 // noop
310 }
311
312 export const appendChildToContainer = appendChild;
313 export const insertInContainerBefore = insertBefore;
314 export const removeChildFromContainer = removeChild;
315
316 export function hideInstance(instance: Instance): void {
317 instance.isHidden = true;
318 }
319
320 export function hideTextInstance(textInstance: TextInstance): void {
321 textInstance.isHidden = true;
322 }
323
324 export function unhideInstance(instance: Instance, props: Props): void {
325 instance.isHidden = false;
326 }
327
328 export function unhideTextInstance(
329 textInstance: TextInstance,
330 text: string,
331 ): void {
332 textInstance.isHidden = false;
333 }
334
335 export function applyViewTransitionName(
336 instance: Instance,
337 name: string,
338 className: ?string,
339 ): void {
340 // Noop
341 }
342
343 export function restoreViewTransitionName(
344 instance: Instance,
345 props: Props,
346 ): void {
347 // Noop
348 }
349
350 export function cancelViewTransitionName(
351 instance: Instance,
352 name: string,
353 props: Props,
354 ): void {
355 // Noop
356 }
357
358 export function cancelRootViewTransitionName(rootContainer: Container): void {
359 // Noop
360 }
361
362 export function restoreRootViewTransitionName(rootContainer: Container): void {
363 // Noop
364 }
365
366 export function cloneRootViewTransitionContainer(
367 rootContainer: Container,
368 ): Instance {
369 return {
370 type: 'ROOT',
371 props: {},
372 isHidden: false,
373 children: [],
374 internalInstanceHandle: null,
375 rootContainerInstance: rootContainer,
376 tag: 'INSTANCE',
377 };
378 }
379
380 export function removeRootViewTransitionClone(
381 rootContainer: Container,
382 clone: Instance,
383 ): void {
384 // Noop since it was never inserted anywhere.
385 }
386
387 export type InstanceMeasurement = null;
388
389 export function measureInstance(instance: Instance): InstanceMeasurement {
390 return null;
391 }
392
393 export function measureClonedInstance(instance: Instance): InstanceMeasurement {
394 return null;
395 }
396
397 export function wasInstanceInViewport(
398 measurement: InstanceMeasurement,
399 ): boolean {
400 return true;
401 }
402
403 export function hasInstanceChanged(
404 oldMeasurement: InstanceMeasurement,
405 newMeasurement: InstanceMeasurement,
406 ): boolean {
407 return false;
408 }
409
410 export function hasInstanceAffectedParent(
411 oldMeasurement: InstanceMeasurement,
412 newMeasurement: InstanceMeasurement,
413 ): boolean {
414 return false;
415 }
416
417 export function startViewTransition(
418 suspendedState: null | SuspendedState,
419 rootContainer: Container,
420 transitionTypes: null | TransitionTypes,
421 mutationCallback: () => void,
422 layoutCallback: () => void,
423 afterMutationCallback: () => void,
424 spawnedWorkCallback: () => void,
425 passiveCallback: () => mixed,
426 errorCallback: mixed => void,
427 blockedCallback: string => void, // Profiling-only
428 finishedAnimation: () => void, // Profiling-only
429 ): null | RunningViewTransition {
430 mutationCallback();
431 layoutCallback();
432 // Skip afterMutationCallback(). We don't need it since we're not animating.
433 spawnedWorkCallback();
434 // Skip passiveCallback(). Spawned work will schedule a task.
435 return null;
436 }
437
438 export type RunningViewTransition = null;
439
440 export function startGestureTransition(
441 suspendedState: null | SuspendedState,
442 rootContainer: Container,
443 timeline: GestureTimeline,
444 rangeStart: number,
445 rangeEnd: number,
446 transitionTypes: null | TransitionTypes,
447 mutationCallback: () => void,
448 animateCallback: () => void,
449 errorCallback: mixed => void,
450 finishedAnimation: () => void, // Profiling-only
451 ): null | RunningViewTransition {
452 mutationCallback();
453 animateCallback();
454 if (enableProfilerTimer) {
455 finishedAnimation();
456 }
457 return null;
458 }
459
460 export function stopViewTransition(transition: RunningViewTransition) {}
461
462 export function addViewTransitionFinishedListener(
463 transition: RunningViewTransition,
464 callback: () => void,
465 ) {
466 callback();
467 }
468
469 export type ViewTransitionInstance = null | {name: string, ...};
470
471 export function createViewTransitionInstance(
472 name: string,
473 ): ViewTransitionInstance {
474 return null;
475 }
476
477 export type FragmentInstanceType = null;
478
479 export function createFragmentInstance(
480 fragmentFiber: Object,
481 ): FragmentInstanceType {
482 return null;
483 }
484
485 export function updateFragmentInstanceFiber(
486 fragmentFiber: Object,
487 instance: FragmentInstanceType,
488 ): void {
489 // Noop
490 }
491
492 export function commitNewChildToFragmentInstance(
493 child: Instance,
494 fragmentInstance: FragmentInstanceType,
495 ): void {
496 // noop
497 }
498
499 export function deleteChildFromFragmentInstance(
500 child: Instance,
501 fragmentInstance: FragmentInstanceType,
502 ): void {
503 // Noop
504 }
505
506 export function getInstanceFromNode(mockNode: Object): Object | null {
507 const instance = nodeToInstanceMap.get(mockNode);
508 if (instance !== undefined) {
509 return instance.internalInstanceHandle;
510 }
511 return null;
512 }
513
514 export type GestureTimeline = null;
515
516 export function getCurrentGestureOffset(provider: GestureTimeline): number {
517 return 0;
518 }
519
520 export function beforeActiveInstanceBlur(internalInstanceHandle: Object) {
521 // noop
522 }
523
524 export function afterActiveInstanceBlur() {
525 // noop
526 }
527
528 export function preparePortalMount(portalInstance: Instance): void {
529 // noop
530 }
531
532 export function prepareScopeUpdate(scopeInstance: Object, inst: Object): void {
533 nodeToInstanceMap.set(scopeInstance, inst);
534 }
535
536 export function getInstanceFromScope(scopeInstance: Object): null | Object {
537 return nodeToInstanceMap.get(scopeInstance) || null;
538 }
539
540 export function detachDeletedInstance(node: Instance): void {
541 // noop
542 }
543
544 export function logRecoverableError(error: mixed): void {
545 // noop
546 }
547
548 export function requestPostPaintCallback(callback: (time: number) => void) {
549 // noop
550 }
551
552 export function maySuspendCommit(type: Type, props: Props): boolean {
553 return false;
554 }
555
556 export function maySuspendCommitOnUpdate(
557 type: Type,
558 oldProps: Props,
559 newProps: Props,
560 ): boolean {
561 return false;
562 }
563
564 export function maySuspendCommitInSyncRender(
565 type: Type,
566 props: Props,
567 ): boolean {
568 return false;
569 }
570
571 export function preloadInstance(
572 instance: Instance,
573 type: Type,
574 props: Props,
575 ): boolean {
576 // Return true to indicate it's already loaded
577 return true;
578 }
579
580 export opaque type SuspendedState = null;
581
582 export function startSuspendingCommit(): SuspendedState {
583 return null;
584 }
585
586 export function suspendInstance(
587 state: SuspendedState,
588 instance: Instance,
589 type: Type,
590 props: Props,
591 ): void {}
592
593 export function suspendOnActiveViewTransition(
594 state: SuspendedState,
595 container: Container,
596 ): void {}
597
598 export function waitForCommitToBeReady(
599 state: SuspendedState,
600 timeoutOffset: number,
601 ): null {
602 return null;
603 }
604
605 export function getSuspendedCommitReason(
606 state: SuspendedState,
607 rootContainer: Container,
608 ): null | string {
609 return null;
610 }
611
612 export const NotPendingTransition: TransitionStatus = null;
613 export const HostTransitionContext: ReactContext<TransitionStatus> = {
614 $$typeof: REACT_CONTEXT_TYPE,
615 Provider: null as any,
616 Consumer: null as any,
617 _currentValue: NotPendingTransition,
618 _currentValue2: NotPendingTransition,
619 _threadCount: 0,
620 };
621
622 export type FormInstance = Instance;
623 export function resetFormInstance(form: Instance): void {}