1
0
mirror of synced 2024-11-21 21:06:04 +03:00

refactor: Got rid of promises in default validation rules fns, rewritten rules tests

This commit is contained in:
Zaytsev Kirill 2020-11-06 20:23:01 +03:00
parent a0345a605e
commit 8e3bacab3c
2 changed files with 449 additions and 495 deletions

View File

@ -1,264 +1,259 @@
import isUrl from 'is-url'
import { shallowEqualObjects, regexForFormat, has } from '@/utils'
import { has, regexForFormat, shallowEqualObjects } from '@/utils'
import { ValidationContext } from '@/validation/validator'
interface DateValidationContext extends ValidationContext {
value: Date|string;
}
export default {
/**
* Rule: the value must be "yes", "on", "1", or true
*/
accepted ({ value }: ValidationContext): Promise<boolean> {
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
accepted ({ value }: ValidationContext): boolean {
return ['yes', 'on', '1', 1, true, 'true'].includes(value)
},
/**
* Rule: checks if a value is after a given date. Defaults to current time
*/
after ({ value }: DateValidationContext, compare: string | false = false): Promise<boolean> {
const timestamp = compare !== false ? Date.parse(compare) : Date.now()
const fieldValue = value instanceof Date ? value.getTime() : Date.parse(value)
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
after ({ value }: ValidationContext, compare: string | false = false): boolean {
const compareTimestamp = compare !== false ? Date.parse(compare) : Date.now()
const valueTimestamp = value instanceof Date ? value.getTime() : Date.parse(value)
return isNaN(valueTimestamp) ? false : (valueTimestamp > compareTimestamp)
},
/**
* Rule: checks if the value is only alpha
*/
alpha ({ value }: { value: string }, set = 'default'): Promise<boolean> {
alpha ({ value }: ValidationContext, set = 'default'): boolean {
const sets: Record<string, RegExp> = {
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
latin: /^[a-zA-Z]+$/
latin: /^[a-zA-Z]+$/,
}
const selectedSet = has(sets, set) ? set : 'default'
return Promise.resolve(sets[selectedSet].test(value))
return typeof value === 'string' && sets[has(sets, set) ? set : 'default'].test(value)
},
/**
* Rule: checks if the value is alpha numeric
*/
alphanumeric ({ value }: { value: string }, set = 'default'): Promise<boolean> {
alphanumeric ({ value }: ValidationContext, set = 'default'): boolean {
const sets: Record<string, RegExp> = {
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
latin: /^[a-zA-Z0-9]+$/
}
const selectedSet = has(sets, set) ? set : 'default'
return Promise.resolve(sets[selectedSet].test(value))
return typeof value === 'string' && sets[has(sets, set) ? set : 'default'].test(value)
},
/**
* Rule: checks if a value is after a given date. Defaults to current time
*/
before ({ value }: DateValidationContext, compare: string|false = false): Promise<boolean> {
const timestamp = compare !== false ? Date.parse(compare) : Date.now()
const fieldValue = value instanceof Date ? value.getTime() : Date.parse(value)
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
before ({ value }: ValidationContext, compare: string|false = false): boolean {
const compareTimestamp = compare !== false ? Date.parse(compare) : Date.now()
const valueTimestamp = value instanceof Date ? value.getTime() : Date.parse(value)
return isNaN(valueTimestamp) ? false : (valueTimestamp < compareTimestamp)
},
/**
* Rule: checks if the value is between two other values
*/
between ({ value }: { value: string|number }, from: number|any = 0, to: number|any = 10, force?: string): Promise<boolean> {
return Promise.resolve(((): boolean => {
if (from === null || to === null || isNaN(from) || isNaN(to)) {
return false
}
if ((!isNaN(Number(value)) && force !== 'length') || force === 'value') {
value = Number(value)
from = Number(from)
to = Number(to)
return (value > from && value < to)
}
if (typeof value === 'string' || force === 'length') {
value = (!isNaN(Number(value)) ? value.toString() : value) as string
return value.length > from && value.length < to
}
between ({ value }: ValidationContext, from: number|any = 0, to: number|any = 10, force?: string): boolean {
if (from === null || to === null || isNaN(from) || isNaN(to)) {
return false
})())
}
if ((!isNaN(Number(value)) && force !== 'length') || force === 'value') {
value = Number(value)
return (value > Number(from) && value < Number(to))
}
if (typeof value === 'string' || force === 'length') {
value = (!isNaN(Number(value)) ? value.toString() : value) as string
return value.length > from && value.length < to
}
return false
},
/**
* Confirm that the value of one field is the same as another, mostly used
* for password confirmations.
*/
confirm ({ value, formValues, name }: ValidationContext, field?: string): Promise<boolean> {
return Promise.resolve(((): boolean => {
let confirmationFieldName = field
if (!confirmationFieldName) {
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : `${name}_confirm`
}
return formValues[confirmationFieldName] === value
})())
confirm ({ value, formValues, name }: ValidationContext, field?: string): boolean {
let confirmationFieldName = field
if (!confirmationFieldName) {
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : `${name}_confirm`
}
return formValues[confirmationFieldName] === value
},
/**
* Rule: ensures the value is a date according to Date.parse(), or a format
* regex.
*/
date ({ value }: { value: string }, format: string | false = false): Promise<boolean> {
return Promise.resolve(format ? regexForFormat(format).test(value) : !isNaN(Date.parse(value)))
date ({ value }: ValidationContext, format: string | false = false): boolean {
return format ? regexForFormat(format).test(value) : !isNaN(Date.parse(value))
},
/**
* Rule: tests
*/
email ({ value }: { value: string }): Promise<boolean> {
email ({ value }: ValidationContext): boolean {
if (!value) {
return Promise.resolve(true)
return true
}
// eslint-disable-next-line
const isEmail = /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
return Promise.resolve(isEmail.test(value))
return isEmail.test(value)
},
/**
* Rule: Value ends with one of the given Strings
*/
endsWith ({ value }: { value: any }, ...stack: any[]): Promise<boolean> {
endsWith ({ value }: ValidationContext, ...stack: any[]): boolean {
if (!value) {
return Promise.resolve(true)
return true
}
if (typeof value === 'string') {
return Promise.resolve(stack.length === 0 || stack.some(str => value.endsWith(str)))
return stack.length === 0 || stack.some(str => value.endsWith(str))
}
return Promise.resolve(false)
return false
},
/**
* Rule: Value is in an array (stack).
*/
in ({ value }: { value: any }, ...stack: any[]): Promise<boolean> {
return Promise.resolve(stack.some(item => {
return typeof item === 'object' ? shallowEqualObjects(item, value) : item === value
}))
in ({ value }: ValidationContext, ...stack: any[]): boolean {
return stack.some(item => typeof item === 'object' ? shallowEqualObjects(item, value) : item === value)
},
/**
* Rule: Match the value against a (stack) of patterns or strings
*/
matches ({ value }: { value: any }, ...stack: any[]): Promise<boolean> {
return Promise.resolve(!!stack.find(pattern => {
matches ({ value }: ValidationContext, ...stack: any[]): boolean {
return !!stack.find(pattern => {
if (typeof pattern === 'string' && pattern.substr(0, 1) === '/' && pattern.substr(-1) === '/') {
pattern = new RegExp(pattern.substr(1, pattern.length - 2))
}
if (pattern instanceof RegExp) {
return pattern.test(value)
}
return pattern === value
}))
})
},
/**
* Check the maximum value of a particular.
*/
max ({ value }: { value: any }, maximum: string | number = 10, force?: string): Promise<boolean> {
return Promise.resolve(((): boolean => {
if (Array.isArray(value)) {
maximum = !isNaN(Number(maximum)) ? Number(maximum) : maximum
return value.length <= maximum
}
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value
return value <= maximum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value
return value.length <= maximum
}
return false
})())
max ({ value }: ValidationContext, maximum: string | number = 10, force?: string): boolean {
if (Array.isArray(value)) {
maximum = !isNaN(Number(maximum)) ? Number(maximum) : maximum
return value.length <= maximum
}
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value
return value <= maximum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value
return value.length <= maximum
}
return false
},
/**
* Check the minimum value of a particular.
*/
min ({ value }: { value: any }, minimum: number | any = 1, force?: string): Promise<boolean> {
return Promise.resolve(((): boolean => {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum
return value.length >= minimum
}
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value
return value >= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value
return value.length >= minimum
}
return false
})())
min ({ value }: ValidationContext, minimum: number | any = 1, force?: string): boolean {
if (Array.isArray(value)) {
minimum = !isNaN(minimum) ? Number(minimum) : minimum
return value.length >= minimum
}
if ((!isNaN(value) && force !== 'length') || force === 'value') {
value = !isNaN(value) ? Number(value) : value
return value >= minimum
}
if (typeof value === 'string' || (force === 'length')) {
value = !isNaN(value) ? value.toString() : value
return value.length >= minimum
}
return false
},
/**
* Rule: Value is not in stack.
*/
not ({ value }: { value: any }, ...stack: any[]): Promise<boolean> {
return Promise.resolve(!stack.some(item => {
return typeof item === 'object' ? shallowEqualObjects(item, value) : item === value
}))
not ({ value }: ValidationContext, ...stack: any[]): boolean {
return !stack.some(item => typeof item === 'object' ? shallowEqualObjects(item, value) : item === value)
},
/**
* Rule: checks if the value is only alpha numeric
*/
number ({ value }: { value: any }): Promise<boolean> {
return Promise.resolve(String(value).length > 0 && !isNaN(Number(value)))
number ({ value }: ValidationContext): boolean {
return String(value).length > 0 && !isNaN(Number(value))
},
/**
* Rule: must be a value
*/
required ({ value }: { value: any }, isRequired: string|boolean = true): Promise<boolean> {
return Promise.resolve(((): boolean => {
if (!isRequired || ['no', 'false'].includes(isRequired as string)) {
return true
}
if (Array.isArray(value)) {
return !!value.length
}
if (typeof value === 'string') {
return !!value
}
if (typeof value === 'object') {
return (!value) ? false : !!Object.keys(value).length
}
required ({ value }: ValidationContext, isRequired: string|boolean = true): boolean {
if (!isRequired || ['no', 'false'].includes(isRequired as string)) {
return true
})())
}
if (Array.isArray(value)) {
return !!value.length
}
if (typeof value === 'string') {
return !!value
}
if (typeof value === 'object') {
return (!value) ? false : !!Object.keys(value).length
}
return true
},
/**
* Rule: Value starts with one of the given Strings
*/
startsWith ({ value }: { value: any }, ...stack: string[]): Promise<boolean> {
startsWith ({ value }: ValidationContext, ...stack: string[]): boolean {
if (!value) {
return Promise.resolve(true)
return true
}
if (typeof value === 'string') {
return Promise.resolve(stack.length === 0 || stack.some(str => value.startsWith(str)))
return stack.length === 0 || stack.some(str => value.startsWith(str))
}
return Promise.resolve(false)
return false
},
/**
* Rule: checks if a string is a valid url
*/
url ({ value }: { value: string }): Promise<boolean> {
return Promise.resolve(isUrl(value))
url ({ value }: ValidationContext): boolean {
return isUrl(value)
},
/**
* Rule: not a true rule more like a compiler flag.
*/
bail (): Promise<boolean> {
return Promise.resolve(true)
}
bail (): boolean {
return true
},
}

View File

@ -1,473 +1,428 @@
import rules from '@/validation/rules.ts'
/**
* Accepted rule
*/
const today = new Date()
const tomorrow = new Date()
const yesterday = new Date()
tomorrow.setDate(today.getDate() + 1)
yesterday.setDate(today.getDate() - 1)
describe('accepted', () => {
it('passes with true', async () => expect(await rules.accepted({ value: 'yes' })).toBe(true))
const validate = value => rules.accepted({ value, name: '', formValues: {} })
const expectPass = value => expect(validate(value)).toBe(true)
const expectFail = value => expect(validate(value)).toBe(false)
it('passes with on', async () => expect(await rules.accepted({ value: 'on' })).toBe(true))
it('passes with 1', async () => expect(await rules.accepted({ value: '1' })).toBe(true))
it('passes with number 1', async () => expect(await rules.accepted({ value: 1 })).toBe(true))
it('passes with boolean true', async () => expect(await rules.accepted({ value: true })).toBe(true))
it('fail with boolean false', async () => expect(await rules.accepted({ value: false })).toBe(false))
it('fail with "false"', async () => expect(await rules.accepted({ value: 'false' })).toBe(false))
it('passes with true', () => expectPass('yes'))
it('passes with on', () => expectPass('on'))
it('passes with 1', () => expectPass('1'))
it('passes with number 1', () => expectPass(1))
it('passes with boolean true', () => expectPass(true))
it('fail with boolean false', () => expectFail(false))
it('fail with "false"', () => expectFail('false'))
})
/**
* Checks if a date is after another date
*/
describe('after', () => {
const today = new Date()
const tomorrow = new Date()
const yesterday = new Date()
tomorrow.setDate(today.getDate() + 1)
yesterday.setDate(today.getDate() - 1)
const validate = (value, compare = false) => rules.after({ value, name: '', formValues: {} }, compare)
const expectPass = (value, compare = false) => expect(validate(value, compare)).toBe(true)
const expectFail = (value, compare = false) => expect(validate(value, compare)).toBe(false)
it('passes with tomorrows date object', async () => expect(await rules.after({ value: tomorrow })).toBe(true))
it('passes with future date', async () => expect(await rules.after({ value: 'January 15, 2999' })).toBe(true))
it('passes with long past date', async () => expect(await rules.after({ value: yesterday }, 'Jan 15, 2000')).toBe(true))
it('fails with yesterdays date', async () => expect(await rules.after({ value: yesterday })).toBe(false))
it('fails with old date string', async () => expect(await rules.after({ value: 'January, 2000' })).toBe(false))
it('fails with invalid value', async () => expect(await rules.after({ value: '' })).toBe(false))
it('passes with tomorrows date object', () => expectPass(tomorrow))
it('passes with future date', () => expectPass('January 15, 2999'))
it('passes with long past date', () => expectPass(yesterday, 'Jan 15, 2000'))
it('fails with yesterdays date', () => expectFail(yesterday))
it('fails with old date string', () => expectFail('January, 2000'))
it('fails with invalid value', () => expectFail(''))
})
/**
* Checks if a date is after another date
*/
describe('alpha', () => {
it('passes with simple string', async () => expect(await rules.alpha({ value: 'abc' })).toBe(true))
const validate = (value, set = 'default') => rules.alpha({ value, name: '', formValues: {} }, set)
it('passes with long string', async () => expect(await rules.alpha({ value: 'lkashdflaosuihdfaisudgflakjsdbflasidufg' })).toBe(true))
it('passes with simple string', () => {
expect(validate('abc')).toBe(true)
})
it('passes with single character', async () => expect(await rules.alpha({ value: 'z' })).toBe(true))
it('passes with long string', () => {
expect(validate('lkashdflaosuihdfaisudgflakjsdbflasidufg')).toBe(true)
})
it('passes with accented character', async () => expect(await rules.alpha({ value: 'jüstin' })).toBe(true))
it('passes with single character', () => {
expect(validate('z')).toBe(true)
})
it('passes with lots of accented characters', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' })).toBe(true))
it('passes with accented character', () => {
expect(validate('jüstin')).toBe(true)
})
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' }, 'russian')).toBe(true))
it('passes with lots of accented characters', () => {
expect(validate('àáâäïíôöÆ')).toBe(true)
})
it('fails with lots of accented characters if latin', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' }, 'latin')).toBe(false))
it('passes with lots of accented characters if invalid set', () => {
expect(validate('àáâäïíôöÆ', 'russian')).toBe(true)
})
it('fails with numbers', async () => expect(await rules.alpha({ value: 'justin83' })).toBe(false))
it('fails with lots of accented characters if latin', () => {
expect(validate('àáâäïíôöÆ', 'latin')).toBe(false)
})
it('fails with symbols', async () => expect(await rules.alpha({ value: '-justin' })).toBe(false))
it('fails with numbers', () => {
expect(validate('justin83')).toBe(false)
})
it('fails with symbols', () => {
expect(validate('-justin')).toBe(false)
})
})
/**
* Checks if a date alpha and numeric
*/
describe('alphanumeric', () => {
it('passes with simple string', async () => expect(await rules.alphanumeric({ value: '567abc' })).toBe(true))
const validate = (value, set = 'default') => rules.alphanumeric({ value, name: '', formValues: {} }, set)
it('passes with long string', async () => expect(await rules.alphanumeric({ value: 'lkashdfla234osuihdfaisudgflakjsdbfla567sidufg' })).toBe(true))
it('passes with simple string', () => {
expect(validate('567abc')).toBe(true)
})
it('passes with single character', async () => expect(await rules.alphanumeric({ value: 'z' })).toBe(true))
it('passes with long string', () => {
expect(validate('lkashdfla234osuihdfaisudgflakjsdbfla567sidufg')).toBe(true)
})
it('passes with accented character', async () => expect(await rules.alphanumeric({ value: 'jüst56in' })).toBe(true))
it('passes with single character', () => {
expect(validate('z')).toBe(true)
})
it('passes with lots of accented characters', async () => expect(await rules.alphanumeric({ value: 'àáâ7567567äïíôöÆ' })).toBe(true))
it('passes with accented character', () => {
expect(validate('jüst56in')).toBe(true)
})
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alphanumeric({ value: '123123àáâäï67íôöÆ' }, 'russian')).toBe(true))
it('passes with lots of accented characters', () => {
expect(validate('àáâ7567567äïíôöÆ')).toBe(true)
})
it('fails with lots of accented characters if latin', async () => expect(await rules.alphanumeric({ value: 'àáâäï123123íôöÆ' }, 'latin')).toBe(false))
it('passes with lots of accented characters if invalid set', () => {
expect(validate('123123àáâäï67íôöÆ', 'russian')).toBe(true)
})
it('fails with decimals in', async () => expect(await rules.alphanumeric({ value: 'abcABC99.123' })).toBe(false))
it('fails with lots of accented characters if latin', () => {
expect(validate('àáâäï123123íôöÆ', 'latin')).toBe(false)
})
it('fails with decimals in', () => {
expect(validate('abcABC99.123')).toBe(false)
})
})
/**
* Checks if a date is after another date
*/
describe('before', () => {
const today = new Date()
const tomorrow = new Date()
const yesterday = new Date()
tomorrow.setDate(today.getDate() + 1)
yesterday.setDate(today.getDate() - 1)
const validate = (value, compare = false) => rules.before({ value, name: '', formValues: {} }, compare)
const expectPass = (value, compare = false) => expect(validate(value, compare)).toBe(true)
const expectFail = (value, compare = false) => expect(validate(value, compare)).toBe(false)
it('fails with tomorrows date object', async () => expect(await rules.before({ value: tomorrow })).toBe(false))
it('fails with future date', async () => expect(await rules.before({ value: 'January 15, 2999' })).toBe(false))
it('fails with long past date', async () => expect(await rules.before({ value: yesterday }, 'Jan 15, 2000')).toBe(false))
it('passes with yesterdays date', async () => expect(await rules.before({ value: yesterday })).toBe(true))
it('passes with old date string', async () => expect(await rules.before({ value: 'January, 2000' })).toBe(true))
it('fails with invalid value', async () => expect(await rules.after({ value: '' })).toBe(false))
it('fails with tomorrows date object', () => expectFail(tomorrow))
it('fails with future date', () => expectFail('January 15, 2999'))
it('fails with long past date', () => expectFail(yesterday, 'Jan 15, 2000'))
it('passes with yesterdays date', () => expectPass(yesterday))
it('passes with old date string', () => expectPass('January, 2000'))
it('fails with invalid value', () => expectFail(''))
})
/**
* Checks if between
*/
describe('between', () => {
it('passes with simple number', async () => expect(await rules.between({ value: 5 }, 0, 10)).toBe(true))
const validate = (value, from, to, force = undefined) => {
return rules.between({value, name: '', formValues: {}}, from, to, force)
}
it('passes with simple number string', async () => expect(await rules.between({ value: '5' }, '0', '10')).toBe(true))
const expectPass = (value, from, to, force = undefined) => expect(validate(value, from, to, force)).toBe(true)
const expectFail = (value, from, to, force = undefined) => expect(validate(value, from, to, force)).toBe(false)
it('passes with decimal number string', async () => expect(await rules.between({ value: '0.5' }, '0', '1')).toBe(true))
it('passes with string length', async () => expect(await rules.between({ value: 'abc' }, 2, 4)).toBe(true))
it('fails with string length too long', async () => expect(await rules.between({ value: 'abcdef' }, 2, 4)).toBe(false))
it('fails with string length too short', async () => expect(await rules.between({ value: 'abc' }, 3, 10)).toBe(false))
it('fails with number to small', async () => expect(await rules.between({ value: 0 }, 3, 10)).toBe(false))
it('fails with number to large', async () => expect(await rules.between({ value: 15 }, 3, 10)).toBe(false))
it('passes when forced to value', async () => expect(await rules.between({ value: '4' }, 3, 10, 'value')).toBe(true))
it('fails when forced to value', async () => expect(await rules.between({ value: 442 }, 3, 10, 'value')).toBe(false))
it('passes when forced to length', async () => expect(await rules.between({ value: 7442 }, 3, 10, 'length')).toBe(true))
it('fails when forced to length', async () => expect(await rules.between({ value: 6 }, 3, 10, 'length')).toBe(false))
it('passes with simple number', () => expectPass(5, 0, 10))
it('passes with simple number string', () => expectPass('5', '0', '10'))
it('passes with decimal number string', () => expectPass('0.5', '0', '1'))
it('passes with string length', () => expectPass('abc', 2, 4))
it('fails with string length too long', () => expectFail('abcdef', 2, 4))
it('fails with string length too short', () => expectFail('abc', 3, 10))
it('fails with number too small', () => expectFail(0, 3, 10))
it('fails with number too large', () => expectFail(15, 3, 10))
it('passes when forced to value', () => expectPass('4', 3, 10, 'value'))
it('fails when forced to value', () => expectFail(442, 3, 10, 'value'))
it('passes when forced to length', () => expectPass(7442, 3, 10, 'length'))
it('fails when forced to length', () => expectFail(6, 3, 10, 'length'))
})
/**
* Confirm
*/
describe('confirm', () => {
it('Passes when the values are the same strings', async () => expect(await rules.confirm(
{ value: 'abc', name: 'password', formValues: { password_confirm: 'abc' } }
)).toBe(true))
const validate = (context, field = undefined) => rules.confirm(context, field)
const expectPass = (context, field = undefined) => expect(validate(context, field)).toBe(true)
const expectFail = (context, field = undefined) => expect(validate(context, field)).toBe(false)
it('Passes when the values are the same integers', async () => expect(await rules.confirm(
{ value: 4422132, name: 'xyz', formValues: { xyz_confirm: 4422132 } }
)).toBe(true))
it('Passes when the values are the same strings', () => expectPass({
value: 'abc',
name: 'password',
formValues: { password_confirm: 'abc' }
}))
it('Passes when using a custom field', async () => expect(await rules.confirm(
{ value: 4422132, name: 'name', formValues: { other_field: 4422132 } },
'other_field'
)).toBe(true))
it('Passes when the values are the same integers', () => expectPass({
value: 4422132,
name: 'xyz',
formValues: { xyz_confirm: 4422132 }
}))
it('Passes when using a field ends in _confirm', async () => expect(await rules.confirm(
{ value: '$ecret', name: 'password_confirm', formValues: { password: '$ecret' } }
)).toBe(true))
it('Passes when using a custom field', () => expectPass({
value: 4422132,
name: 'name',
formValues: { other_field: 4422132 }
}, 'other_field'))
it('Fails when using different strings', async () => expect(await rules.confirm(
{ value: 'Justin', name: 'name', formValues: { name_confirm: 'Daniel' } },
)).toBe(false))
it('Passes when using a field ends in _confirm', () => expectPass({
value: '$ecret',
name: 'password_confirm',
formValues: { password: '$ecret' }
}))
it('Fails when the types are different', async () => expect(await rules.confirm(
{ value: '1234', name: 'num', formValues: { num_confirm: 1234 } },
)).toBe(false))
it('Fails when using different strings', () => expectFail({
value: 'Justin',
name: 'name',
formValues: { name_confirm: 'Daniel' }
}))
it('Fails when the types are different', () => expectFail({
value: '1234',
name: 'num',
formValues: { num_confirm: 1234 }
}))
})
/**
* Determines if the string is a date
*/
describe('date', () => {
it('passes with month day year', async () => expect(await rules.date({ value: 'December 17, 2020' })).toBe(true))
it('passes with month day', async () => expect(await rules.date({ value: 'December 17' })).toBe(true))
it('passes with short month day', async () => expect(await rules.date({ value: 'Dec 17' })).toBe(true))
it('passes with short month day', async () => expect(await rules.date({ value: 'Dec 17 12:34:15' })).toBe(true))
it('passes with out of bounds number', async () => expect(await rules.date({ value: 'January 77' })).toBe(true))
it('passes with only month', async () => expect(await rules.date({ value: 'January' })).toBe(false))
it('passes with valid date format', async () => expect(await rules.date({ value: '12/17/1987' }, 'MM/DD/YYYY')).toBe(true))
it('fails with simple number and date format', async () => expect(await rules.date({ value: '1234' }, 'MM/DD/YYYY')).toBe(false))
it('fails with only day of week', async () => expect(await rules.date({ value: 'saturday' })).toBe(false))
it('fails with random string', async () => expect(await rules.date({ value: 'Pepsi 17' })).toBe(false))
it('fails with random number', async () => expect(await rules.date({ value: '1872301237' })).toBe(false))
const validate = (value, format = false) => rules.date({ value, name: '', formValues: {} }, format)
const expectPass = (value, compare = false) => expect(validate(value, compare)).toBe(true)
const expectFail = (value, compare = false) => expect(validate(value, compare)).toBe(false)
it('passes with month day year', () => expectPass('December 17, 2020'))
it('passes with month day', () => expectPass('December 17'))
it('passes with short month day', () => expectPass('Dec 17'))
it('passes with short month day and time', () => expectPass('Dec 17 12:34:15'))
it('passes with out of bounds number', () => expectPass('January 77'))
it('fails with only month', () => expectFail('January'))
it('passes with valid date format', () => expectPass('12/17/1987', 'MM/DD/YYYY'))
it('fails with simple number and date format', () => expectFail('1234', 'MM/DD/YYYY'))
it('fails with only day of week', () => expectFail('saturday'))
it('fails with random string', () => expectFail('Pepsi 17'))
it('fails with random number', () => expectFail('1872301237'))
})
/**
* Checks if email.
*
* Note: testing is light, regular expression used is here: http://jsfiddle.net/ghvj4gy9/embedded/result,js/
*/
describe('email', () => {
it('passes normal email', async () => expect(await rules.email({ value: 'dev+123@wearebraid.com' })).toBe(true))
const validate = value => rules.email({ value, name: '', formValues: {} })
const expectPass = value => expect(validate(value)).toBe(true)
const expectFail = value => expect(validate(value)).toBe(false)
it('passes numeric email', async () => expect(await rules.email({ value: '12345@google.com' })).toBe(true))
it('passes unicode email', async () => expect(await rules.email({ value: 'àlphä@❤️.ly' })).toBe(true))
it('passes numeric with new tld', async () => expect(await rules.email({ value: '12345@google.photography' })).toBe(true))
it('fails string without tld', async () => expect(await rules.email({ value: '12345@localhost' })).toBe(false))
it('fails string without invalid name', async () => expect(await rules.email({ value: '1*(123)2345@localhost' })).toBe(false))
it('passes normal email', () => expectPass('dev+123@wearebraid.com'))
it('passes numeric email', () => expectPass('12345@google.com'))
it('passes unicode email', () => expectPass('àlphä@❤️.ly'))
it('passes numeric with new tld', () => expectPass('12345@google.photography'))
it('fails string without tld', () => expectFail('12345@localhost'))
it('fails string without invalid name', () => expectFail('1*(123)2345@localhost'))
})
/**
* Checks if value ends with a one of the specified Strings.
*/
describe('endsWith', () => {
it('fails when value ending is not in stack of single value', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com' }, '@gmail.com')).toBe(false)
})
const validate = (value, ...stack) => rules.endsWith({ value, name: '', formValues: {} }, ...stack)
const expectPass = (value, ...stack) => expect(validate(value, ...stack)).toBe(true)
const expectFail = (value, ...stack) => expect(validate(value, ...stack)).toBe(false)
it('fails when value ending is not in stack of multiple values', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com' }, '@gmail.com', '@yahoo.com')).toBe(false)
})
it('fails when value ending is not in stack of single value', () => expectFail(
'andrew@wearebraid.com',
'@gmail.com'
))
it('fails when passed value is not a string', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com'}, ['@gmail.com', '@wearebraid.com'])).toBe(false)
})
it('fails when value ending is not in stack of multiple values', () => expectFail(
'andrew@wearebraid.com',
'@gmail.com', '@yahoo.com'
))
it('fails when passed value is not a string', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com'}, {value: '@wearebraid.com'})).toBe(false)
})
it('fails when passed value is not a string', () => expectFail(
'andrew@wearebraid.com',
['@gmail.com', '@wearebraid.com']
))
it('passes when a string value is present and matched even if non-string values also exist as arguments', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com'}, {value: 'bad data'}, ['no bueno'], '@wearebraid.com')).toBe(true)
})
it('fails when passed value is not a string', () => expectFail(
'andrew@wearebraid.com',
{ value: '@wearebraid.com' }
))
it('passes when stack consists of zero values', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com' })).toBe(true)
})
it('passes when a string value is present and matched even if non-string values also exist as arguments', () => {
expectPass('andrew@wearebraid.com', { value: 'bad data' }, ['no bueno'], '@wearebraid.com')
})
it('passes when value ending is in stack of single value', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com' }, '@wearebraid.com')).toBe(true)
})
it('passes when stack consists of zero values', () => expectPass('andrew@wearebraid.com'))
it('passes when value ending is in stack of multiple values', async () => {
expect(await rules.endsWith({ value: 'andrew@wearebraid.com' }, '@yahoo.com', '@wearebraid.com', '@gmail.com')).toBe(true)
})
it('passes when value ending is in stack of single value', () => expectPass(
'andrew@wearebraid.com',
'@wearebraid.com'
))
it('passes when value ending is in stack of multiple values', () => expectPass(
'andrew@wearebraid.com',
'@yahoo.com', '@wearebraid.com', '@gmail.com'
))
})
/**
* In rule
*/
describe('in', () => {
it('fails when not in stack', async () => {
expect(await rules.in({ value: 'third' }, 'first', 'second')).toBe(false)
})
const validate = (value, ...stack) => rules.in({ value, name: '', formValues: {} }, ...stack)
const expectPass = (value, ...stack) => expect(validate(value, ...stack)).toBe(true)
const expectFail = (value, ...stack) => expect(validate(value, ...stack)).toBe(false)
it('fails when case sensitive mismatch is in stack', async () => {
expect(await rules.in({ value: 'third' }, 'first', 'second', 'Third')).toBe(false)
})
it('fails comparing dissimilar objects', async () => {
expect(await rules.in({ value: {f: 'abc'} }, {a: 'cdf'}, {b: 'abc'})).toBe(false)
})
it('passes when case sensitive match is in stack', async () => {
expect(await rules.in({ value: 'third' }, 'first', 'second', 'third')).toBe(true)
})
it('passes a shallow array compare', async () => {
expect(await rules.in({ value: ['abc'] }, ['cdf'], ['abc'])).toBe(true)
})
it('passes a shallow object compare', async () => {
expect(await rules.in({ value: {f: 'abc'} }, {a: 'cdf'}, {f: 'abc'},)).toBe(true)
})
it('fails when not in stack', () => expectFail('third', 'first', 'second'))
it('fails when case sensitive mismatch is in stack', () => expectFail(
'third',
'first', 'second', 'Third'
))
it('fails comparing dissimilar objects', () => expectFail(
{ f: 'abc' },
{ a: 'cdf' }, { b: 'abc' }
))
it('passes when case sensitive match is in stack', () => expectPass(
'third',
'first', 'second', 'third'
))
it('passes a shallow array compare', () => expectPass(['abc'], ['cdf'], ['abc']))
it('passes a shallow object compare', () => expectPass(
{ f: 'abc' },
{ a: 'cdf' }, { f: 'abc' }
))
})
/**
* Matches rule
*/
describe('matches', () => {
it('simple strings fail if they arent equal', async () => {
expect(await rules.matches({ value: 'third' }, 'first')).toBe(false)
})
const validate = (value, ...stack) => rules.matches({ value, name: '', formValues: {} }, ...stack)
const expectPass = (value, ...stack) => expect(validate(value, ...stack)).toBe(true)
const expectFail = (value, ...stack) => expect(validate(value, ...stack)).toBe(false)
it('fails on non matching regex', async () => {
expect(await rules.matches({ value: 'third' }, /^thirds/)).toBe(false)
})
it('passes if simple strings match', async () => {
expect(await rules.matches({ value: 'second' }, 'third', 'second')).toBe(true)
})
it('passes on matching regex', async () => {
expect(await rules.matches({ value: 'third' }, /^third/)).toBe(true)
})
it('passes on matching mixed regex and string', async () => {
expect(await rules.matches({ value: 'first-fourth' }, 'second', /^third/, /fourth$/)).toBe(true)
})
it('fails on a regular expression encoded as a string', async () => {
expect(await rules.matches({ value: 'mypassword' }, '/[0-9]/')).toBe(false)
})
it('passes on a regular expression encoded as a string', async () => {
expect(await rules.matches({ value: 'mypa55word' }, '/[0-9]/')).toBe(true)
})
it('passes on a regular expression containing slashes', async () => {
expect(await rules.matches({ value: 'https://' }, '/https?:///')).toBe(true)
})
it('simple strings fail if they arent equal', () => expectFail('third', 'first'))
it('fails on non matching regex', () => expectFail('third', /^thirds/))
it('passes if simple strings match', () => expectPass('second', 'third', 'second'))
it('passes on matching regex', () => expectPass('third', /^third/))
it('passes on matching mixed regex and string', () => expectPass(
'first-fourth',
'second', /^third/, /fourth$/
))
it('fails on a regular expression encoded as a string', () => expectFail('mypassword', '/[0-9]/'))
it('passes on a regular expression encoded as a string', () => expectPass('mypa55word', '/[0-9]/'))
it('passes on a regular expression containing slashes', () => expectPass(
'https://',
'/https?:///'
))
})
/**
* Minimum.
*/
describe('min', () => {
it('passes when a number string', async () => expect(await rules.min({ value: '5' }, '5')).toBe(true))
it('passes when a number', async () => expect(await rules.min({ value: 6 }, 5)).toBe(true))
it('passes when a string length', async () => expect(await rules.min({ value: 'foobar' }, '6')).toBe(true))
it('passes when a array length', async () => expect(await rules.min({ value: Array(6) }, '6')).toBe(true))
it('passes when string is forced to value', async () => expect(await rules.min({ value: 'bcd' }, 'aaa', 'value')).toBe(true))
it('fails when string is forced to lesser value', async () => expect(await rules.min({ value: 'a' }, 'b', 'value')).toBe(false))
it('passes when a number is forced to length', async () => expect(await rules.min({ value: '000' }, 3, 'length')).toBe(true))
it('fails when a number is forced to length', async () => expect(await rules.min({ value: '44' }, 3, 'length')).toBe(false))
it('fails when a array length', async () => expect(await rules.min({ value: Array(6) }, '7')).toBe(false))
it('fails when a string length', async () => expect(await rules.min({ value: 'bar' }, 4)).toBe(false))
it('fails when a number', async () => expect(await rules.min({ value: 3 }, '7')).toBe(false))
})
/**
* Maximum.
*/
describe('max', () => {
it('passes when a number string', async () => expect(await rules.max({ value: '5' }, '5')).toBe(true))
const validate = (value, max, force = undefined) => rules.max({value, name: '', formValues: {}}, max, force)
const expectPass = (v, max, force = undefined) => expect(validate(v, max, force)).toBe(true)
const expectFail = (v, max, force = undefined) => expect(validate(v, max, force)).toBe(false)
it('passes when a number', async () => expect(await rules.max({ value: 5 }, 6)).toBe(true))
it('passes when a string length', async () => expect(await rules.max({ value: 'foobar' }, '6')).toBe(true))
it('passes when a array length', async () => expect(await rules.max({ value: Array(6) }, '6')).toBe(true))
it('passes when forced to validate on length', async () => expect(await rules.max({ value: 10 }, 3, 'length')).toBe(true))
it('passes when forced to validate string on value', async () => expect(await rules.max({ value: 'b' }, 'e', 'value')).toBe(true))
it('fails when a array length', async () => expect(await rules.max({ value: Array(6) }, '5')).toBe(false))
it('fails when a string length', async () => expect(await rules.max({ value: 'bar' }, 2)).toBe(false))
it('fails when a number', async () => expect(await rules.max({ value: 10 }, '7')).toBe(false))
it('fails when a number', async () => expect(await rules.max({ value: 10 }, '7')).toBe(false))
it('fails when forced to validate on length', async () => expect(await rules.max({ value: -10 }, '1', 'length')).toBe(false))
it('passes when a number string', () => expectPass('5', '5'))
it('passes when a number', () => expectPass(5, 6))
it('passes when a string length', () => expectPass('foobar', '6'))
it('passes when a array length', () => expectPass(Array(6), '6'))
it('passes when forced to validate on length', () => expectPass(10, 3, 'length'))
it('passes when forced to validate string on value', () => expectPass('b', 'e', 'value'))
it('fails when a array length', () => expectFail(Array(6), '5'))
it('fails when a string length', () => expectFail('bar', 2))
it('fails when a number', () => expectFail(10, '7'))
it('fails when forced to validate on length', () => expectFail(-10, '1', 'length'))
})
describe('min', () => {
const validate = (value, min, force = undefined) => rules.min({value, name: '', formValues: {}}, min, force)
const expectPass = (v, min, force = undefined) => expect(validate(v, min, force)).toBe(true)
const expectFail = (v, min, force = undefined) => expect(validate(v, min, force)).toBe(false)
it('passes when a number string', () => expectPass('5', '5'))
it('passes when a number', () => expectPass(6, 5))
it('passes when a string length', () => expectPass('foobar', '6'))
it('passes when a array length', () => expectPass(Array(6), '6'))
it('passes when string is forced to value', () => expectPass('bcd', 'aaa', 'value'))
it('fails when string is forced to lesser value', () => expectFail('a', 'b', 'value'))
it('passes when a number is forced to length', () => expectPass('000', 3, 'length'))
it('fails when a number is forced to length', () => expectFail('44', 3, 'length'))
it('fails when a array length', () => expectFail(Array(6), '7'))
it('fails when a string length', () => expectFail('bar', 4))
it('fails when a number', () => expectFail(3, '7'))
})
/**
* Maximum.
*/
describe('not', () => {
it('passes when a number string', async () => expect(await rules.not({ value: '5' }, '6')).toBe(true))
const validate = (value, ...stack) => rules.not({ value, name: '', formValues: {} }, ...stack)
const expectPass = (value, ...stack) => expect(validate(value, ...stack)).toBe(true)
const expectFail = (value, ...stack) => expect(validate(value, ...stack)).toBe(false)
it('passes when a number', async () => expect(await rules.not({ value: 1 }, 30)).toBe(true))
it('passes when a string', async () => expect(await rules.not({ value: 'abc' }, 'def')).toBe(true))
it('fails when a shallow equal array', async () => expect(await rules.not({ value: ['abc'] }, ['abc'])).toBe(false))
it('fails when a shallow equal object', async () => expect(await rules.not({ value: {a: 'abc'} }, ['123'], {a: 'abc'})).toBe(false))
it('fails when string is in stack', async () => expect(await rules.not({ value: 'a' }, 'b', 'c', 'd', 'a', 'f')).toBe(false))
it('passes when a number string', () => expectPass('5', '6'))
it('passes when a number', () => expectPass(1, 30))
it('passes when a string', () => expectPass('abc', 'def'))
it('fails when a shallow equal array', () => expectFail(['abc'], ['abc']))
it('fails when a shallow equal object', () => expectFail({a: 'abc'}, ['123'], {a: 'abc'}))
it('fails when string is in stack', () => expectFail('a', 'b', 'c', 'd', 'a', 'f'))
})
/**
* Checks if a date is after another date
*/
describe('number', () => {
it('passes with simple number string', async () => expect(await rules.number({ value: '123' })).toBe(true))
const validate = value => rules.number({ value, name: '', formValues: {} })
const expectPass = value => expect(validate(value)).toBe(true)
const expectFail = value => expect(validate(value)).toBe(false)
it('passes with simple number', async () => expect(await rules.number({ value: 19832461234 })).toBe(true))
it('passes with float', async () => expect(await rules.number({ value: 198.32464 })).toBe(true))
it('passes with decimal in string', async () => expect(await rules.number({ value: '567.23' })).toBe(true))
it('fails with comma in number string', async () => expect(await rules.number({ value: '123,456' })).toBe(false))
it('fails with alpha', async () => expect(await rules.number({ value: '123sdf' })).toBe(false))
it('passes with simple number string', () => expectPass('123'))
it('passes with simple number', () => expectPass(19832461234))
it('passes with float', () => expectPass(198.32464))
it('passes with decimal in string', () => expectPass('567.23'))
it('fails with comma in number string', () => expectFail('123,456'))
it('fails with alpha', () => expectFail('123sdf'))
})
/**
* Required rule
*/
describe('required', () => {
it('fails on empty string', async () => expect(await rules.required({ value: '' })).toBe(false))
const validate = (value, isRequired = true) => rules.required({ value, name: '', formValues: {} }, isRequired)
const expectPass = (value, isRequired = true) => expect(validate(value, isRequired)).toBe(true)
const expectFail = (value, isRequired = true) => expect(validate(value, isRequired)).toBe(false)
it('fails on empty array', async () => expect(await rules.required({ value: [] })).toBe(false))
it('fails on empty object', async () => expect(await rules.required({ value: {} })).toBe(false))
it('fails on null', async () => expect(await rules.required({ value: null })).toBe(false))
it('passes with the number zero', async () => expect(await rules.required({ value: 0 })).toBe(true))
it('passes with the boolean false', async () => expect(await rules.required({ value: false })).toBe(true))
it('passes with a non empty array', async () => expect(await rules.required({ value: ['123'] })).toBe(true))
it('passes with a non empty object', async () => expect(await rules.required({ value: {a: 'b'} })).toBe(true))
it('passes with empty value if second argument is false', async () => expect(await rules.required({ value: '' }, false)).toBe(true))
it('passes with empty value if second argument is false string', async () => expect(await rules.required({ value: '' }, 'false')).toBe(true))
it('fails on empty string', () => expectFail(''))
it('fails on empty array', () => expectFail([]))
it('fails on empty object', () => expectFail({}))
it('fails on null', () => expectFail(null))
it('passes with the number zero', () => expectPass(0))
it('passes with the boolean false', () => expectPass(false))
it('passes with a non empty array', () => expectPass(['123']))
it('passes with a non empty object', () => expectPass({ a: 'b' }))
it('passes with empty value if second argument is false', () => expectPass('', false))
it('passes with empty value if second argument is false string', () => {
expectPass('', 'false')
})
})
/**
* Checks if value starts with a one of the specified Strings.
*/
describe('startsWith', () => {
it('fails when value starting is not in stack of single value', async () => {
expect(await rules.startsWith({ value: 'taco tuesday' }, 'pizza')).toBe(false)
})
const validate = (value, ...args) => rules.startsWith({ value, name: '', formValues: {} }, ...args)
it('fails when value starting is not in stack of multiple values', async () => {
expect(await rules.startsWith({ value: 'taco tuesday' }, 'pizza', 'coffee')).toBe(false)
})
it('fails when value starting is not in stack of single value', () => {
expect(validate('taco tuesday', 'pizza')).toBe(false)
})
it('fails when passed value is not a string', async () => {
expect(await rules.startsWith({ value: 'taco tuesday'}, ['taco', 'pizza'])).toBe(false)
})
it('fails when value starting is not in stack of multiple values', () => {
expect(validate('taco tuesday', 'pizza', 'coffee')).toBe(false)
})
it('fails when passed value is not a string', async () => {
expect(await rules.startsWith({ value: 'taco tuesday'}, {value: 'taco'})).toBe(false)
})
it('fails when passed value is not a string', () => {
expect(validate('taco tuesday', ['taco', 'pizza'])).toBe(false)
})
it('passes when a string value is present and matched even if non-string values also exist as arguments', async () => {
expect(await rules.startsWith({ value: 'taco tuesday'}, {value: 'taco'}, ['taco'], 'taco')).toBe(true)
})
it('fails when passed value is not a string', () => {
expect(validate('taco tuesday', {value: 'taco'})).toBe(false)
})
it('passes when stack consists of zero values', async () => {
expect(await rules.startsWith({ value: 'taco tuesday' })).toBe(true)
})
it('passes when a string value is present and matched even if non-string values also exist as arguments', () => {
expect(validate('taco tuesday', {value: 'taco'}, ['taco'], 'taco')).toBe(true)
})
it('passes when value starting is in stack of single value', async () => {
expect(await rules.startsWith({ value: 'taco tuesday' }, 'taco')).toBe(true)
})
it('passes when stack consists of zero values', () => {
expect(validate('taco tuesday')).toBe(true)
})
it('passes when value starting is in stack of multiple values', async () => {
expect(await rules.startsWith({ value: 'taco tuesday' }, 'pizza', 'taco', 'coffee')).toBe(true)
})
it('passes when value starting is in stack of single value', () => {
expect(validate('taco tuesday', 'taco')).toBe(true)
})
it('passes when value starting is in stack of multiple values', () => {
expect(validate('taco tuesday', 'pizza', 'taco', 'coffee')).toBe(true)
})
})
/**
@ -477,7 +432,11 @@ describe('startsWith', () => {
* well tested: https://github.com/segmentio/is-url/blob/master/test/index.js
*/
describe('url', () => {
it('passes with http://google.com', async () => expect(await rules.url({ value: 'http://google.com' })).toBe(true))
const validate = value => rules.url({ value, name: '', formValues: {} })
const expectPass = value => expect(validate(value)).toBe(true)
const expectFail = value => expect(validate(value)).toBe(false)
it('fails with google.com', async () => expect(await rules.url({ value: 'google.com' })).toBe(false))
it('passes with http://google.com', () => expectPass('http://google.com'))
it('passes with http://scholar.google.com', () => expectPass('http://scholar.google.com'))
it('fails with google.com', () => expectFail('google.com'))
})