Skip to content

Commit

Permalink
add arrayElementType to validator, #195
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Sep 20, 2019
1 parent e9824fd commit f9d8019
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 12 deletions.
82 changes: 70 additions & 12 deletions js/ValidatorDef.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ define( require => {
// isValidValue: function( value ) { return Util.isInteger( value ) && value >= 0; }
'isValidValue',

// {function(PhetioType is a
// This option takes the same types as are supported with `valueType`. This option is to specify the type of the
// elements of an array. For this option to valid, `valueType` must be not also be provided. It is assumed that
// valueType is `Array`.
'arrayElementType',

// {function(new: ObjectIO)} - A TypeIO used to specify the public typeing for PhET-iO. Each TypeIO must have a
// `validator` key specified that can be used for validation. See ObjectIO for an example.
'phetioType'

/**************************************
Expand Down Expand Up @@ -106,26 +112,25 @@ define( require => {
}
if ( !( validator.hasOwnProperty( 'isValidValue' ) ||
validator.hasOwnProperty( 'valueType' ) ||
validator.hasOwnProperty( 'arrayElementType' ) ||
validator.hasOwnProperty( 'validValues' ) ||
validator.hasOwnProperty( 'phetioType' ) ) ) {
assert && options.assertions && assert( false,
'validator must have at least one of: isValidValue, valueType, validValues' );
return false;
}

if ( validator.hasOwnProperty( 'valueType' ) ) {
const valueType = validator.valueType;
if ( Array.isArray( valueType ) ) {
if ( validator.hasOwnProperty( 'valueType' ) && !validateValueOrElementType( validator.valueType, options ) ) {
return false;
}

// If every valueType in the list is not valid, then return false, pass options through verbatum.
if ( !_.every( valueType.map( typeInArray => ValidatorDef.validateValueType( typeInArray, options ) ) ) ) {
return false;
}
if ( validator.hasOwnProperty( 'arrayElementType' ) ) {
if ( validator.hasOwnProperty( 'valueType' ) ) {
assert && options.assertions && assert( false, 'valueType is redundant with arrayElementType. valueType is Array.' );
return false;
}
else if ( valueType ) {
if ( !ValidatorDef.validateValueType( valueType, options ) ) {
return false;
}
if ( !validateValueOrElementType( validator.arrayElementType, options ) ) {
return false;
}
}

Expand Down Expand Up @@ -259,6 +264,37 @@ define( require => {
}
}
}

if ( validator.hasOwnProperty( 'arrayElementType' ) ) {
const arrayElementType = validator.arrayElementType;

// If using arrayElementType, then the value should be an array. No need for assertions, because nested
// isValueValid will assert out if asserting.
if ( !ValidatorDef.isValueValid( value, { valueType: Array }, options ) ) {
return false;
}

// every element in the array should pass
if ( !_.every( value.map( arrayElement => {

// if the type is an array, then handle it like we did for valueType, with _.some
if ( Array.isArray( arrayElementType ) ) {
if ( !_.some( arrayElementType.map( typeInArray => ValidatorDef.isValueValidValueType( arrayElement, typeInArray, ASSERTIONS_FALSE ) ) ) ) {
assert && options.assertions && assert( false, `array element not valid for any arrayElementType in ${arrayElementType}, value: ${arrayElement}` );
return false;
}
return true;
}
else {

// if not an array, then just check the array element
return ValidatorDef.isValueValidValueType( arrayElement, validator.arrayElementType, options )
}
} ) ) ) {
return false; // if every element didn't pass, then return false
}
}

if ( validator.hasOwnProperty( 'validValues' ) && validator.validValues.indexOf( value ) === -1 ) {
assert && options.assertions && assert( false, `value not in validValues: ${value}` );
return false;
Expand Down Expand Up @@ -310,6 +346,28 @@ define( require => {
}
};

/**
* Validate a type that can be a type, or an array of multiple types.
* @param {*} type - see valueType documentation
* @param {Object} options - see isValidValidator
* @returns {boolean}
*/
const validateValueOrElementType = ( type, options ) => {
if ( Array.isArray( type ) ) {

// If every type in the list is not valid, then return false, pass options through verbatum.
if ( !_.every( type.map( typeInArray => ValidatorDef.validateValueType( typeInArray, options ) ) ) ) {
return false;
}
}
else if ( type ) {
if ( !ValidatorDef.validateValueType( type, options ) ) {
return false;
}
}
return true;
};

ValidatorDef.VALIDATOR_KEYS = VALIDATOR_KEYS;

return axon.register( 'ValidatorDef', ValidatorDef );
Expand Down
61 changes: 61 additions & 0 deletions js/ValidatorDefTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,65 @@ define( require => {

assert.ok( ValidatorDef.isValueValid( new Emitter(), { phetioType: EmitterIO( [] ) } ), 'emitter is valid' );
} );

QUnit.test( 'Test arrayElementType', assert => {


window.assert && assert.throws( () => ValidatorDef.validateValidator( {
valueType: Array,
arrayElementType: null
} ), 'arrayElementType expected should not have valueType' );

assert.ok( ValidatorDef.isValidValidator( { arrayElementType: 'number' } ), 'good valueType' );
assert.ok( !ValidatorDef.isValidValidator( { arrayElementTypes: 'number' } ), 'no validator keys supplied' );
assert.ok( !ValidatorDef.isValidValidator( { arrayElementTypes: 4 } ), 'no validator keys supplied' );
assert.ok( !ValidatorDef.isValidValidator( { arrayElementType: 'blaradysharady' } ), 'invalid valueType string' );

assert.ok( ValidatorDef.isValidValidator( { arrayElementType: null } ), 'null is valid' );
assert.ok( ValidatorDef.isValidValidator( { arrayElementType: [ 'number', null ] } ), 'array of null and number is valid' );
assert.ok( ValidatorDef.isValidValidator( { arrayElementType: [ 'number', null, Node ] } ), 'array of null and number is valid' );
assert.ok( !ValidatorDef.isValidValidator( { arrayElementType: [ 'numberf', null, Node ] } ), 'numberf is not a valid arrayElementType' );
assert.ok( ValidatorDef.isValueValid( [ 1, 2, 3, 4, 5 ], { arrayElementType: [ 'number' ] } ), 'number array ok' );
assert.ok( !ValidatorDef.isValueValid( [ 1, 2, 3, 4, 5, null ], { arrayElementType: [ 'number' ] } ), 'number array bad with null' );
assert.ok( ValidatorDef.isValueValid( [ 1, 2, 3, 4, 5, null ], { arrayElementType: [ 'number', null ] } ), 'number array ok with null' );
assert.ok( ValidatorDef.isValueValid( [ 1, 'fdsaf', 3, 4, 5, null ], { arrayElementType: [ 'number', 'string', null ] } ), 'number and string array ok with null' );
assert.ok( !ValidatorDef.isValueValid( [ 1, 'fdsaf', 3, 4, 5, null ], { arrayElementType: [ 'string', null ] } ), 'number and string array ok with null' );
assert.ok( ValidatorDef.isValueValid( [ [], [], [], [] ], { arrayElementType: [ Array ] } ), 'array array' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( undefined, { arrayElementType: [ 'number', 'string' ], }, ASSERTIONS_TRUE );
}, 'undefined is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( undefined, { arrayElementType: [ 7 ] }, ASSERTIONS_TRUE );
}, '7 is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( undefined, { arrayElementType: [ 'number', {} ] }, ASSERTIONS_TRUE );
}, 'Object literal is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( [ 'sting here, what up' ], { arrayElementType: [ 'number' ] }, ASSERTIONS_TRUE );
}, 'sstring is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( [ 'sting here, what up' ], { arrayElementType: [ 'number' ] }, ASSERTIONS_TRUE );
}, 'sstring is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( [ 5 ], { arrayElementType: [ 'string' ] }, ASSERTIONS_TRUE );
}, 'sstring is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( [ null, 3, 4, 5, undefined ], { arrayElementType: [ 'number', null ] }, ASSERTIONS_TRUE );
}, 'sstring is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( undefined, { arrayElementType: [ 7 ] }, ASSERTIONS_TRUE );
}, '7 is not a valid arrayElementType' );

window.assert && assert.throws( () => {
ValidatorDef.isValueValid( undefined, { arrayElementType: [ 'number', {} ] }, ASSERTIONS_TRUE );
}, 'Object literal is not a valid arrayElementType' );
} );
} );

0 comments on commit f9d8019

Please sign in to comment.