Refactor: Make StaticViewConfigValidator.validate() output objects

Summary:
## Changes
- StaticViewConfigValidator.validate() now outputs a ValidationOutput object, that contains a Array<Differences>, if invalid
- The Difference type now contains the nativeValue and the staticValue. This makes the validate() function more useful in reconciling ViewConfigs.
- Nothing should change in NativeComponentRegistry.

Changelog: [Internal]

Reviewed By: yungsters

Differential Revision: D32139973

fbshipit-source-id: a9556fa370d2c14f9e5d0540b44824cd61773958
This commit is contained in:
Ramanpreet Nara
2021-11-09 00:29:13 -08:00
committed by Facebook GitHub Bot
parent 36612a8060
commit 73b243acbc
3 changed files with 124 additions and 67 deletions
@@ -68,33 +68,30 @@ export function get<Config>(
: createViewConfig(viewConfigProvider());
if (verify) {
const nativeViewConfig = native
? viewConfig
: getNativeComponentAttributes(name);
const staticViewConfig = native
? createViewConfig(viewConfigProvider())
: viewConfig;
if (strict) {
const results = native
? StaticViewConfigValidator.validate(
const validationOutput = StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
);
if (validationOutput.type === 'invalid') {
console.error(
StaticViewConfigValidator.stringifyValidationResult(
name,
viewConfig,
createViewConfig(viewConfigProvider()),
)
: StaticViewConfigValidator.validate(
name,
getNativeComponentAttributes(name),
viewConfig,
);
if (results != null) {
console.error(results);
validationOutput,
),
);
}
} else {
if (native) {
verifyComponentAttributeEquivalence(
viewConfig,
createViewConfig(viewConfigProvider()),
);
} else {
verifyComponentAttributeEquivalence(
getNativeComponentAttributes(name),
viewConfig,
);
}
verifyComponentAttributeEquivalence(nativeViewConfig, staticViewConfig);
}
}
@@ -10,9 +10,31 @@
import {type ViewConfig} from '../Renderer/shims/ReactNativeTypes';
type Difference = {
path: $ReadOnlyArray<string>,
type: 'missing' | 'unequal' | 'unexpected',
type Difference =
| {
type: 'missing',
path: Array<string>,
nativeValue: mixed,
}
| {
type: 'unequal',
path: Array<string>,
nativeValue: mixed,
staticValue: mixed,
}
| {
type: 'unexpected',
path: Array<string>,
staticValue: mixed,
};
type ValidationResult = ValidResult | InvalidResult;
type ValidResult = {
type: 'valid',
};
type InvalidResult = {
type: 'invalid',
differences: Array<Difference>,
};
/**
@@ -23,7 +45,7 @@ export function validate(
name: string,
nativeViewConfig: ViewConfig,
staticViewConfig: ViewConfig,
): ?string {
): ValidationResult {
const differences = [];
accumulateDifferences(
differences,
@@ -41,13 +63,27 @@ export function validate(
validAttributes: staticViewConfig.validAttributes,
},
);
if (differences.length === 0) {
return null;
return {type: 'valid'};
}
return {
type: 'invalid',
differences,
};
}
export function stringifyValidationResult(
name: string,
validationResult: InvalidResult,
): string {
const {differences} = validationResult;
return [
`StaticViewConfigValidator: Invalid static view config for '${name}'.`,
'',
...differences.map(({path, type}) => {
...differences.map(difference => {
const {type, path} = difference;
switch (type) {
case 'missing':
return `- '${path.join('.')}' is missing.`;
@@ -71,7 +107,11 @@ function accumulateDifferences(
const nativeValue = nativeObject[nativeKey];
if (!staticObject.hasOwnProperty(nativeKey)) {
differences.push({path: [...path, nativeKey], type: 'missing'});
differences.push({
path: [...path, nativeKey],
type: 'missing',
nativeValue,
});
continue;
}
@@ -94,13 +134,22 @@ function accumulateDifferences(
}
if (nativeValue !== staticValue) {
differences.push({path: [...path, nativeKey], type: 'unequal'});
differences.push({
path: [...path, nativeKey],
type: 'unequal',
nativeValue,
staticValue,
});
}
}
for (const staticKey in staticObject) {
if (!nativeObject.hasOwnProperty(staticKey)) {
differences.push({path: [...path, staticKey], type: 'unexpected'});
differences.push({
path: [...path, staticKey],
type: 'unexpected',
staticValue: staticObject[staticKey],
});
}
}
}
@@ -74,13 +74,13 @@ test('passes for identical configs', () => {
},
};
expect(
StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
),
).toBe(null);
const validationResult = StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
);
expect(validationResult.type).toBe('valid');
});
test('fails for mismatched names', () => {
@@ -98,13 +98,10 @@ test('fails for mismatched names', () => {
},
};
expect(
StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
),
).toBe(
expectSVCToNotMatchNVC(
name,
nativeViewConfig,
staticViewConfig,
`
StaticViewConfigValidator: Invalid static view config for 'RCTView'.
@@ -130,13 +127,10 @@ test('fails for unequal attributes', () => {
},
};
expect(
StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
),
).toBe(
expectSVCToNotMatchNVC(
name,
nativeViewConfig,
staticViewConfig,
`
StaticViewConfigValidator: Invalid static view config for 'RCTView'.
@@ -165,13 +159,10 @@ test('fails for missing attributes', () => {
},
};
expect(
StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
),
).toBe(
expectSVCToNotMatchNVC(
name,
nativeViewConfig,
staticViewConfig,
`
StaticViewConfigValidator: Invalid static view config for 'RCTView'.
@@ -203,13 +194,10 @@ test('fails for unexpected attributes', () => {
},
};
expect(
StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
),
).toBe(
expectSVCToNotMatchNVC(
name,
nativeViewConfig,
staticViewConfig,
`
StaticViewConfigValidator: Invalid static view config for 'RCTView'.
@@ -220,3 +208,26 @@ StaticViewConfigValidator: Invalid static view config for 'RCTView'.
`.trimStart(),
);
});
function expectSVCToNotMatchNVC(
name,
nativeViewConfig,
staticViewConfig,
message,
) {
const validationResult = StaticViewConfigValidator.validate(
name,
nativeViewConfig,
staticViewConfig,
);
expect(validationResult.type).toBe('invalid');
if (validationResult.type === 'invalid') {
expect(
StaticViewConfigValidator.stringifyValidationResult(
name,
validationResult,
),
).toBe(message);
}
}