de-formed-validations
Version:
Function-based modular validations
493 lines (449 loc) • 14.8 kB
text/typescript
import { ValidationSchema, ValidationState } from '../validations/types';
import { Validation } from '../validations/validation';
type TestSchema = {
name: string;
age: number;
dingo?: boolean;
};
const schema: ValidationSchema<TestSchema> = {
name: [
{
errorMessage: 'Name is required.',
validation: (val: string) => val.length > 0,
},
{
errorMessage: 'Cannot be bob.',
validation: (val: string) => val !== 'bob',
},
{
errorMessage: 'Must be dingo.',
validation: (val: string, state: any) => {
return state.dingo ? val === 'dingo' : true;
},
},
],
age: [
{
errorMessage: 'Must be 18.',
validation: (val: number) => val >= 18,
},
],
};
const mockValidationState: ValidationState = {
name: {
isValid: true,
errors: [],
},
age: {
isValid: true,
errors: [],
},
};
const defaultState = {
name: 'jack',
dingo: false,
age: 42,
};
const failingState = {
...defaultState,
name: 'bob',
age: 15,
};
describe('useValidation tests', () => {
it('should be defined', () => {
expect(Validation).toBeDefined();
});
it('builds the object correctly and checks types', () => {
const v = new Validation(schema);
expect(typeof v.getError).toBe('function');
expect(typeof v.getAllErrors).toBe('function');
expect(typeof v.getFieldValid).toBe('function');
expect(typeof v.isValid).toBe('boolean');
expect(typeof v.validate).toBe('function');
expect(typeof v.validateAll).toBe('function');
expect(typeof v.validateIfTrue).toBe('function');
expect(typeof v.validateOnBlur).toBe('function');
expect(typeof v.validateOnChange).toBe('function');
expect(Array.isArray(v.validationErrors)).toBe(true);
expect(typeof v.validationState).toBe('object');
});
it('returns all functions and read-only objects defined by class', () => {
const v = new Validation(schema);
expect(v.validationState).toStrictEqual(mockValidationState);
expect(Object.keys(v)).toStrictEqual([
'createValidationsState',
'resetValidationState',
'forceValidationState',
'allValid',
'runAllValidators',
'getError',
'getAllErrors',
'getFieldValid',
'validate',
'validateAll',
'validateCustom',
'validateIfTrue',
'validateOnBlur',
'validateOnChange',
'_validationSchema',
'_validationState',
]);
});
describe('getError', () => {
it('returns empty string by default', () => {
const v = new Validation(schema);
const output = v.getError('name');
expect(output).toBe('');
});
it('returns empty string if the property does not exist', () => {
const v = new Validation(schema);
const output = v.getError('balls' as keyof TestSchema);
expect(output).toBe('');
});
it('retrieves an error message', () => {
const v = new Validation(schema);
const name = 'name';
const value = '';
const state = defaultState;
v.validate(name, value, state);
const output = v.getError('name');
expect(output).toBe('Name is required.');
});
});
describe('getAllErrors', () => {
it('returns empty array by default', () => {
const v = new Validation(schema);
const output = v.getAllErrors('name');
expect(output).toStrictEqual([]);
});
it('returns empty array if the property does not exist', () => {
const v = new Validation(schema);
const output = v.getAllErrors('balls' as keyof TestSchema);
expect(output).toStrictEqual([]);
});
it('retrieves array of all error messages', () => {
const v = new Validation(schema);
const name = 'name';
const value = '';
const state = defaultState;
v.validate(name, value, state);
const output = v.getAllErrors('name');
expect(output).toStrictEqual(['Name is required.']);
});
});
describe('getFieldValid', () => {
it('returns true by default', () => {
const v = new Validation(schema);
const output = v.getFieldValid('name');
expect(output).toBe(true);
});
it('returns true if the property does not exist', () => {
const v = new Validation(schema);
const output = v.getFieldValid('balls' as keyof TestSchema);
expect(output).toBe(true);
});
it('retrieves an invalid state', () => {
const v = new Validation(schema);
const name = 'name';
const value = '';
const state = defaultState;
v.validate(name, value, state);
const output = v.getFieldValid('name');
expect(output).toBe(false);
});
});
describe('isValid', () => {
it('returns true by default', () => {
const v = new Validation(schema);
expect(v.isValid).toBe(true);
});
it('changes to false after a validation fails', () => {
let output: boolean;
const v = new Validation(schema);
const state = defaultState;
output = v.validate('name', 'bob', state);
expect(v.isValid).toBe(output);
expect(v.isValid).toBe(false);
});
it('changes to true after a failed validation passes', () => {
const v = new Validation(schema);
const state = defaultState;
v.validate('name', 'bob', state);
v.validate('name', 'bob ross', state);
const output = v.isValid;
expect(output).toBe(true);
});
});
describe('validate', () => {
it('returns a boolean if key exists', () => {
const v = new Validation(schema);
let output: boolean;
const name = 'name';
const value = 'bob';
const state = defaultState;
output = v.validate(name, value, state);
expect(typeof output).toBe('boolean');
});
it('returns true if key does not exist', () => {
let output: boolean;
const v = new Validation(schema);
const name = 'balls' as keyof TestSchema;
const value = 'bob';
const state = defaultState;
output = v.validate(name, value, state);
expect(output).toBe(true);
});
it('updates the validationState when validation fails', () => {
const v = new Validation(schema);
const validationState: ValidationState = {
...mockValidationState,
name: {
isValid: false,
errors: ['Must be dingo.'],
},
};
const name = 'name';
const value = 'chuck';
const state = { dingo: true } as TestSchema;
v.validate(name, value, state);
expect(v.isValid).toBe(false);
expect(v.validationState).toStrictEqual(validationState);
});
});
describe('validateAll', () => {
it('returns a boolean', () => {
const v = new Validation(schema);
let output: boolean;
output = v.validateAll(defaultState);
expect(typeof output).toBe('boolean');
});
it('returns true if validations pass', () => {
const v = new Validation(schema);
let output: boolean;
output = v.validateAll(defaultState);
expect(output).toBe(true);
});
it('returns false if any validation fails', () => {
const v = new Validation(schema);
let output: boolean;
output = v.validateAll(failingState);
expect(output).toBe(false);
});
});
describe('validateCustom', () => {
const weirdSchema = {
namesAreAllBob: [
{
errorMessage: 'Names all have to be bob.',
validation: (names: string[]) => {
return names.reduce((acc: boolean, name: string) => {
return acc ? name === 'bob' : false;
}, true);
},
},
],
namesAreAllDingo: [
{
errorMessage: 'Names all have to be dino if dingo is true.',
validation: (names: string[], object: any) => {
return names.reduce((acc: boolean, name: string) => {
if (object.dingo === true) {
return acc ? name === 'dingo' : false;
}
return true;
}, true);
},
},
],
};
it('returns a boolean', () => {
const v = new Validation(weirdSchema);
let output: boolean;
const validNames = ['bob', 'bob', 'bob'];
output = v.validateCustom([
{ key: 'namesAreAllBob', value: validNames },
{ key: 'namesAreAllDingo', value: validNames, state: defaultState },
]);
expect(typeof output).toBe('boolean');
});
it('returns true if validations pass', () => {
const v = new Validation(weirdSchema);
let output: boolean;
const validNames = ['bob', 'bob', 'bob'];
output = v.validateCustom([
{ key: 'namesAreAllBob', value: validNames },
{ key: 'namesAreAllDingo', value: validNames, state: defaultState },
]);
expect(output).toBe(true);
});
it('returns false if validations fail', () => {
const v = new Validation(weirdSchema);
const invalidNames = ['jack', 'bob', 'bob'];
let output: boolean;
output = v.validateCustom([
{ key: 'namesAreAllBob', value: invalidNames },
{ key: 'namesAreAllDingo', value: invalidNames, state: defaultState },
]);
expect(output).toBe(false);
});
it('updates validation state', () => {
const v = new Validation(weirdSchema);
const invalidNames = ['jack', 'bob', 'bob'];
v.validateCustom([
{ key: 'namesAreAllBob', value: invalidNames },
{ key: 'namesAreAllDingo', value: invalidNames, state: defaultState },
]);
expect(v.isValid).toBe(false);
});
it('takes an optional object for second argument', () => {
const v = new Validation(weirdSchema);
const validNames = ['dingo', 'dingo', 'dingo'];
v.validateCustom([
{ key: 'namesAreAllDingo', value: validNames, state: { dingo: true } },
]);
expect(v.isValid).toBe(true);
});
});
describe('validateIfTrue', () => {
it('returns a boolean if key exists', () => {
const v = new Validation(schema);
let output: boolean;
const name = 'name';
const value = 'bob';
const state = defaultState;
output = v.validateIfTrue(name, value, state);
expect(typeof output).toBe('boolean');
});
it('returns true if key does not exist', () => {
const v = new Validation(schema);
const name = 'balls' as keyof TestSchema;
const value = 'bob';
const state = defaultState;
let output: boolean;
output = v.validateIfTrue(name, value, state);
expect(output).toBe(true);
});
it('does not update the validationState when validation fails', () => {
const v = new Validation(schema);
const validationState = {
...mockValidationState,
};
const name = 'name';
const value = 'chuck';
const state = { dingo: true } as TestSchema;
v.validateIfTrue(name, value, state);
expect(v.isValid).toBe(true);
expect(v.validationState).toStrictEqual(validationState);
});
// TODO: fix isValid
it('updates the validationState when an invalid validation succeeds', () => {
const v = new Validation(schema);
const state = defaultState;
const validationState = {
...mockValidationState,
};
v.validate('name', 'bob', state);
expect(v.isValid).toBe(false);
v.validateIfTrue('name', 'jack', state);
expect(v.isValid).toBe(true);
expect(v.validationState).toStrictEqual(validationState);
});
});
describe('validateOnBlur', () => {
it('returns a new function', () => {
const v = new Validation(schema);
const state = defaultState;
const handleBlur = v.validateOnBlur(state);
expect(typeof handleBlur).toBe('function');
});
it('updates the valdiation state when called', () => {
const v = new Validation(schema);
const state = defaultState;
const handleBlur = v.validateOnBlur(state);
const event = {
target: {
name: 'name',
value: 'bob',
dispatchEvent: new Event('blur'),
},
};
handleBlur(event as any);
expect(v.isValid).toBe(false);
});
});
describe('validateOnChange', () => {
it('returns a new function', () => {
const v = new Validation(schema);
const state = defaultState;
const onChange = () => 'bob ross';
const handleChange = v.validateOnChange(onChange, state);
expect(typeof handleChange).toBe('function');
});
it('updates the valdiation state if true and returns event', () => {
const v = new Validation(schema);
const state = defaultState;
v.validate('name', 'bob', defaultState);
expect(v.isValid).toBe(false);
const onChange = () => 'bob ross';
const handleChange = v.validateOnChange(onChange, state);
const event = {
target: {
name: 'name',
value: 'jack',
dispatchEvent: new Event('change'),
},
};
let output: any;
output = handleChange(event as any);
expect(v.isValid).toBe(true);
expect(output).toBe('bob ross');
});
});
describe('validationErrors', () => {
it('returns an empty array', () => {
const v = new Validation(schema);
expect(v.validationErrors).toStrictEqual([]);
});
it('returns an array of all errors', () => {
const v = new Validation(schema);
v.validateAll(failingState);
expect(v.validationErrors).toStrictEqual([
'Cannot be bob.',
'Must be 18.',
]);
});
});
describe('resetValidationState', () => {
it('resets the validation state', () => {
const v = new Validation(schema);
v.validateAll(failingState);
v.resetValidationState();
expect(v.isValid).toBe(true);
});
});
describe('validationErrors', () => {
it('adds validation errors when validation state is invalid', () => {
const v = new Validation(schema);
v.validateAll(failingState);
expect(v.validationErrors).toStrictEqual([
'Cannot be bob.',
'Must be 18.',
]);
});
it('removes validation errors when validation state is valid', () => {
const v = new Validation(schema);
v.validateAll(failingState);
v.validateAll(defaultState);
expect(v.validationErrors).toStrictEqual([]);
});
});
describe('forceValidationState', () => {
it('overrides the existing validation state with a new one', () => {
const v1 = new Validation(schema);
const v2 = new Validation(schema);
v1.validateAll(failingState);
v2.forceValidationState(v1.validationState);
expect(v1.validationState).toStrictEqual(v2.validationState);
});
});
});