RN: Hoist Reflection from Verification

Summary:
Hoists the call to `getNativeComponentAttributes` out of the verification function so that it's easier to keep track of when this function is and is not called.

The purpose of this will become clearer in a future refactor.

Changelog:
[Internal]

Reviewed By: ejanzer

Differential Revision: D25072600

fbshipit-source-id: bc42461baae3476fa7ecb6186c4256dd23921ed5
This commit is contained in:
Tim Yung 2020-11-18 21:16:22 -08:00 коммит произвёл Facebook GitHub Bot
Родитель 9611a7b43e
Коммит 69b4611049
3 изменённых файлов: 66 добавлений и 62 удалений

Просмотреть файл

@ -10,12 +10,13 @@
'use strict';
const getNativeComponentAttributes = require('../../ReactNative/getNativeComponentAttributes');
jest.dontMock('../verifyComponentAttributeEquivalence');
const verifyComponentAttributeEquivalence = require('../verifyComponentAttributeEquivalence')
.default;
jest.dontMock('../verifyComponentAttributeEquivalence');
jest.mock('../../ReactNative/getNativeComponentAttributes', () => () => ({
const TestComponentNativeViewConfig = {
uiViewClassName: 'TestComponent',
NativeProps: {
value: 'BOOL',
},
@ -40,62 +41,63 @@ jest.mock('../../ReactNative/getNativeComponentAttributes', () => () => ({
},
transform: 'CATransform3D',
},
}));
};
describe('verifyComponentAttributeEquivalence', () => {
beforeEach(() => {
global.__DEV__ = true;
console.error = jest.fn();
jest.resetModules();
});
describe('verifyComponentAttributeEquivalence', () => {
test('should not verify in prod', () => {
it('should not verify in prod', () => {
global.__DEV__ = false;
verifyComponentAttributeEquivalence('TestComponent', {});
verifyComponentAttributeEquivalence(TestComponentNativeViewConfig, {});
});
test('should not error with native config that is a subset of the given config', () => {
const configWithAdditionalProperties = getNativeComponentAttributes(
'TestComponent',
);
configWithAdditionalProperties.bubblingEventTypes.topFocus = {
it('should not error with native config that is a subset of the given config', () => {
const configWithAdditionalProperties = {
...TestComponentNativeViewConfig,
bubblingEventTypes: {
...TestComponentNativeViewConfig.bubblingEventTypes,
topFocus: {
phasedRegistrationNames: {
bubbled: 'onFocus',
captured: 'onFocusCapture',
},
};
configWithAdditionalProperties.directEventTypes.topSlidingComplete = {
},
},
directEventTypes: {
...TestComponentNativeViewConfig.directEventTypes,
topSlidingComplete: {
registrationName: 'onSlidingComplete',
},
},
validAttributes: {
...TestComponentNativeViewConfig.validAttributes,
active: true,
},
};
configWithAdditionalProperties.validAttributes.active = true;
verifyComponentAttributeEquivalence(
'TestComponent',
configWithAdditionalProperties,
);
verifyComponentAttributeEquivalence(
'TestComponent',
TestComponentNativeViewConfig,
configWithAdditionalProperties,
);
expect(console.error).not.toBeCalled();
});
test('should error if given config is missing native config properties', () => {
verifyComponentAttributeEquivalence('TestComponent', {});
it('should error if given config is missing native config properties', () => {
verifyComponentAttributeEquivalence(TestComponentNativeViewConfig, {});
expect(console.error).toBeCalledTimes(3);
expect(console.error).toBeCalledWith(
'TestComponent generated view config for directEventTypes does not match native, missing: topAccessibilityAction',
"'TestComponent' has a view config that does not match native. 'validAttributes' is missing: borderColor, style",
);
expect(console.error).toBeCalledWith(
'TestComponent generated view config for bubblingEventTypes does not match native, missing: topChange',
"'TestComponent' has a view config that does not match native. 'bubblingEventTypes' is missing: topChange",
);
expect(console.error).toBeCalledWith(
'TestComponent generated view config for validAttributes does not match native, missing: borderColor style',
"'TestComponent' has a view config that does not match native. 'directEventTypes' is missing: topAccessibilityAction",
);
});
});

Просмотреть файл

@ -10,8 +10,9 @@
'use strict';
const ReactNativeViewConfigRegistry = require('../Renderer/shims/ReactNativeViewConfigRegistry');
const ReactNativeViewViewConfig = require('../Components/View/ReactNativeViewViewConfig');
import ReactNativeViewConfigRegistry from '../Renderer/shims/ReactNativeViewConfigRegistry';
import ReactNativeViewViewConfig from '../Components/View/ReactNativeViewViewConfig';
import getNativeComponentAttributes from '../ReactNative/getNativeComponentAttributes';
import verifyComponentAttributeEquivalence from './verifyComponentAttributeEquivalence';
export type GeneratedViewConfig = {
@ -47,7 +48,7 @@ function registerGeneratedViewConfig(
componentName: string,
viewConfig: GeneratedViewConfig,
) {
const mergedViewConfig = {
const staticViewConfig = {
uiViewClassName: componentName,
Commands: {},
/* $FlowFixMe(>=0.122.0 site=react_native_fb) This comment suppresses an
@ -75,10 +76,12 @@ function registerGeneratedViewConfig(
ReactNativeViewConfigRegistry.register(componentName, () => {
if (!global.RN$Bridgeless) {
verifyComponentAttributeEquivalence(componentName, mergedViewConfig);
const nativeViewConfig = getNativeComponentAttributes(componentName);
verifyComponentAttributeEquivalence(nativeViewConfig, staticViewConfig);
}
return mergedViewConfig;
return staticViewConfig;
});
}

Просмотреть файл

@ -10,8 +10,6 @@
'use strict';
const getNativeComponentAttributes = require('../ReactNative/getNativeComponentAttributes');
import ReactNativeViewViewConfig from '../Components/View/ReactNativeViewViewConfig';
import type {ReactNativeBaseComponentViewConfig} from '../Renderer/shims/ReactNativeTypes';
@ -41,26 +39,27 @@ const IGNORED_KEYS = ['transform', 'hitSlop'];
* years from now...
*/
export default function verifyComponentAttributeEquivalence(
componentName: string,
config: ReactNativeBaseComponentViewConfig<>,
nativeViewConfig: ReactNativeBaseComponentViewConfig<>,
staticViewConfig: ReactNativeBaseComponentViewConfig<>,
) {
const nativeAttributes = getNativeComponentAttributes(componentName);
['validAttributes', 'bubblingEventTypes', 'directEventTypes'].forEach(
prop => {
const diffKeys = Object.keys(
lefthandObjectDiff(nativeAttributes[prop], config[prop]),
for (const prop of [
'validAttributes',
'bubblingEventTypes',
'directEventTypes',
]) {
const diff = Object.keys(
lefthandObjectDiff(nativeViewConfig[prop], staticViewConfig[prop]),
);
if (diffKeys.length) {
if (diff.length > 0) {
const name =
staticViewConfig.uiViewClassName ?? nativeViewConfig.uiViewClassName;
console.error(
`${componentName} generated view config for ${prop} does not match native, missing: ${diffKeys.join(
' ',
)}`,
`'${name}' has a view config that does not match native. ` +
`'${prop}' is missing: ${diff.join(', ')}`,
);
}
},
);
}
}
export function lefthandObjectDiff(leftObj: Object, rightObj: Object): Object {