Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4cc8ec6

Browse files
authoredJul 27, 2021
Separate unit tests for ReactFabricHostComponent (#21969)
1 parent d4d7864 commit 4cc8ec6

File tree

2 files changed

+220
-354
lines changed

2 files changed

+220
-354
lines changed
 

‎packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js

Lines changed: 0 additions & 354 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,9 @@
1313
let React;
1414
let ReactFabric;
1515
let createReactNativeComponentClass;
16-
let UIManager;
1716
let StrictMode;
18-
let TextInputState;
1917
let act;
2018

21-
const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
22-
'Warning: setNativeProps is not currently supported in Fabric';
23-
2419
const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
2520
"Warning: dispatchCommand was called with a ref that isn't a " +
2621
'native component. Use React.forwardRef to get access to the underlying native component';
@@ -42,12 +37,8 @@ describe('ReactFabric', () => {
4237
React = require('react');
4338
StrictMode = React.StrictMode;
4439
ReactFabric = require('react-native-renderer/fabric');
45-
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
46-
.UIManager;
4740
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
4841
.ReactNativeViewConfigRegistry.register;
49-
TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
50-
.TextInputState;
5142

5243
act = require('jest-react').act;
5344
});
@@ -227,44 +218,6 @@ describe('ReactFabric', () => {
227218
).toMatchSnapshot();
228219
});
229220

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-
268221
it('should call dispatchCommand for native refs', () => {
269222
const View = createReactNativeComponentClass('RCTView', () => ({
270223
validAttributes: {foo: true},
@@ -386,275 +339,6 @@ describe('ReactFabric', () => {
386339
expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled();
387340
});
388341

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-
658342
it('returns the correct instance and calls it in the callback', () => {
659343
const View = createReactNativeComponentClass('RCTView', () => ({
660344
validAttributes: {foo: true},
@@ -1202,44 +886,6 @@ describe('ReactFabric', () => {
1202886
expect(match).toBe(child._nativeTag);
1203887
});
1204888

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-
1243889
it('should no-op if calling sendAccessibilityEvent on unmounted refs', () => {
1244890
const View = createReactNativeComponentClass('RCTView', () => ({
1245891
validAttributes: {foo: true},
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
});

0 commit comments

Comments
 (0)
Please sign in to comment.