2020-10-26 20:54:43 +03:00
|
|
|
import {
|
|
|
|
createValidator,
|
|
|
|
enlarge,
|
|
|
|
parseModifier,
|
|
|
|
processSingleArrayConstraint,
|
|
|
|
processSingleStringConstraint,
|
|
|
|
validate,
|
|
|
|
} from '@/validation/validator.ts'
|
2020-10-22 16:37:57 +03:00
|
|
|
|
2020-10-26 20:54:43 +03:00
|
|
|
const isNumberAndInRangeRule = ({ value }, from, to) => !isNaN(value) && value >= from && value <= to
|
|
|
|
const isNumberAndInRangeMessage = ({ value }, from, to) => {
|
|
|
|
return isNaN(value) ? 'Value is NaN' : `Value not in range [${from}, ${to}]`
|
|
|
|
}
|
2020-10-22 16:37:57 +03:00
|
|
|
|
2020-10-26 20:54:43 +03:00
|
|
|
describe('createValidator', () => {
|
|
|
|
it ('Creates correct validator', async () => {
|
|
|
|
const context = { value: 'abc', formValues: {}, name: 'field' }
|
|
|
|
const validate = createValidator(
|
|
|
|
isNumberAndInRangeRule,
|
|
|
|
'rule',
|
|
|
|
[1, 2],
|
|
|
|
isNumberAndInRangeMessage,
|
|
|
|
)
|
|
|
|
|
|
|
|
await expect(validate(context)).toBeInstanceOf(Promise)
|
|
|
|
expect(await validate(context)).toEqual({
|
|
|
|
rule: 'rule',
|
|
|
|
args: [1, 2],
|
|
|
|
context,
|
|
|
|
message: 'Value is NaN',
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(await validate({ ...context, value: 0 })).toEqual({
|
|
|
|
rule: 'rule',
|
|
|
|
args: [1, 2],
|
|
|
|
context: { ...context, value: 0 },
|
|
|
|
message: 'Value not in range [1, 2]',
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(await validate({ ...context, value: 1.5 })).toBeNull()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('enlarge', () => {
|
|
|
|
it ('Merges non-bail validator groups', () => {
|
|
|
|
expect(enlarge([
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
])).toEqual([
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
it ('Merges non-bail validator groups, bail groups stayed unmerged', () => {
|
|
|
|
expect(enlarge([
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: true },
|
|
|
|
{ validators: [], bail: true },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
])).toEqual([
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
{ validators: [], bail: true },
|
|
|
|
{ validators: [], bail: true },
|
|
|
|
{ validators: [], bail: false },
|
|
|
|
])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('parseModifier', () => {
|
|
|
|
it ('Extracts modifier if present', () => {
|
|
|
|
expect(parseModifier('^required')).toEqual(['required', '^'])
|
|
|
|
expect(parseModifier('required')).toEqual(['required', null])
|
|
|
|
expect(parseModifier('bail')).toEqual(['bail', null])
|
|
|
|
expect(parseModifier('^min_length')).toEqual(['minLength', '^'])
|
|
|
|
expect(parseModifier('min_length')).toEqual(['minLength', null])
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('processSingleArrayConstraint', () => {
|
|
|
|
const rules = { isNumberAndInRange: isNumberAndInRangeRule }
|
|
|
|
const messages = { isNumberAndInRange: isNumberAndInRangeMessage }
|
|
|
|
|
|
|
|
it ('Creates validator context if constraint is valid and rule exists', () => {
|
|
|
|
expect(processSingleArrayConstraint(['isNumberAndInRange', 1, 2], rules, messages)).toEqual([
|
|
|
|
expect.any(Function),
|
|
|
|
'isNumberAndInRange',
|
|
|
|
null,
|
|
|
|
])
|
|
|
|
|
|
|
|
expect(processSingleArrayConstraint(['^is_number_and_in_range', 1, 2], rules, messages)).toEqual([
|
|
|
|
expect.any(Function),
|
|
|
|
'isNumberAndInRange',
|
|
|
|
'^',
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
it ('Creates validator context if constraint is validator', () => {
|
|
|
|
const validate = createValidator(
|
|
|
|
isNumberAndInRangeRule,
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
isNumberAndInRangeMessage,
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(processSingleArrayConstraint([validate], rules, messages)).toEqual([
|
|
|
|
expect.any(Function),
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
it ('Throws error if constraint is valid and rule not exists', () => {
|
|
|
|
expect(() => processSingleArrayConstraint(
|
|
|
|
['^rule_that_not_exists'],
|
|
|
|
{ rule: isNumberAndInRangeRule },
|
|
|
|
{ rule: isNumberAndInRangeMessage },
|
|
|
|
)).toThrow('[Formulario] Can\'t create validator for constraint: [\"^rule_that_not_exists\"]')
|
|
|
|
})
|
|
|
|
|
|
|
|
it ('Throws error if constraint is not valid', () => {
|
|
|
|
expect(() => processSingleArrayConstraint(
|
|
|
|
[null],
|
|
|
|
{ rule: isNumberAndInRangeRule },
|
|
|
|
{ rule: isNumberAndInRangeMessage },
|
|
|
|
)).toThrow('[Formulario]: For array constraint first element must be rule name or Validator function')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('processSingleStringConstraint', () => {
|
|
|
|
const rules = { isNumberAndInRange: isNumberAndInRangeRule }
|
|
|
|
const messages = { isNumberAndInRange: isNumberAndInRangeMessage }
|
|
|
|
|
|
|
|
it ('Creates validator context if constraint is valid and rule exists', () => {
|
|
|
|
expect(processSingleStringConstraint('isNumberAndInRange:1,2', rules, messages)).toEqual([
|
|
|
|
expect.any(Function),
|
|
|
|
'isNumberAndInRange',
|
|
|
|
null,
|
|
|
|
])
|
|
|
|
|
|
|
|
expect(processSingleStringConstraint('^is_number_and_in_range:1,2', rules, messages)).toEqual([
|
|
|
|
expect.any(Function),
|
|
|
|
'isNumberAndInRange',
|
|
|
|
'^',
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
it ('Throws error if constraint is valid and rule not exists', () => {
|
|
|
|
expect(() => processSingleStringConstraint(
|
|
|
|
'^rule_that_not_exists',
|
|
|
|
{ rule: isNumberAndInRangeRule },
|
|
|
|
{ rule: isNumberAndInRangeMessage },
|
|
|
|
)).toThrow('[Formulario] Can\'t create validator for constraint: ^rule_that_not_exists')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('validate', () => {
|
|
|
|
const isNumber = createValidator(
|
|
|
|
({ value }) => String(value) !== '' && !isNaN(value),
|
|
|
|
'number',
|
|
|
|
[],
|
|
|
|
() => 'Value is NaN'
|
|
|
|
)
|
|
|
|
const isRequired = createValidator(
|
|
|
|
({ value }) => value !== undefined && String(value) !== '',
|
|
|
|
'required',
|
|
|
|
[],
|
|
|
|
() => 'Value is required'
|
|
|
|
)
|
|
|
|
const context = { value: '', formValues: {}, name: 'field' }
|
|
|
|
|
|
|
|
it('Applies all rules if no bail', async () => {
|
|
|
|
expect(await validate([
|
|
|
|
[isRequired, 'required', null],
|
|
|
|
[isNumber, 'number', null],
|
|
|
|
], context)).toEqual([{
|
|
|
|
rule: 'required',
|
|
|
|
args: [],
|
|
|
|
context,
|
|
|
|
message: 'Value is required',
|
2020-10-22 16:37:57 +03:00
|
|
|
}, {
|
2020-10-26 20:54:43 +03:00
|
|
|
rule: 'number',
|
|
|
|
args: [],
|
|
|
|
context,
|
|
|
|
message: 'Value is NaN',
|
|
|
|
}])
|
|
|
|
})
|
|
|
|
|
|
|
|
it('Applies only first rule (bail)', async () => {
|
|
|
|
expect(await validate([
|
|
|
|
[() => {}, 'bail', null],
|
|
|
|
[isRequired, 'required', '^'],
|
|
|
|
[isNumber, 'number', null],
|
|
|
|
], context)).toEqual([{
|
|
|
|
rule: 'required',
|
|
|
|
args: [],
|
|
|
|
context,
|
|
|
|
message: 'Value is required',
|
|
|
|
}])
|
|
|
|
})
|
|
|
|
|
|
|
|
it('Applies only first rule (bail modifier)', async () => {
|
|
|
|
expect(await validate([
|
|
|
|
[isRequired, 'required', '^'],
|
|
|
|
[isNumber, 'number', null],
|
|
|
|
], context)).toEqual([{
|
|
|
|
rule: 'required',
|
|
|
|
args: [],
|
|
|
|
context,
|
|
|
|
message: 'Value is required',
|
2020-10-22 16:37:57 +03:00
|
|
|
}])
|
|
|
|
})
|
2020-10-26 20:54:43 +03:00
|
|
|
|
|
|
|
it('No violations on valid context', async () => {
|
|
|
|
expect(await validate([
|
|
|
|
[isRequired, 'required', '^'],
|
|
|
|
[isNumber, 'number', null],
|
|
|
|
], { ...context, value: 0 })).toEqual([])
|
|
|
|
})
|
2020-10-22 16:37:57 +03:00
|
|
|
})
|