mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Separate unit tests for ReactFabricHostComponent (#21969)
This commit is contained in:
@@ -13,14 +13,9 @@
|
||||
let React;
|
||||
let ReactFabric;
|
||||
let createReactNativeComponentClass;
|
||||
let UIManager;
|
||||
let StrictMode;
|
||||
let TextInputState;
|
||||
let act;
|
||||
|
||||
const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
|
||||
'Warning: setNativeProps is not currently supported in Fabric';
|
||||
|
||||
const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
|
||||
"Warning: dispatchCommand was called with a ref that isn't a " +
|
||||
'native component. Use React.forwardRef to get access to the underlying native component';
|
||||
@@ -42,12 +37,8 @@ describe('ReactFabric', () => {
|
||||
React = require('react');
|
||||
StrictMode = React.StrictMode;
|
||||
ReactFabric = require('react-native-renderer/fabric');
|
||||
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.UIManager;
|
||||
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.ReactNativeViewConfigRegistry.register;
|
||||
TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.TextInputState;
|
||||
|
||||
act = require('jest-react').act;
|
||||
});
|
||||
@@ -227,44 +218,6 @@ describe('ReactFabric', () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not call UIManager.updateView from ref.setNativeProps', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
UIManager.updateView.mockReset();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
|
||||
expect(() => {
|
||||
viewRef.setNativeProps({foo: 'baz'});
|
||||
}).toErrorDev([SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE], {
|
||||
withoutStack: true,
|
||||
});
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should call dispatchCommand for native refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
@@ -386,275 +339,6 @@ describe('ReactFabric', () => {
|
||||
expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should call FabricUIManager.measure on ref.measure', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measure.mockClear();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measure(successCallback);
|
||||
expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0);
|
||||
});
|
||||
|
||||
it('should no-op if calling measure on unmounted refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measure.mockClear();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
const dangerouslyRetainedViewRef = viewRef;
|
||||
act(() => {
|
||||
ReactFabric.stopSurface(11);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measure).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
dangerouslyRetainedViewRef.measure(successCallback);
|
||||
expect(nativeFabricUIManager.measure).not.toBeCalled();
|
||||
expect(successCallback).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should call FabricUIManager.measureInWindow on ref.measureInWindow', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measureInWindow.mockClear();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
viewRef.measureInWindow(successCallback);
|
||||
expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100);
|
||||
});
|
||||
|
||||
it('should no-op if calling measureInWindow on unmounted refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measureInWindow.mockClear();
|
||||
|
||||
let viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
const dangerouslyRetainedViewRef = viewRef;
|
||||
act(() => {
|
||||
ReactFabric.stopSurface(11);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
dangerouslyRetainedViewRef.measureInWindow(successCallback);
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toBeCalled();
|
||||
expect(successCallback).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should support ref in ref.measureLayout', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(otherRef, successCallback, failureCallback);
|
||||
expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100);
|
||||
});
|
||||
|
||||
it('should no-op if calling measureLayout on unmounted "from" ref', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
const dangerouslyRetainedOtherRef = otherRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
{null}
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
viewRef.measureLayout(
|
||||
dangerouslyRetainedOtherRef,
|
||||
successCallback,
|
||||
failureCallback,
|
||||
);
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
expect(successCallback).not.toBeCalled();
|
||||
expect(failureCallback).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should no-op if calling measureLayout on unmounted "to" ref', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
nativeFabricUIManager.measureLayout.mockClear();
|
||||
|
||||
let viewRef;
|
||||
let otherRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
<View
|
||||
foo="bar"
|
||||
ref={ref => {
|
||||
viewRef = ref;
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
const dangerouslyRetainedViewRef = viewRef;
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<View>
|
||||
{null}
|
||||
<View
|
||||
ref={ref => {
|
||||
otherRef = ref;
|
||||
}}
|
||||
/>
|
||||
</View>,
|
||||
11,
|
||||
);
|
||||
});
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
dangerouslyRetainedViewRef.measureLayout(
|
||||
otherRef,
|
||||
successCallback,
|
||||
failureCallback,
|
||||
);
|
||||
expect(nativeFabricUIManager.measureLayout).not.toBeCalled();
|
||||
expect(successCallback).not.toBeCalled();
|
||||
expect(failureCallback).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('returns the correct instance and calls it in the callback', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
@@ -1202,44 +886,6 @@ describe('ReactFabric', () => {
|
||||
expect(match).toBe(child._nativeTag);
|
||||
});
|
||||
|
||||
it('blur on host component calls TextInputState', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
const viewRef = React.createRef();
|
||||
act(() => {
|
||||
ReactFabric.render(<View ref={viewRef} />, 11);
|
||||
});
|
||||
|
||||
expect(TextInputState.blurTextInput).not.toBeCalled();
|
||||
|
||||
viewRef.current.blur();
|
||||
|
||||
expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1);
|
||||
expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current);
|
||||
});
|
||||
|
||||
it('focus on host component calls TextInputState', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
uiViewClassName: 'RCTView',
|
||||
}));
|
||||
|
||||
const viewRef = React.createRef();
|
||||
act(() => {
|
||||
ReactFabric.render(<View ref={viewRef} />, 11);
|
||||
});
|
||||
|
||||
expect(TextInputState.focusTextInput).not.toBeCalled();
|
||||
|
||||
viewRef.current.focus();
|
||||
|
||||
expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
|
||||
expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
|
||||
});
|
||||
|
||||
it('should no-op if calling sendAccessibilityEvent on unmounted refs', () => {
|
||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||
validAttributes: {foo: true},
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* (c) Facebook, Inc. and its affiliates. Confidential and proprietary.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.restoreAllMocks();
|
||||
|
||||
require('react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager');
|
||||
});
|
||||
|
||||
/**
|
||||
* Renders a sequence of mock views as dictated by `keyLists`. The `keyLists`
|
||||
* argument is an array of arrays which determines the number of render passes,
|
||||
* how many views will be rendered in each pass, and what the keys are for each
|
||||
* of the views.
|
||||
*
|
||||
* If an element in `keyLists` is null, the entire root will be unmounted.
|
||||
*
|
||||
* The return value is an array of arrays with the resulting refs from rendering
|
||||
* each corresponding array of keys.
|
||||
*
|
||||
* If the corresponding array of keys is null, the returned element at that
|
||||
* index will also be null.
|
||||
*/
|
||||
function mockRenderKeys(keyLists) {
|
||||
const ReactFabric = require('react-native-renderer/fabric');
|
||||
const createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.ReactNativeViewConfigRegistry.register;
|
||||
const {act} = require('jest-react');
|
||||
|
||||
const mockContainerTag = 11;
|
||||
const MockView = createReactNativeComponentClass('RCTMockView', () => ({
|
||||
validAttributes: {},
|
||||
uiViewClassName: 'RCTMockView',
|
||||
}));
|
||||
|
||||
return keyLists.map(keyList => {
|
||||
if (Array.isArray(keyList)) {
|
||||
const refs = keyList.map(key => undefined);
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<MockView>
|
||||
{keyList.map((key, index) => (
|
||||
<MockView
|
||||
key={key}
|
||||
ref={ref => {
|
||||
refs[index] = ref;
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</MockView>,
|
||||
mockContainerTag,
|
||||
);
|
||||
});
|
||||
// Clone `refs` to ignore future passes.
|
||||
return [...refs];
|
||||
}
|
||||
if (keyList == null) {
|
||||
act(() => {
|
||||
ReactFabric.stopSurface(mockContainerTag);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
throw new TypeError(
|
||||
`Invalid 'keyLists' element of type ${typeof keyList}.`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
describe('blur', () => {
|
||||
test('blur() invokes TextInputState', () => {
|
||||
const {
|
||||
TextInputState,
|
||||
} = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
|
||||
|
||||
const [[fooRef]] = mockRenderKeys([['foo']]);
|
||||
|
||||
fooRef.blur();
|
||||
|
||||
expect(TextInputState.blurTextInput.mock.calls).toEqual([[fooRef]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus', () => {
|
||||
test('focus() invokes TextInputState', () => {
|
||||
const {
|
||||
TextInputState,
|
||||
} = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
|
||||
|
||||
const [[fooRef]] = mockRenderKeys([['foo']]);
|
||||
|
||||
fooRef.focus();
|
||||
|
||||
expect(TextInputState.focusTextInput.mock.calls).toEqual([[fooRef]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('measure', () => {
|
||||
test('component.measure(...) invokes callback', () => {
|
||||
const [[fooRef]] = mockRenderKeys([['foo']]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
|
||||
expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]);
|
||||
});
|
||||
|
||||
test('unmounted.measure(...) does nothing', () => {
|
||||
const [[fooRef]] = mockRenderKeys([['foo'], null]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
|
||||
expect(nativeFabricUIManager.measure).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('measureInWindow', () => {
|
||||
test('component.measureInWindow(...) invokes callback', () => {
|
||||
const [[fooRef]] = mockRenderKeys([['foo']]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
|
||||
expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
|
||||
});
|
||||
|
||||
test('unmounted.measureInWindow(...) does nothing', () => {
|
||||
const [[fooRef]] = mockRenderKeys([['foo'], null]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
|
||||
expect(nativeFabricUIManager.measureInWindow).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('measureLayout', () => {
|
||||
test('component.measureLayout(component, ...) invokes callback', () => {
|
||||
const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar']]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]);
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(component, ...) does nothing', () => {
|
||||
const [[fooRef, barRef]] = mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
['foo', null],
|
||||
]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('component.measureLayout(unmounted, ...) does nothing', () => {
|
||||
const [[fooRef, barRef]] = mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
[null, 'bar'],
|
||||
]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(unmounted, ...) does nothing', () => {
|
||||
const [[fooRef, barRef]] = mockRenderKeys([['foo', 'bar'], null]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setNativeProps', () => {
|
||||
test('setNativeProps(...) emits a warning', () => {
|
||||
const {
|
||||
UIManager,
|
||||
} = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
|
||||
|
||||
const [[fooRef]] = mockRenderKeys([['foo']]);
|
||||
|
||||
expect(() => {
|
||||
fooRef.setNativeProps({});
|
||||
}).toErrorDev(
|
||||
['Warning: setNativeProps is not currently supported in Fabric'],
|
||||
{
|
||||
withoutStack: true,
|
||||
},
|
||||
);
|
||||
expect(UIManager.updateView).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user