validd
Version:
An experiment on data validation
269 lines (244 loc) • 9.23 kB
JavaScript
import { describe, it } from 'mocha'
import { assert } from 'chai'
import { validate } from '../lib'
describe('Data validation', () => {
describe('validate()', () => {
it('should return no errors on empty schema', done => {
validate({}, {}).then(result => {
assert.deepEqual(result, {})
done()
})
})
it('should return no errors on undefined schema', done => {
validate(undefined, {}).then(result => {
assert.deepEqual(result, {})
done()
})
})
it('should return no errors on null schema', done => {
validate(null, {}).then(result => {
assert.deepEqual(result, {})
done()
})
})
it('should return invalid-type error when specified type doesnt match', done => {
const schema = { type: 'object' }
const expectedResult = { errors: [{ error: 'invalidType', message: 'Invalid data type' }] }
validate(schema, []).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return invalid-type error with custom message when specified type doesnt match', done => {
const schema = { type: 'object', messages: { invalidType: 'Tipo de dato inválido' } }
const expectedResult = { errors: [{ error: 'invalidType', message: 'Tipo de dato inválido' }] }
validate(schema, []).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return is-required error when value is empty string', done => {
const schema = { type: 'string', isRequired: true }
const expectedResult = { errors: [{ error: 'isRequired', message: 'The field is required' }] }
validate(schema, '').then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return is-required error when value is null', done => {
const schema = { type: 'string', isRequired: true }
const expectedResult = { errors: [{ error: 'isRequired', message: 'The field is required' }] }
validate(schema, null).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return is-required when required object field is not provided', done => {
const schema = {
type: 'object',
fields: {
fieldName: { type: 'string', isRequired: true },
fieldName2: { type: 'string', isRequired: false },
},
}
const expectedResult = {
fields: {
fieldName: { errors: [{ error: 'isRequired', message: 'The field is required' }] },
fieldName2: {},
},
}
validate(schema, { fieldName: '', fieldName2: '' }).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should not return error when required value is provided', done => {
const schema = { type: 'string', isRequired: true }
validate(schema, 'abc').then(result => {
assert.deepEqual(result, {})
done()
})
})
it('should not return error when required object field is provided', done => {
const schema = { type: 'object', fields: { name: { type: 'string', isRequired: true } } }
const expectedResult = { fields: { name: {} } }
validate(schema, { name: 'abc' }).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return min-length error', done => {
const schema = { type: 'string', minLength: 5 }
const expectedResult = { errors: [{ error: 'minLength', message: 'This field must be larger' }] }
validate(schema, 'abcd').then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should not return min-length error on empty string', done => {
const schema = { type: 'string', minLength: 5 }
const expectedResult = {}
validate(schema, '').then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should not return min-length error on undefined data', done => {
const schema = { type: 'string', minLength: 5 }
const expectedResult = {}
validate(schema, undefined).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return max-length error', done => {
const schema = { type: 'string', maxLength: 10 }
const expectedResult = { errors: [{ error: 'maxLength', message: 'This field must be shorter' }] }
validate(schema, 'abcde-abcde').then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should evaluate custom validation function', done => {
const validationFunction = value => (value === 'abc' ? { error: 'customError', message: 'Custom error' } : null)
const schema = {
type: 'object',
fields: {
field1: { type: 'string', validation: validationFunction },
field2: { type: 'string', validation: validationFunction },
},
}
const expectedResult = {
fields: {
field1: { errors: [{ error: 'customError', message: 'Custom error' }] },
field2: {},
},
}
validate(schema, { field1: 'abc', field2: 'abcde' }).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should evaluate custom validation promise', done => {
const validationFunction = value =>
new Promise(resolve => {
const error = value === 'abc' ? { error: 'customError', message: 'Custom error' } : null
resolve(error)
})
const schema = {
type: 'object',
fields: {
field1: { type: 'string', validation: validationFunction },
field2: { type: 'string', validation: validationFunction },
},
}
const expectedResult = {
fields: {
field1: { errors: [{ error: 'customError', message: 'Custom error' }] },
field2: {},
},
}
validate(schema, { field1: 'abc', field2: 'abcde' }).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('allow the use of a regular expression to add validations', done => {
const schema = {
type: 'object',
fields: {
correctChars: { type: 'string', regex: /^[a-zA-ZñÑ ]+$/ },
incorrectChars: { type: 'string', regex: /^[a-zA-ZñÑ ]+$/ },
correctNum: { type: 'string', regex: /^[0-9]+$/ },
incorrectNum: { type: 'string', regex: /^[0-9]+$/ },
},
}
const expectedResult = {
fields: {
correctChars: {},
incorrectChars: { errors: [{ error: 'regex', message: 'The field value is invalid' }] },
correctNum: {},
incorrectNum: { errors: [{ error: 'regex', message: 'The field value is invalid' }] },
},
}
const data = { correctChars: 'abcñ Ñ ABC', incorrectChars: '@abc', correctNum: '123', incorrectNum: '123a' }
validate(schema, data).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return errors for each field on multiple fields schema', done => {
const schema = {
type: 'object',
fields: {
numberField: { type: 'number', isRequired: true },
textField: { type: 'string', isRequired: true },
},
}
const expectedResult = {
fields: {
numberField: { errors: [{ error: 'isRequired', message: 'The field is required' }] },
textField: { errors: [{ error: 'isRequired', message: 'The field is required' }] },
},
}
validate(schema, {}).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return errors on one fields on multiple fields schema', done => {
const schema = {
type: 'object',
fields: {
numberField: { type: 'number', isRequired: true },
textField: { type: 'string', isRequired: true },
},
}
const expectedResult = {
fields: {
numberField: { errors: [{ error: 'isRequired', message: 'The field is required' }] },
textField: {},
},
}
validate(schema, { textField: '123' }).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
it('should return error on array min length', done => {
const schema = { type: 'array', minLength: 3 }
const expectedResult = { errors: [{ error: 'minLength', message: 'This field must be larger' }] }
validate(schema, [1, 2]).then(result => {
assert.deepEqual(result, expectedResult)
done()
})
})
// TODO: Handle custom types: 'email', 'phoneNumber', ...
// TODO Warn about missing schema.type
// TODO: Handle exceptions for arrays and objects...
// TODO: Warn about object type without fields
// TODO: warn about schema errors
// TODO: Add i18n
// TODO: Create constants to describe allowed data types
})
})