mirror of https://github.com/abpframework/abp.git
committed by
GitHub
9 changed files with 380 additions and 10 deletions
@ -0,0 +1,191 @@ |
|||
import { FormControl, Validators } from '@angular/forms'; |
|||
import { AbpValidators, validateRange } from '../validators'; |
|||
import { validateCreditCard } from '../validators/credit-card.validator'; |
|||
import { validateRequired } from '../validators/required.validator'; |
|||
import { validateStringLength } from '../validators/string-length.validator'; |
|||
import { validateUrl } from '../validators/url.validator'; |
|||
|
|||
describe('Validators', () => { |
|||
describe('Credit Card Validator', () => { |
|||
const error = { creditCard: true }; |
|||
|
|||
test.each` |
|||
input | expected |
|||
${undefined} | ${null} |
|||
${null} | ${null} |
|||
${''} | ${null} |
|||
${'0'} | ${error} |
|||
${'5105105105105100' /* Mastercard */} | ${null} |
|||
${'5105105105105101' /* Mastercard */} | ${error} |
|||
${'5105 1051 0510 5100'} | ${null} |
|||
${'5105-1051-0510-5100'} | ${null} |
|||
${'5105 - 1051 - 0510 - 5100'} | ${null} |
|||
${'4111111111111111' /*Visa*/} | ${null} |
|||
${'4111111111111112' /*Visa*/} | ${error} |
|||
${'4012888888881881' /* Visa */} | ${null} |
|||
${'4012888888881882' /* Visa */} | ${error} |
|||
${'4222222222222' /* Visa */} | ${null} |
|||
${'4222222222223' /* Visa */} | ${error} |
|||
${'378282246310005' /* American Express */} | ${null} |
|||
${'378282246310006' /* American Express */} | ${error} |
|||
${'6011111111111117' /* Discover */} | ${null} |
|||
${'6011111111111118' /* Discover */} | ${error} |
|||
`('should return $expected when input is $input', ({ input, expected }) => {
|
|||
const control = new FormControl(input, [validateCreditCard()]); |
|||
control.markAsDirty({ onlySelf: true }); |
|||
control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); |
|||
|
|||
expect(control.errors).toEqual(expected); |
|||
}); |
|||
|
|||
it('should return null when control is pristine', () => { |
|||
const invalidNumber = '5105105105105101'; |
|||
const control = new FormControl(invalidNumber, [validateCreditCard()]); |
|||
// control is not dirty
|
|||
|
|||
expect(control.valid).toBe(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('Email Validator', () => { |
|||
it('should return email validator of Angular', () => { |
|||
expect(AbpValidators.emailAddress()).toBe(Validators.email); |
|||
}); |
|||
}); |
|||
|
|||
describe('Range Validator', () => { |
|||
test.each` |
|||
input | options | expected |
|||
${null} | ${undefined} | ${null} |
|||
${undefined} | ${undefined} | ${null} |
|||
${''} | ${undefined} | ${null} |
|||
${0} | ${undefined} | ${null} |
|||
${Infinity} | ${undefined} | ${null} |
|||
${'-1'} | ${{ minimum: 0 }} | ${{ range: { min: 0, max: Infinity } }} |
|||
${-1} | ${{ minimum: 0 }} | ${{ range: { min: 0, max: Infinity } }} |
|||
${2} | ${{ minimum: 3, maximum: 5 }} | ${{ range: { min: 3, max: 5 } }} |
|||
${3} | ${{ minimum: 3, maximum: 5 }} | ${null} |
|||
${5} | ${{ minimum: 3, maximum: 5 }} | ${null} |
|||
${6} | ${{ minimum: 3, maximum: 5 }} | ${{ range: { min: 3, max: 5 } }} |
|||
`(
|
|||
'should return $expected when input is $input and options are $options', |
|||
({ input, options, expected }) => { |
|||
const control = new FormControl(input, [validateRange(options)]); |
|||
control.markAsDirty({ onlySelf: true }); |
|||
control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); |
|||
|
|||
expect(control.errors).toEqual(expected); |
|||
}, |
|||
); |
|||
|
|||
it('should return null when control is pristine', () => { |
|||
const invalidUrl = ''; |
|||
const control = new FormControl(invalidUrl, [validateRange({ minimum: 3 })]); |
|||
// control is not dirty
|
|||
|
|||
expect(control.valid).toBe(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('Required Validator', () => { |
|||
const error = { required: true }; |
|||
|
|||
test.each` |
|||
input | options | expected |
|||
${0} | ${undefined} | ${null} |
|||
${false} | ${undefined} | ${null} |
|||
${null} | ${undefined} | ${error} |
|||
${undefined} | ${undefined} | ${error} |
|||
${''} | ${undefined} | ${error} |
|||
${''} | ${{}} | ${error} |
|||
${''} | ${{ allowEmptyStrings: false }} | ${error} |
|||
${''} | ${{ allowEmptyStrings: true }} | ${null} |
|||
`(
|
|||
'should return $expected when input is $input and options are $options', |
|||
({ input, options, expected }) => { |
|||
const control = new FormControl(input, [validateRequired(options)]); |
|||
control.markAsDirty({ onlySelf: true }); |
|||
control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); |
|||
|
|||
expect(control.errors).toEqual(expected); |
|||
}, |
|||
); |
|||
|
|||
it('should return null when control is pristine', () => { |
|||
const invalidUrl = ''; |
|||
const control = new FormControl(invalidUrl, [validateRequired()]); |
|||
// control is not dirty
|
|||
|
|||
expect(control.valid).toBe(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('String Length Validator', () => { |
|||
test.each` |
|||
input | options | expected |
|||
${null} | ${undefined} | ${null} |
|||
${undefined} | ${undefined} | ${null} |
|||
${''} | ${undefined} | ${null} |
|||
${'ab'} | ${{ minimumLength: 3 }} | ${{ minlength: { requiredLength: 3 } }} |
|||
${'abp'} | ${{ minimumLength: 3 }} | ${null} |
|||
${'abp'} | ${{ maximumLength: 2 }} | ${{ maxlength: { requiredLength: 2 } }} |
|||
${'abp'} | ${{ maximumLength: 3 }} | ${null} |
|||
`(
|
|||
'should return $expected when input is $input and options are $options', |
|||
({ input, options, expected }) => { |
|||
const control = new FormControl(input, [validateStringLength(options)]); |
|||
control.markAsDirty({ onlySelf: true }); |
|||
control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); |
|||
|
|||
expect(control.errors).toEqual(expected); |
|||
}, |
|||
); |
|||
|
|||
it('should return null when control is pristine', () => { |
|||
const invalidUrl = ''; |
|||
const control = new FormControl(invalidUrl, [validateStringLength({ minimumLength: 3 })]); |
|||
// control is not dirty
|
|||
|
|||
expect(control.valid).toBe(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('Url Validator', () => { |
|||
const error = { url: true }; |
|||
|
|||
test.each` |
|||
input | expected |
|||
${undefined} | ${null} |
|||
${null} | ${null} |
|||
${''} | ${null} |
|||
${'http://x'} | ${null} |
|||
${'http:///x'} | ${error} |
|||
${'https://x'} | ${null} |
|||
${'https:///x'} | ${error} |
|||
${'ftp://x'} | ${null} |
|||
${'ftp:///x'} | ${error} |
|||
${'http://x.com'} | ${null} |
|||
${'http://x.photography'} | ${null} |
|||
${'http://www.x.org'} | ${null} |
|||
${'http://sub.x.gov.tr'} | ${null} |
|||
${'x'} | ${error} |
|||
${'x.com'} | ${error} |
|||
${'www.x.org'} | ${error} |
|||
${'sub.x.gov.tr'} | ${error} |
|||
`('should return $expected when input is $input', ({ input, expected }) => {
|
|||
const control = new FormControl(input, [validateUrl()]); |
|||
control.markAsDirty({ onlySelf: true }); |
|||
control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); |
|||
|
|||
expect(control.errors).toEqual(expected); |
|||
}); |
|||
|
|||
it('should return null when control is pristine', () => { |
|||
const invalidUrl = 'x'; |
|||
const control = new FormControl(invalidUrl, [validateUrl()]); |
|||
// control is not dirty
|
|||
|
|||
expect(control.valid).toBe(true); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,34 @@ |
|||
import { AbstractControl, ValidatorFn } from '@angular/forms'; |
|||
|
|||
export interface CreditCardError { |
|||
creditCard: true; |
|||
} |
|||
|
|||
export function validateCreditCard(): ValidatorFn { |
|||
return (control: AbstractControl): CreditCardError | null => { |
|||
if (control.pristine) return null; |
|||
|
|||
if (['', null, undefined].indexOf(control.value) > -1) return null; |
|||
|
|||
return isValidCreditCard(String(control.value)) ? null : { creditCard: true }; |
|||
}; |
|||
} |
|||
|
|||
function isValidCreditCard(value: string): boolean { |
|||
value = value.replace(/[ -]/g, ''); |
|||
|
|||
if (!/^[0-9]{13,19}$/.test(value)) return false; |
|||
|
|||
let checksum = 0; |
|||
let multiplier = 1; |
|||
|
|||
for (let i = value.length; i > 0; i--) { |
|||
const digit = Number(value[i - 1]) * multiplier; |
|||
/* tslint:disable-next-line:no-bitwise */ |
|||
checksum += (digit % 10) + ~~(digit / 10); |
|||
|
|||
multiplier = (multiplier * 2) % 3; |
|||
} |
|||
|
|||
return checksum % 10 === 0; |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
import { Validators } from '@angular/forms'; |
|||
import { validateCreditCard } from './credit-card.validator'; |
|||
import { validateRange } from './range.validator'; |
|||
import { validateRequired } from './required.validator'; |
|||
import { validateStringLength } from './string-length.validator'; |
|||
import { validateUrl } from './url.validator'; |
|||
export * from './credit-card.validator'; |
|||
export * from './range.validator'; |
|||
export * from './required.validator'; |
|||
export * from './string-length.validator'; |
|||
export * from './url.validator'; |
|||
|
|||
export const AbpValidators = { |
|||
creditCard: validateCreditCard, |
|||
emailAddress: () => Validators.email, |
|||
range: validateRange, |
|||
required: validateRequired, |
|||
stringLength: validateStringLength, |
|||
url: validateUrl, |
|||
}; |
|||
@ -0,0 +1,32 @@ |
|||
import { AbstractControl, ValidatorFn } from '@angular/forms'; |
|||
|
|||
export interface RangeError { |
|||
range: { |
|||
max: number; |
|||
min: number; |
|||
}; |
|||
} |
|||
|
|||
export interface RangeOptions { |
|||
maximum?: number; |
|||
minimum?: number; |
|||
} |
|||
|
|||
export function validateRange({ maximum = Infinity, minimum = 0 }: RangeOptions = {}): ValidatorFn { |
|||
return (control: AbstractControl): RangeError | null => { |
|||
if (control.pristine) return null; |
|||
|
|||
if (['', null, undefined].indexOf(control.value) > -1) return null; |
|||
|
|||
const value = Number(control.value); |
|||
return getMinError(value, minimum, maximum) || getMaxError(value, maximum, minimum); |
|||
}; |
|||
} |
|||
|
|||
function getMaxError(value: number, max: number, min: number): RangeError { |
|||
return value > max ? { range: { max, min } } : null; |
|||
} |
|||
|
|||
function getMinError(value: number, min: number, max: number): RangeError { |
|||
return value < min ? { range: { min, max } } : null; |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
import { AbstractControl, ValidatorFn } from '@angular/forms'; |
|||
|
|||
export interface RequiredError { |
|||
required: true; |
|||
} |
|||
|
|||
export interface RequiredOptions { |
|||
allowEmptyStrings?: boolean; |
|||
} |
|||
|
|||
export function validateRequired({ allowEmptyStrings }: RequiredOptions = {}): ValidatorFn { |
|||
return (control: AbstractControl): RequiredError | null => { |
|||
return control.pristine || isValidRequired(control.value, allowEmptyStrings) |
|||
? null |
|||
: { required: true }; |
|||
}; |
|||
} |
|||
|
|||
function isValidRequired(value: any, allowEmptyStrings: boolean): boolean { |
|||
if (value || value === 0 || value === false) return true; |
|||
|
|||
if (allowEmptyStrings && value === '') return true; |
|||
|
|||
return false; |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
import { AbstractControl, ValidatorFn } from '@angular/forms'; |
|||
|
|||
export interface StringLengthError { |
|||
maxlength?: { |
|||
requiredLength: number; |
|||
}; |
|||
minlength?: { |
|||
requiredLength: number; |
|||
}; |
|||
} |
|||
|
|||
export interface StringLengthOptions { |
|||
maximumLength?: number; |
|||
minimumLength?: number; |
|||
} |
|||
|
|||
export function validateStringLength({ |
|||
maximumLength = Infinity, |
|||
minimumLength = 0, |
|||
}: StringLengthOptions = {}): ValidatorFn { |
|||
return (control: AbstractControl): StringLengthError | null => { |
|||
if (control.pristine) return null; |
|||
|
|||
if (['', null, undefined].indexOf(control.value) > -1) return null; |
|||
|
|||
const value = String(control.value); |
|||
|
|||
return getMinLengthError(value, minimumLength) || getMaxLengthError(value, maximumLength); |
|||
}; |
|||
} |
|||
|
|||
function getMaxLengthError(value: string, requiredLength: number): StringLengthError { |
|||
return value.length > requiredLength ? { maxlength: { requiredLength } } : null; |
|||
} |
|||
|
|||
function getMinLengthError(value: string, requiredLength: number): StringLengthError { |
|||
return value.length < requiredLength ? { minlength: { requiredLength } } : null; |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
import { AbstractControl, ValidatorFn } from '@angular/forms'; |
|||
|
|||
export interface UrlError { |
|||
url: true; |
|||
} |
|||
|
|||
export function validateUrl(): ValidatorFn { |
|||
return (control: AbstractControl): UrlError | null => { |
|||
if (control.pristine) return null; |
|||
|
|||
if (['', null, undefined].indexOf(control.value) > -1) return null; |
|||
|
|||
return isValidUrl(control.value) ? null : { url: true }; |
|||
}; |
|||
} |
|||
|
|||
function isValidUrl(value: string): boolean { |
|||
if (/^http(s)?:\/\/[^/]/.test(value) || /^ftp:\/\/[^/]/.test(value)) { |
|||
const a = document.createElement('a'); |
|||
a.href = value; |
|||
return !!a.host; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
Loading…
Reference in new issue