Separate unit tests for ReactFabricHostComponent (#21969)
Timothy Yung committed
Jul 26, 2021 at 23:45 UTC
4cc8ec64c2c853146f35ee3c7d072fe23847a006
2 files changed
+220
-354
packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js
-354
@@ -13,14 +13,9 @@
13
let React;
14
let ReactFabric;
15
let createReactNativeComponentClass;
16
-let UIManager;
16
let StrictMode;
18
-let TextInputState;
17
let act;
18
21
-const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
22
- 'Warning: setNativeProps is not currently supported in Fabric';
23
-
19
const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
20
"Warning: dispatchCommand was called with a ref that isn't a " +
21
'native component. Use React.forwardRef to get access to the underlying native component';
@@ -42,12 +37,8 @@ describe('ReactFabric', () => {
37
React = require('react');
38
StrictMode = React.StrictMode;
39
ReactFabric = require('react-native-renderer/fabric');
45
- UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
46
- .UIManager;
40
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
41
.ReactNativeViewConfigRegistry.register;
49
- TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
50
- .TextInputState;
42
43
act = require('jest-react').act;
44
});
@@ -227,44 +218,6 @@ describe('ReactFabric', () => {
218
).toMatchSnapshot();
219
});
220
230
- it('should not call UIManager.updateView from ref.setNativeProps', () => {
231
- const View = createReactNativeComponentClass('RCTView', () => ({
232
- validAttributes: {foo: true},
233
- uiViewClassName: 'RCTView',
234
- }));
235
-
236
- UIManager.updateView.mockReset();
237
-
238
- let viewRef;
239
- act(() => {
240
- ReactFabric.render(
241
- <View
242
- foo="bar"
243
- ref={ref => {
244
- viewRef = ref;
245
- }}
246
- />,
247
- 11,
248
- );
249
- });
250
- expect(UIManager.updateView).not.toBeCalled();
251
-
252
- expect(() => {
253
- viewRef.setNativeProps({});
254
- }).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
255
- withoutStack: true,
256
- });
257
-
258
- expect(UIManager.updateView).not.toBeCalled();
259
-
260
- expect(() => {
261
- viewRef.setNativeProps({foo: 'baz'});
262
- }).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
263
- withoutStack: true,
264
- });
265
- expect(UIManager.updateView).not.toBeCalled();
266
- });
267
-
221
it('should call dispatchCommand for native refs', () => {
222
const View = createReactNativeComponentClass('RCTView', () => ({
223
validAttributes: {foo: true},
@@ -386,275 +339,6 @@ describe('ReactFabric', () => {
339
expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled();
340
});
341
389
- it('should call FabricUIManager.measure on ref.measure', () => {
390
- const View = createReactNativeComponentClass('RCTView', () => ({
391
- validAttributes: {foo: true},
392
- uiViewClassName: 'RCTView',
393
- }));
394
-
395
- nativeFabricUIManager.measure.mockClear();
396
-
397
- let viewRef;
398
- act(() => {
399
- ReactFabric.render(
400
- <View
401
- ref={ref => {
402
- viewRef = ref;
403
- }}
404
- />,
405
- 11,
406
- );
407
- });
408
-
409
- expect(nativeFabricUIManager.measure).not.toBeCalled();
410
- const successCallback = jest.fn();
411
- viewRef.measure(successCallback);
412
- expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
413
- expect(successCallback).toHaveBeenCalledTimes(1);
414
- expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
415
- });
416
-
417
- it('should no-op if calling measure on unmounted refs', () => {
418
- const View = createReactNativeComponentClass('RCTView', () => ({
419
- validAttributes: {foo: true},
420
- uiViewClassName: 'RCTView',
421
- }));
422
-
423
- nativeFabricUIManager.measure.mockClear();
424
-
425
- let viewRef;
426
- act(() => {
427
- ReactFabric.render(
428
- <View
429
- ref={ref => {
430
- viewRef = ref;
431
- }}
432
- />,
433
- 11,
434
- );
435
- });
436
- const dangerouslyRetainedViewRef = viewRef;
437
- act(() => {
438
- ReactFabric.stopSurface(11);
439
- });
440
-
441
- expect(nativeFabricUIManager.measure).not.toBeCalled();
442
- const successCallback = jest.fn();
443
- dangerouslyRetainedViewRef.measure(successCallback);
444
- expect(nativeFabricUIManager.measure).not.toBeCalled();
445
- expect(successCallback).not.toBeCalled();
446
- });
447
-
448
- it('should call FabricUIManager.measureInWindow on ref.measureInWindow', () => {
449
- const View = createReactNativeComponentClass('RCTView', () => ({
450
- validAttributes: {foo: true},
451
- uiViewClassName: 'RCTView',
452
- }));
453
-
454
- nativeFabricUIManager.measureInWindow.mockClear();
455
-
456
- let viewRef;
457
- act(() => {
458
- ReactFabric.render(
459
- <View
460
- ref={ref => {
461
- viewRef = ref;
462
- }}
463
- />,
464
- 11,
465
- );
466
- });
467
-
468
- expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
469
- const successCallback = jest.fn();
470
- viewRef.measureInWindow(successCallback);
471
- expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
472
- expect(successCallback).toHaveBeenCalledTimes(1);
473
- expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
474
- });
475
-
476
- it('should no-op if calling measureInWindow on unmounted refs', () => {
477
- const View = createReactNativeComponentClass('RCTView', () => ({
478
- validAttributes: {foo: true},
479
- uiViewClassName: 'RCTView',
480
- }));
481
-
482
- nativeFabricUIManager.measureInWindow.mockClear();
483
-
484
- let viewRef;
485
- act(() => {
486
- ReactFabric.render(
487
- <View
488
- ref={ref => {
489
- viewRef = ref;
490
- }}
491
- />,
492
- 11,
493
- );
494
- });
495
- const dangerouslyRetainedViewRef = viewRef;
496
- act(() => {
497
- ReactFabric.stopSurface(11);
498
- });
499
-
500
- expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
501
- const successCallback = jest.fn();
502
- dangerouslyRetainedViewRef.measureInWindow(successCallback);
503
- expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
504
- expect(successCallback).not.toBeCalled();
505
- });
506
-
507
- it('should support ref in ref.measureLayout', () => {
508
- const View = createReactNativeComponentClass('RCTView', () => ({
509
- validAttributes: {foo: true},
510
- uiViewClassName: 'RCTView',
511
- }));
512
-
513
- nativeFabricUIManager.measureLayout.mockClear();
514
-
515
- let viewRef;
516
- let otherRef;
517
- act(() => {
518
- ReactFabric.render(
519
- <View>
520
- <View
521
- foo="bar"
522
- ref={ref => {
523
- viewRef = ref;
524
- }}
525
- />
526
- <View
527
- ref={ref => {
528
- otherRef = ref;
529
- }}
530
- />
531
- </View>,
532
- 11,
533
- );
534
- });
535
-
536
- expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
537
- const successCallback = jest.fn();
538
- const failureCallback = jest.fn();
539
- viewRef.measureLayout(otherRef, successCallback, failureCallback);
540
- expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
541
- expect(successCallback).toHaveBeenCalledTimes(1);
542
- expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
543
- });
544
-
545
- it('should no-op if calling measureLayout on unmounted "from" ref', () => {
546
- const View = createReactNativeComponentClass('RCTView', () => ({
547
- validAttributes: {foo: true},
548
- uiViewClassName: 'RCTView',
549
- }));
550
-
551
- nativeFabricUIManager.measureLayout.mockClear();
552
-
553
- let viewRef;
554
- let otherRef;
555
- act(() => {
556
- ReactFabric.render(
557
- <View>
558
- <View
559
- foo="bar"
560
- ref={ref => {
561
- viewRef = ref;
562
- }}
563
- />
564
- <View
565
- ref={ref => {
566
- otherRef = ref;
567
- }}
568
- />
569
- </View>,
570
- 11,
571
- );
572
- });
573
- const dangerouslyRetainedOtherRef = otherRef;
574
- act(() => {
575
- ReactFabric.render(
576
- <View>
577
- <View
578
- foo="bar"
579
- ref={ref => {
580
- viewRef = ref;
581
- }}
582
- />
583
- {null}
584
- </View>,
585
- 11,
586
- );
587
- });
588
-
589
- expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
590
- const successCallback = jest.fn();
591
- const failureCallback = jest.fn();
592
- viewRef.measureLayout(
593
- dangerouslyRetainedOtherRef,
594
- successCallback,
595
- failureCallback,
596
- );
597
- expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
598
- expect(successCallback).not.toBeCalled();
599
- expect(failureCallback).not.toBeCalled();
600
- });
601
-
602
- it('should no-op if calling measureLayout on unmounted "to" ref', () => {
603
- const View = createReactNativeComponentClass('RCTView', () => ({
604
- validAttributes: {foo: true},
605
- uiViewClassName: 'RCTView',
606
- }));
607
-
608
- nativeFabricUIManager.measureLayout.mockClear();
609
-
610
- let viewRef;
611
- let otherRef;
612
- act(() => {
613
- ReactFabric.render(
614
- <View>
615
- <View
616
- foo="bar"
617
- ref={ref => {
618
- viewRef = ref;
619
- }}
620
- />
621
- <View
622
- ref={ref => {
623
- otherRef = ref;
624
- }}
625
- />
626
- </View>,
627
- 11,
628
- );
629
- });
630
- const dangerouslyRetainedViewRef = viewRef;
631
- act(() => {
632
- ReactFabric.render(
633
- <View>
634
- {null}
635
- <View
636
- ref={ref => {
637
- otherRef = ref;
638
- }}
639
- />
640
- </View>,
641
- 11,
642
- );
643
- });
644
-
645
- expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
646
- const successCallback = jest.fn();
647
- const failureCallback = jest.fn();
648
- dangerouslyRetainedViewRef.measureLayout(
649
- otherRef,
650
- successCallback,
651
- failureCallback,
652
- );
653
- expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
654
- expect(successCallback).not.toBeCalled();
655
- expect(failureCallback).not.toBeCalled();
656
- });
657
-
342
it('returns the correct instance and calls it in the callback', () => {
343
const View = createReactNativeComponentClass('RCTView', () => ({
344
validAttributes: {foo: true},
@@ -1202,44 +886,6 @@ describe('ReactFabric', () => {
886
expect(match).toBe(child._nativeTag);
887
});
888
1205
- it('blur on host component calls TextInputState', () => {
1206
- const View = createReactNativeComponentClass('RCTView', () => ({
1207
- validAttributes: {foo: true},
1208
- uiViewClassName: 'RCTView',
1209
- }));
1210
-
1211
- const viewRef = React.createRef();
1212
- act(() => {
1213
- ReactFabric.render(<View ref={viewRef} />, 11);
1214
- });
1215
-
1216
- expect(TextInputState.blurTextInput).not.toBeCalled();
1217
-
1218
- viewRef.current.blur();
1219
-
1220
- expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1);
1221
- expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current);
1222
- });
1223
-
1224
- it('focus on host component calls TextInputState', () => {
1225
- const View = createReactNativeComponentClass('RCTView', () => ({
1226
- validAttributes: {foo: true},
1227
- uiViewClassName: 'RCTView',
1228
- }));
1229
-
1230
- const viewRef = React.createRef();
1231
- act(() => {
1232
- ReactFabric.render(<View ref={viewRef} />, 11);
1233
- });
1234
-
1235
- expect(TextInputState.focusTextInput).not.toBeCalled();
1236
-
1237
- viewRef.current.focus();
1238
-
1239
- expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
1240
- expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
1241
- });
1242
-
889
it('should no-op if calling sendAccessibilityEvent on unmounted refs', () => {
890
const View = createReactNativeComponentClass('RCTView', () => ({
891
validAttributes: {foo: true},
packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js
new
+220
@@ -0,0 +1,220 @@
1
+/**
2
+ * (c) Facebook, Inc. and its affiliates. Confidential and proprietary.
3
+ *
4
+ * @emails react-core
5
+ * @jest-environment node
6
+ */
7
+
8
+'use strict';
9
+
10
+import * as React from 'react';
11
+
12
+beforeEach(() => {
13
+ jest.resetModules();
14
+ jest.restoreAllMocks();
15
+
16
+ require('react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager');
17
+});
18
+
19
+/**
20
+ * Renders a sequence of mock views as dictated by `keyLists`. The `keyLists`
21
+ * argument is an array of arrays which determines the number of render passes,
22
+ * how many views will be rendered in each pass, and what the keys are for each
23
+ * of the views.
24
+ *
25
+ * If an element in `keyLists` is null, the entire root will be unmounted.
26
+ *
27
+ * The return value is an array of arrays with the resulting refs from rendering
28
+ * each corresponding array of keys.
29
+ *
30
+ * If the corresponding array of keys is null, the returned element at that
31
+ * index will also be null.
32
+ */
33
+function mockRenderKeys(keyLists) {
34
+ const ReactFabric = require('react-native-renderer/fabric');
35
+ const createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
36
+ .ReactNativeViewConfigRegistry.register;
37
+ const {act} = require('jest-react');
38
+
39
+ const mockContainerTag = 11;
40
+ const MockView = createReactNativeComponentClass('RCTMockView', () => ({
41
+ validAttributes: {},
42
+ uiViewClassName: 'RCTMockView',
43
+ }));
44
+
45
+ return keyLists.map(keyList => {
46
+ if (Array.isArray(keyList)) {
47
+ const refs = keyList.map(key => undefined);
48
+ act(() => {
49
+ ReactFabric.render(
50
+ <MockView>
51
+ {keyList.map((key, index) => (
52
+ <MockView
53
+ key={key}
54
+ ref={ref => {
55
+ refs[index] = ref;
56
+ }}
57
+ />
58
+ ))}
59
+ </MockView>,
60
+ mockContainerTag,
61
+ );
62
+ });
63
+ // Clone `refs` to ignore future passes.
64
+ return [...refs];
65
+ }
66
+ if (keyList == null) {
67
+ act(() => {
68
+ ReactFabric.stopSurface(mockContainerTag);
69
+ });
70
+ return null;
71
+ }
72
+ throw new TypeError(
73
+ `Invalid 'keyLists' element of type ${typeof keyList}.`,
74
+ );
75
+ });
76
+}
77
+
78
+describe('blur', () => {
79
+ test('blur() invokes TextInputState', () => {
80
+ const {
81
+ TextInputState,
82
+ } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
83
+
84
+ const [[fooRef]] = mockRenderKeys([['foo']]);
85
+
86
+ fooRef.blur();
87
+
88
+ expect(TextInputState.blurTextInput.mock.calls).toEqual([[fooRef]]);
89
+ });
90
+});
91
+
92
+describe('focus', () => {
93
+ test('focus() invokes TextInputState', () => {
94
+ const {
95
+ TextInputState,
96
+ } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
97
+
98
+ const [[fooRef]] = mockRenderKeys([['foo']]);
99
+
100
+ fooRef.focus();
101
+
102
+ expect(TextInputState.focusTextInput.mock.calls).toEqual([[fooRef]]);
103
+ });
104
+});
105
+
106
+describe('measure', () => {
107
+ test('component.measure(...) invokes callback', () => {
108
+ const [[fooRef]] = mockRenderKeys([['foo']]);
109
+
110
+ const callback = jest.fn();
111
+ fooRef.measure(callback);
112
+
113
+ expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
114
+ expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]);
115
+ });
116
+
117
+ test('unmounted.measure(...) does nothing', () => {
118
+ const [[fooRef]] = mockRenderKeys([['foo'], null]);
119
+
120
+ const callback = jest.fn();
121
+ fooRef.measure(callback);
122
+
123
+ expect(nativeFabricUIManager.measure).not.toHaveBeenCalled();
124
+ expect(callback).not.toHaveBeenCalled();
125
+ });
126
+});
127
+
128
+describe('measureInWindow', () => {
129
+ test('component.measureInWindow(...) invokes callback', () => {
130
+ const [[fooRef]] = mockRenderKeys([['foo']]);
131
+
132
+ const callback = jest.fn();
133
+ fooRef.measureInWindow(callback);
134
+
135
+ expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
136
+ expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
137
+ });
138
+
139
+ test('unmounted.measureInWindow(...) does nothing', () => {
140
+ const [[fooRef]] = mockRenderKeys([['foo'], null]);
141
+
142
+ const callback = jest.fn();
143
+ fooRef.measureInWindow(callback);
144
+
145
+ expect(nativeFabricUIManager.measureInWindow).not.toHaveBeenCalled();
146
+ expect(callback).not.toHaveBeenCalled();
147
+ });
148
+});
149
+
150
+describe('measureLayout', () => {
151
+ test('component.measureLayout(component, ...) invokes callback', () => {
152
+ const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar']]);
153
+
154
+ const successCallback = jest.fn();
155
+ const failureCallback = jest.fn();
156
+ fooRef.measureLayout(barRef, successCallback, failureCallback);
157
+
158
+ expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
159
+ expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]);
160
+ });
161
+
162
+ test('unmounted.measureLayout(component, ...) does nothing', () => {
163
+ const [[fooRef, barRef]] = mockRenderKeys([
164
+ ['foo', 'bar'],
165
+ ['foo', null],
166
+ ]);
167
+
168
+ const successCallback = jest.fn();
169
+ const failureCallback = jest.fn();
170
+ fooRef.measureLayout(barRef, successCallback, failureCallback);
171
+
172
+ expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
173
+ expect(successCallback).not.toHaveBeenCalled();
174
+ });
175
+
176
+ test('component.measureLayout(unmounted, ...) does nothing', () => {
177
+ const [[fooRef, barRef]] = mockRenderKeys([
178
+ ['foo', 'bar'],
179
+ [null, 'bar'],
180
+ ]);
181
+
182
+ const successCallback = jest.fn();
183
+ const failureCallback = jest.fn();
184
+ fooRef.measureLayout(barRef, successCallback, failureCallback);
185
+
186
+ expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
187
+ expect(successCallback).not.toHaveBeenCalled();
188
+ });
189
+
190
+ test('unmounted.measureLayout(unmounted, ...) does nothing', () => {
191
+ const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar'], null]);
192
+
193
+ const successCallback = jest.fn();
194
+ const failureCallback = jest.fn();
195
+ fooRef.measureLayout(barRef, successCallback, failureCallback);
196
+
197
+ expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
198
+ expect(successCallback).not.toHaveBeenCalled();
199
+ });
200
+});
201
+
202
+describe('setNativeProps', () => {
203
+ test('setNativeProps(...) emits a warning', () => {
204
+ const {
205
+ UIManager,
206
+ } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
207
+
208
+ const [[fooRef]] = mockRenderKeys([['foo']]);
209
+
210
+ expect(() => {
211
+ fooRef.setNativeProps({});
212
+ }).toErrorDev(
213
+ ['Warning: setNativeProps is not currently supported in Fabric'],
214
+ {
215
+ withoutStack: true,
216
+ },
217
+ );
218
+ expect(UIManager.updateView).not.toBeCalled();
219
+ });
220
+});