UNPKG

parameter-validator

Version:

Parameter validator makes it easy to verify that an object contains required, valid parameters.

461 lines (367 loc) 19.4 kB
import { expect, fail } from 'chai'; import sinon from 'sinon'; import ParameterValidator from '../src/ParameterValidator'; import { ParameterValidationError } from '../src/ParameterValidator'; describe('ParameterValidator', () => { let parameterValidator; beforeEach(() => { parameterValidator = new ParameterValidator(); }); describe('validate()', () => { describe('parameter existence validation', () => { it('should return the parameters specified if they all exist', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var extractedParams = parameterValidator.validate(animalNames, ['cat', 'dog', 'squirrel']); expect(extractedParams).to.deep.equal(animalNames); }); it('should not return any parameters not specified', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky', mouse: 'Jerry' }; var expectedExtractedParams = cloneDeep(animalNames); delete expectedExtractedParams.mouse; var extractedParams = parameterValidator.validate(animalNames, ['cat', 'dog', 'squirrel']); expect(extractedParams).to.deep.equal(expectedExtractedParams); }); it('should throw a ParameterValidationError if a required parameter is not included', () => { var animalNames = { cat: 'Garfield', dog: 'Jake' }; try { parameterValidator.validate(animalNames, ['cat', 'dog', 'squirrel']); throw new Error('validate() didn\'t throw an error like it was supposed to.'); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); expect(error.toString()).to.equal('ParameterValidationError: Invalid value of \'undefined\' was provided for parameter \'squirrel\'.'); } }); it('should throw a ParameterValidationError decribing multiple failures if multiple required parameters are not included', () => { var animalNames = { cat: 'Garfield', dog: 'Jake' }; try { parameterValidator.validate(animalNames, ['cat', 'dog', 'squirrel', 'kangaroo']); throw new Error('validate() didn\'t throw an error like it was supposed to.'); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); expect(error.toString()).to.equal('ParameterValidationError: Invalid value of \'undefined\' was provided for parameter \'squirrel\'. ' + 'Invalid value of \'undefined\' was provided for parameter \'kangaroo\'.'); } }); it('should accept a parameter with a null value without throwing a ParameterValidationError', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', dragon: null }; var extractedParams = parameterValidator.validate(animalNames, ['cat', 'dog', 'dragon']); expect(extractedParams).to.deep.equal(animalNames); }); it('should accept a parameter that is a blank string without throwing a ParameterValidationError', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', dragon: '' }; var extractedParams = parameterValidator.validate(animalNames, ['cat', 'dog', 'dragon']); expect(extractedParams).to.deep.equal(animalNames); }); it('should add extracted parameters to an existing object if one is supplied as the extractedParams parameter', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var existingParams = { elephant: 'Dumbo' }; var expectedUpdatedExistingParams = cloneDeep(existingParams); Object.assign(expectedUpdatedExistingParams, animalNames); var extractedParams = parameterValidator.validate(animalNames, ['cat', 'dog', 'squirrel'], existingParams); expect(extractedParams).to.deep.equal(expectedUpdatedExistingParams); expect(existingParams).to.deep.equal(expectedUpdatedExistingParams); }); }); describe('logical OR parameter existence validation', () => { it('should return the parameters specified if they all exist', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var extractedParams = parameterValidator.validate(animalNames, [['dog', 'cat', 'squirrel']]); expect(extractedParams).to.deep.equal(animalNames); }); it('should not return any parameters not specified', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky', mouse: 'Jerry' }; var expectedExtractedParams = cloneDeep(animalNames); delete expectedExtractedParams.mouse; var extractedParams = parameterValidator.validate(animalNames, [['cat', 'dog', 'squirrel']]); expect(extractedParams).to.deep.equal(expectedExtractedParams); }); it('should not throw an error if one of the parameters in the logical OR group is included but others in the group are not', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky', mouse: 'Jerry' }; var expectedExtractedParams = cloneDeep(animalNames); delete expectedExtractedParams.mouse; delete expectedExtractedParams.squirrel; var extractedParams = parameterValidator.validate(animalNames, [['cat', 'dog', 'chimp']]); expect(extractedParams).to.deep.equal(expectedExtractedParams); }); it('should throw a ParameterValidationError if none of the parameters specified are included', () => { var animalNames = { cat: 'Garfield', dog: 'Jake' }; try { parameterValidator.validate(animalNames, [['moose', 'kangaroo', 'mouse']]); throw new Error('validate() didn\'t throw an error like it was supposed to.'); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); expect(error.toString()).to.equal('ParameterValidationError: One of the following parameters must be included: \'moose\', \'kangaroo\', \'mouse\'.'); } }); it('should add extracted parameters to an existing object if one is supplied as the extractedParams parameter', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var existingParams = { elephant: 'Dumbo' }; var expectedUpdatedExistingParams = cloneDeep(existingParams); Object.assign(expectedUpdatedExistingParams, animalNames); delete expectedUpdatedExistingParams.squirrel; var extractedParams = parameterValidator.validate(animalNames, [['cat', 'dog', 'chimp']], existingParams); expect(extractedParams).to.deep.equal(expectedUpdatedExistingParams); expect(existingParams).to.deep.equal(expectedUpdatedExistingParams); }); }); describe('execution of custom validation functions', () => { it('should return the parameters specified if they passes validation', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var extractedParams = parameterValidator.validate(animalNames, [{ cat: catNameIsCool, dog: name => name !== 'Beethoven' }]); expect(extractedParams).to.deep.equal({ cat: 'Garfield', dog: 'Jake' }); }); it('should throw a ParameterValidationError if a custom validation function determines one parameter is invalid', () => { var animalNames = { cat: 'Sylvester', dog: 'Jake' }; try { parameterValidator.validate(animalNames, [{ cat: catNameIsCool, dog: name => [ 'Jake', 'Lassie' ].includes(name) }]); throw new Error('validate() didn\'t throw an error like it was supposed to.'); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); expect(error.toString()).to.equal('ParameterValidationError: Invalid value of \'Sylvester\' was provided for parameter \'cat\'.'); } }); it('should throw a ParameterValidationError if a custom validation function determines multiple parameters are invalid', () => { var animalNames = { cat: 'Sylvester', dog: 'Jake' }; try { parameterValidator.validate(animalNames, [{ cat: catNameIsCool, dog: name => [ 'Clifford', 'Lassie' ].includes(name) }]); throw new Error(`validate() didn't throw an error like it was supposed to.`); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); let expectedMessage = `ParameterValidationError: Invalid value of 'Sylvester' was provided for parameter 'cat'. ` + `Invalid value of 'Jake' was provided for parameter 'dog'.`; expect(error.toString()).to.equal(expectedMessage); } }); it('should add extracted parameters to an existing object if one is supplied as the extractedParams parameter', () => { var animalNames = { cat: 'Garfield', dog: 'Beethoven', squirrel: 'Rocky' }; var existingParams = { elephant: 'Dumbo' }; var expectedUpdatedExistingParams = cloneDeep(existingParams); Object.assign(expectedUpdatedExistingParams, animalNames); delete expectedUpdatedExistingParams.squirrel; var extractedParams = parameterValidator.validate(animalNames, [{cat: catNameIsCool}, {dog: dogNameIsCool}], existingParams); expect(extractedParams).to.deep.equal(expectedUpdatedExistingParams); expect(existingParams).to.deep.equal(expectedUpdatedExistingParams); }); it('should add extracted parameters to a new object if null is supplied as the extractedParams parameter', () => { var animalNames = { cat: 'Garfield', dog: 'Beethoven', squirrel: 'Rocky' }; var expectedExtractedParams = cloneDeep(animalNames); delete expectedExtractedParams.squirrel; var extractedParams = parameterValidator.validate(animalNames, [{cat: catNameIsCool}, {dog: dogNameIsCool}], null); expect(extractedParams).to.deep.equal(expectedExtractedParams); expect(extractedParams).to.deep.equal(expectedExtractedParams); }); }); describe('combining different types of validation', () => { it('should return the parameters specified if they are valid', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky', mouse: 'Speedy' }; var expectedExtractedParams = cloneDeep(animalNames); delete expectedExtractedParams.dog; var extractedParams = parameterValidator.validate(animalNames, ['mouse', ['squirrel', 'moose'], {cat: catNameIsCool}]); expect(extractedParams).to.deep.equal(expectedExtractedParams); }); it('should throw a ParameterValidationError listing the validation errors', () => { var animalNames = { cat: 'Sylvester', dog: 'Jake', mouse: 'Jerry' }; var expectedErrorMessage = 'ParameterValidationError: Invalid value of \'undefined\' was provided for parameter \'chicken\'. ' + 'One of the following parameters must be included: \'squirrel\', \'moose\'. Invalid value of \'Sylvester\' was provided for parameter \'cat\'. ' + 'Invalid value of \'Jake\' was provided for parameter \'dog\'.'; try { parameterValidator.validate(animalNames, ['mouse', 'chicken', ['squirrel', 'moose'], {cat: catNameIsCool}, {dog: dogNameIsCool}]); throw new Error('validate() didn\'t throw an error like it was supposed to.'); } catch(error) { expect(error).to.be.instanceof(ParameterValidationError); expect(error.toString()).to.equal(expectedErrorMessage); } }); it('should add extracted parameters to an existing object if one is supplied as the extractedParams parameter', () => { var animalNames = { cat: 'Garfield', dog: 'Jake', squirrel: 'Rocky' }; var existingParams = { elephant: 'Dumbo' }; var expectedUpdatedExistingParams = cloneDeep(existingParams); Object.assign(expectedUpdatedExistingParams, animalNames); var extractedParams = parameterValidator.validate(animalNames, [{cat: catNameIsCool}, 'dog', ['squirrel', 'moose']], existingParams); expect(extractedParams).to.deep.equal(expectedUpdatedExistingParams); expect(existingParams).to.deep.equal(expectedUpdatedExistingParams); }); }); describe('addPrefix option', () => { let animalNames, accumulator; beforeEach(() => { animalNames = { dog: 'Scooby', bear: 'Yogi', penguin: 'Tux' }; accumulator = {}; }); it(`throws an error when a value is provided that's not a string`, () => { try { parameterValidator.validate(animalNames, [ 'penguin', 'bear' ], accumulator, { addPrefix: 4 }); fail(); } catch (error) { expect(error).to.be.instanceof(Error); } }); it('adds a given string prefix to the validated properties it extracts', () => { parameterValidator.validate(animalNames, [ 'penguin', 'bear' ], accumulator, { addPrefix: '_' }); expect(accumulator).to.deep.equal({ _penguin: 'Tux', _bear: 'Yogi' }); }); }); describe('errorClass option', () => { it('allows a different Error subclass to be used instead of ParameterValidationError', () => { class CustomValidationError extends Error { constructor(message) { super(message); this.name = this.constructor.name; this.message = message; if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor.name); } } } let lilBub = { type: 'cat' }; try { parameterValidator.validate(lilBub, [ 'type', 'interests' ], null, { errorClass: CustomValidationError }); expect(`failing the test because an error wasn't thrown`).to.equal(true); } catch (error) { expect(error).to.be.instanceof(CustomValidationError); expect(error.message).to.include('interests'); } }); }); }); describe('validateAsync()', () => { it('calls validate() asynchronously', () => { let expectedReturnValue = { testParam: 'first', testParam2: 'second' }; let params = [ { first: true }, [ 'second' ] ]; sinon.stub(parameterValidator, 'validate').returns(Promise.resolve(expectedReturnValue)); return parameterValidator.validateAsync(...params) .then(validatedParams => { expect(parameterValidator.validate.callCount).to.equal(1); expect(parameterValidator.validate.firstCall.args).to.deep.equal(params); expect(validatedParams).to.deep.equal(expectedReturnValue); }); }); it('wraps errors thrown by validate() in a promise', () => { let expectedError = new Error('Uh oh...'); parameterValidator.validate = () => { throw expectedError; }; return parameterValidator.validateAsync() .then(() => { throw new Error(`validate() didn't throw an error like it's supposed to.`); }) .catch(error => { expect(error).to.equal(expectedError); }); }); }); }); function catNameIsCool(name) { var presidents = ['Lincoln', 'Washington', 'Roosevelt', 'Garfield']; return presidents.indexOf(name) !== -1; } function dogNameIsCool(name) { var composers = ['Tchaikovsky', 'Gershwin', 'Beethoven']; return composers.indexOf(name) !== -1; } function cloneDeep(object) { return JSON.parse(JSON.stringify(object)); }