Skip to content

Commit

Permalink
mapOf and setOf validators
Browse files Browse the repository at this point in the history
  • Loading branch information
conartist6 committed Aug 26, 2019
1 parent bef0803 commit 3960ce3
Show file tree
Hide file tree
Showing 7 changed files with 830 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ MyComponent.propTypes = {
// An array of a certain type
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

// An es6 Map containing certain types of keys and values
optionalMapOf: PropTypes.mapOf([PropTypes.string, PropTypes.number]),

// An es6 Set containing certain types of values
optionalSetOf: PropTypes.setOf(PropTypes.number),

// An object with property values of a certain type
optionalObjectOf: PropTypes.objectOf(PropTypes.number),

Expand Down
234 changes: 234 additions & 0 deletions __tests__/PropTypesDevelopmentReact15.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,240 @@ describe('PropTypesDevelopmentReact15', () => {
});
});

describe('MapOf Type', () => {
it('should fail if argument is not an array', () => {
typeCheckFail(
PropTypes.mapOf({ foo: PropTypes.string }),
{ foo: 'bar' },
'Correct syntax is `PropTypes.mapOf([PropTypes.keyType, PropTypes.valueType])`.',
);
});

it('should fail for invalid keyMatcher or valueMatcher', () => {
typeCheckFail(
PropTypes.mapOf([{ foo: PropTypes.string }]),
{ foo: 'bar' },
'Property `testProp` of component `testComponent` has invalid PropType notation inside mapOf.',
);
});

it('should support the mapOf propTypes', () => {
typeCheckPass(PropTypes.mapOf([PropTypes.string, PropTypes.number]), new Map([['1', 1], ['2', 2], ['3', 3]]));
typeCheckPass(PropTypes.mapOf([PropTypes.number, PropTypes.string]), new Map([[0, 'a'], [1, 'b'], [2, 'c']]));
typeCheckPass(PropTypes.mapOf([PropTypes.object, PropTypes.oneOf(['a', 'b'])]), new Map([[{}, 'a'], [{}, 'b']]));
typeCheckPass(PropTypes.mapOf([PropTypes.symbol, PropTypes.func]), new Map([[Symbol(), function () { }]]));
});

it('should support mapOf with complex types', () => {
typeCheckPass(
PropTypes.mapOf([PropTypes.string, PropTypes.shape({ a: PropTypes.number.isRequired })]),
new Map([['foo', { a: 1 }], ['bar', { a: 2 }]]),
);

function Thing() { }
typeCheckPass(PropTypes.mapOf([PropTypes.string, PropTypes.instanceOf(Thing)]), new Map([
['foo', new Thing()],
['bar', new Thing()],
]));
});

it('should warn with invalid entries in the map', () => {
typeCheckFail(
PropTypes.mapOf([PropTypes.string, PropTypes.number]),
new Map([['1', 1], ['2', 2], ['3', 'b']]),
'Invalid prop `testProp.get("3")` of type `string` supplied to ' +
'`testComponent`, expected `number`.',
);
});

it('should warn with invalid complex types', () => {
function Thing() { }
const name = Thing.name || '<<anonymous>>';

typeCheckFail(
PropTypes.mapOf([PropTypes.any, PropTypes.instanceOf(Thing)]),
new Map([[new Thing(), new Thing()], ['a', 'xyz']]),
'Invalid prop `testProp.get("a")` of type `String` supplied to ' +
'`testComponent`, expected instance of `' +
name +
'`.',
);
});

it('should warn when passed something other than a map', () => {
typeCheckFail(
PropTypes.mapOf([PropTypes.number, PropTypes.number]),
{ '0': 'maybe-array', length: 1 },
'Invalid prop `testProp` of type `object` supplied to ' +
'`testComponent`, expected a Map.',
);
typeCheckFail(
PropTypes.mapOf([PropTypes.number, PropTypes.number]),
123,
'Invalid prop `testProp` of type `number` supplied to ' +
'`testComponent`, expected a Map.',
);
typeCheckFail(
PropTypes.mapOf([PropTypes.number, PropTypes.number]),
'string',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected a Map.',
);
});

it('should not warn when passing an empty map', () => {
typeCheckPass(PropTypes.mapOf([PropTypes.number, PropTypes.number]), new Map());
});

it('should be implicitly optional and not warn without values', () => {
typeCheckPass(PropTypes.mapOf([PropTypes.number, PropTypes.number]), null);
typeCheckPass(PropTypes.mapOf([PropTypes.number, PropTypes.number]), undefined);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.mapOf([PropTypes.number, PropTypes.number]).isRequired,
);
});

it('should warn if called manually in development', () => {
spyOn(console, 'error');
expectWarningInDevelopment(PropTypes.mapOf({ foo: PropTypes.string }), {
foo: 'bar',
});
expectWarningInDevelopment(PropTypes.mapOf([PropTypes.number, PropTypes.number]), [
1,
2,
'b',
]);
expectWarningInDevelopment(PropTypes.mapOf([PropTypes.number, PropTypes.number]), {
'0': 'maybe-array',
length: 1,
});
expectWarningInDevelopment(
PropTypes.mapOf([PropTypes.number, PropTypes.number]).isRequired,
null,
);
expectWarningInDevelopment(
PropTypes.mapOf([PropTypes.number, PropTypes.number]).isRequired,
undefined,
);
});
});

describe('SetOf Type', () => {
it('should fail for invalid argument', () => {
typeCheckFail(
PropTypes.setOf({ foo: PropTypes.string }),
{ foo: 'bar' },
'Property `testProp` of component `testComponent` has invalid PropType notation inside setOf.',
);
});

it('should support the setOf propTypes', () => {
typeCheckPass(PropTypes.setOf(PropTypes.number), new Set([1, 2, 3]));
typeCheckPass(PropTypes.setOf(PropTypes.string), new Set(['a', 'b', 'c']));
typeCheckPass(PropTypes.setOf(PropTypes.oneOf(['a', 'b'])), new Set(['a', 'b']));
typeCheckPass(PropTypes.setOf(PropTypes.symbol), new Set([Symbol()]));
});

it('should support setOf with complex types', () => {
typeCheckPass(
PropTypes.setOf(PropTypes.shape({ a: PropTypes.number.isRequired })),
new Set([{ a: 1 }, { a: 2 }]),
);

function Thing() { }
typeCheckPass(PropTypes.setOf(PropTypes.instanceOf(Thing)), new Set([
new Thing(),
new Thing(),
]));
});

it('should warn with invalid values in the set', () => {
typeCheckFail(
PropTypes.setOf(PropTypes.number),
new Set([1, 2, '3']),
'Invalid prop `testProp.has("3")` of type `string` supplied to ' +
'`testComponent`, expected `number`.',
);
});

it('should warn with invalid complex types', () => {
function Thing() { }
const name = Thing.name || '<<anonymous>>';

typeCheckFail(
PropTypes.setOf(PropTypes.instanceOf(Thing)),
new Set([new Thing(), 'xyz']),
'Invalid prop `testProp.has("xyz")` of type `String` supplied to ' +
'`testComponent`, expected instance of `' +
name +
'`.',
);
});

it('should warn when passed something other than a set', () => {
typeCheckFail(
PropTypes.setOf(PropTypes.number),
{ '0': 'maybe-array', length: 1 },
'Invalid prop `testProp` of type `object` supplied to ' +
'`testComponent`, expected a Set.',
);
typeCheckFail(
PropTypes.setOf(PropTypes.number),
123,
'Invalid prop `testProp` of type `number` supplied to ' +
'`testComponent`, expected a Set.',
);
typeCheckFail(
PropTypes.setOf(PropTypes.number),
'string',
'Invalid prop `testProp` of type `string` supplied to ' +
'`testComponent`, expected a Set.',
);
});

it('should not warn when passing an empty set', () => {
typeCheckPass(PropTypes.setOf(PropTypes.number), new Set());
});

it('should be implicitly optional and not warn without values', () => {
typeCheckPass(PropTypes.setOf(PropTypes.number), null);
typeCheckPass(PropTypes.setOf(PropTypes.number), undefined);
});

it('should warn for missing required values', () => {
typeCheckFailRequiredValues(
PropTypes.setOf(PropTypes.number).isRequired,
);
});

it('should warn if called manually in development', () => {
spyOn(console, 'error');
expectWarningInDevelopment(PropTypes.setOf({ foo: PropTypes.string }), {
foo: 'bar',
});
expectWarningInDevelopment(PropTypes.setOf(PropTypes.number), [
1,
2,
'b',
]);
expectWarningInDevelopment(PropTypes.setOf(PropTypes.number), {
'0': 'maybe-array',
length: 1,
});
expectWarningInDevelopment(
PropTypes.setOf(PropTypes.number).isRequired,
null,
);
expectWarningInDevelopment(
PropTypes.setOf(PropTypes.number).isRequired,
undefined,
);
});
});

describe('Component Type', () => {

it('should support components', () => {
Expand Down

0 comments on commit 3960ce3

Please sign in to comment.