Skip to content
This repository has been archived by the owner on Sep 1, 2024. It is now read-only.

Fix silently failing invalid validators in shape #234

Merged
merged 1 commit into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions __tests__/PropTypesDevelopmentReact15.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ function expectWarningInDevelopment(declaration, value) {
console.error.calls.reset();
}

function expectInvalidValidatorWarning(declaration, type) {
PropTypes.checkPropTypes({ foo: declaration }, { foo: {} }, 'prop', 'testComponent', null);
expect(console.error.calls.argsFor(0)[0]).toEqual(
'Warning: Failed prop type: testComponent: prop type `foo.bar` is invalid; '
+ 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
);
console.error.calls.reset();
}

describe('PropTypesDevelopmentReact15', () => {
beforeEach(() => {
resetWarningCache();
Expand Down Expand Up @@ -224,6 +233,22 @@ describe('PropTypesDevelopmentReact15', () => {
);
expect(returnValue).toBe(undefined);
});

it('should warn for invalid validators inside shape', () => {
spyOn(console, 'error');
expectInvalidValidatorWarning(PropTypes.shape({ bar: PropTypes.invalid_type }), 'undefined');
expectInvalidValidatorWarning(PropTypes.shape({ bar: true }), 'boolean');
expectInvalidValidatorWarning(PropTypes.shape({ bar: 'true' }), 'string');
expectInvalidValidatorWarning(PropTypes.shape({ bar: null }), 'null');
});

it('should warn for invalid validators inside exact', () => {
spyOn(console, 'error');
expectInvalidValidatorWarning(PropTypes.exact({ bar: PropTypes.invalid_type }), 'undefined');
expectInvalidValidatorWarning(PropTypes.exact({ bar: true }), 'boolean');
expectInvalidValidatorWarning(PropTypes.exact({ bar: 'true' }), 'string');
expectInvalidValidatorWarning(PropTypes.exact({ bar: null }), 'null');
});
});

describe('resetWarningCache', () => {
Expand Down
25 changes: 25 additions & 0 deletions __tests__/PropTypesDevelopmentStandalone-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ function expectThrowsInDevelopment(declaration, value) {
);
}

function expectInvalidValidatorWarning(declaration, type) {
PropTypes.checkPropTypes({ foo: declaration }, { foo: {} }, 'prop', 'testComponent', null);
expect(console.error.calls.argsFor(0)[0]).toEqual(
'Warning: Failed prop type: testComponent: prop type `foo.bar` is invalid; '
+ 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
);
console.error.calls.reset();
}

describe('PropTypesDevelopmentStandalone', () => {
beforeEach(() => {
resetWarningCache();
Expand Down Expand Up @@ -221,6 +230,22 @@ describe('PropTypesDevelopmentStandalone', () => {
);
expect(returnValue).toBe(undefined);
});

it('should warn for invalid validators inside shape', () => {
spyOn(console, 'error');
expectInvalidValidatorWarning(PropTypes.shape({ bar: PropTypes.invalid_type }), 'undefined');
expectInvalidValidatorWarning(PropTypes.shape({ bar: true }), 'boolean');
expectInvalidValidatorWarning(PropTypes.shape({ bar: 'true' }), 'string');
expectInvalidValidatorWarning(PropTypes.shape({ bar: null }), 'null');
});

it('should warn for invalid validators inside exact', () => {
spyOn(console, 'error');
expectInvalidValidatorWarning(PropTypes.exact({ bar: PropTypes.invalid_type }), 'undefined');
expectInvalidValidatorWarning(PropTypes.exact({ bar: true }), 'boolean');
expectInvalidValidatorWarning(PropTypes.exact({ bar: 'true' }), 'string');
expectInvalidValidatorWarning(PropTypes.exact({ bar: null }), 'null');
});
});

describe('resetWarningCache', () => {
Expand Down
14 changes: 12 additions & 2 deletions factoryWithTypeCheckers.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
return createChainableTypeChecker(validate);
}

function invalidValidatorError(componentName, location, propFullName, key, type) {
return new PropTypeError(
(componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' +
'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
);
}

function createShapeTypeChecker(shapeTypes) {
function validate(props, propName, componentName, location, propFullName) {
var propValue = props[propName];
Expand All @@ -409,8 +416,8 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
}
for (var key in shapeTypes) {
var checker = shapeTypes[key];
if (!checker) {
continue;
if (typeof checker !== 'function') {
return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
}
var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
if (error) {
Expand All @@ -434,6 +441,9 @@ module.exports = function(isValidElement, throwOnDirectAccess) {
var allKeys = assign({}, props[propName], shapeTypes);
for (var key in allKeys) {
var checker = shapeTypes[key];
if (has(shapeTypes, key) && typeof checker !== 'function') {
return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
}
if (!checker) {
return new PropTypeError(
'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +
Expand Down