Skip to content

Instantly share code, notes, and snippets.

@husa
Last active July 17, 2018 11:26
Show Gist options
  • Save husa/42696ddd845486964dc7dc52971a08ac to your computer and use it in GitHub Desktop.
Save husa/42696ddd845486964dc7dc52971a08ac to your computer and use it in GitHub Desktop.
Formik validation
withFormik({
// ...
validate: createValidator({
title: ['required', rules.minLength(3), rules.maxLength(400), 'commonText'],
price: ['required', 'price', price => price === 1000 ? 'Price can not be 1000' : null]
}),
// ...
})
const isNotANumber = val => val && isNaN(Number(val));
const isRequired = val => !val || (typeof val === 'string' && !val.trim());
export const rules = {
required: {
predicate: val => isRequired(val),
message: 'The field is required.'
},
number: {
predicate: val => isNotANumber(val),
message: 'Please, enter valid number'
},
digits: {
predicate: val => val && !/^[0-9-]*$/.test(val),
message: 'Please, use digits only'
},
alphanumeric: {
predicate: val => val && !/^[a-zA-Z0-9]*$/.test(val),
message: 'Please, use letters and digits only'
},
lettersAndDigits: {
predicate: val => val && !/^[a-zA-Z0-9_.-]*$/.test(val),
message: 'Please, use letters and digits only'
},
lettersDigitsAndSpaces: {
predicate: val => val && !/^[a-zA-Z0-9 _.-]*$/.test(val),
message: 'Please, use digits, numbers and spaces only'
},
commonText: {
predicate: val => val && !/^[a-zA-Z0-9,:;*()$#@% _.-]*$/.test(val),
message:
'Please, use digits, numbers and spaces and following symbols (, : ; * () $ # @ %) only'
},
price: {
predicate: val =>
(val && !/^\d+(\.\d{1,2})?$/.test(val)) ||
parseFloat(val) < 0.01 ||
parseFloat(val) > 2000000,
message:
'Price is invalid. Please, enter a real number between 0.01 and 2 000 000 with 2 decimals after dot.\n' +
'Example: 555.05'
},
minLength: len => val =>
val && (val.length < len ? `Please, use minimum ${len} characters` : null),
maxLength: len => val => val && (val.length > len ? `Please, use up to ${len} characters.` : null)
};
export const getMessage = (fieldRules, value, values) => {
for (const rule of fieldRules) {
if (typeof rule === 'string' && rules[rule]) {
if (rules[rule].predicate(value)) {
return rules[rule].message;
}
}
if (typeof rule === 'function') {
const msg = rule(value, values);
if (msg) return msg;
}
}
return null;
};
const createValidator = (rulesMap = {}) => values => {
return Object.entries(rulesMap).reduce((acc, [name, fieldRules]) => {
const msg = getMessage(fieldRules, values[name], values);
if (msg) acc[name] = msg;
return acc;
}, {});
};
export default createValidator;
import createValidator, {rules} from '../validation';
describe('Utils > validation', () => {
describe('createValidator', () => {
it('should always return function', () => {
expect(typeof createValidator()).toEqual('function');
});
describe('created validator', () => {
let validator;
it('should always return object', () => {
validator = createValidator();
expect(validator()).toEqual({});
validator = createValidator({});
expect(validator({})).toEqual({});
});
it('should run default validation rules', () => {
const originalRequiredPredicate = rules.required.predicate;
const mockFn = jest.fn();
rules.required.predicate = mockFn;
validator = createValidator({
foo: ['required']
});
validator({
foo: 'bar'
});
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('bar');
rules.required.predicate = originalRequiredPredicate;
});
it('should run custom validation rules', () => {
const mockFn = jest.fn();
validator = createValidator({
foo: [mockFn]
});
const rules = {
foo: 'bar'
};
expect(validator(rules));
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('bar', rules);
});
it('should return first message found', () => {
const withMessage = jest.fn().mockImplementation(() => 'msg');
const withOutMessage = jest.fn().mockImplementation(() => null);
validator = createValidator({
foo: [withMessage, withOutMessage]
});
expect(
validator({
foo: 'bar'
})
).toEqual({
foo: 'msg'
});
validator = createValidator({
foo: [withOutMessage, withMessage]
});
expect(
validator({
foo: 'bar'
})
).toEqual({
foo: 'msg'
});
});
it('should return empty object if no message found', () => {
const mockFn = jest.fn();
validator = createValidator({
foo: [mockFn]
});
expect(
validator({
foo: 'bar'
})
).toEqual({});
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment