git.psu.edu will be upgraded to critical security release 13.7.4 Monday, 11/18 between 9 and 10pm. Please email support@git.psu.edu if you have trouble with anything Gitlab-related. Please see the git.psu.edu Yammer group for more information.

Commit 693347a2 authored by Ryan Diehl's avatar Ryan Diehl

feat(forms): port validators from @psu/form-lib

parent 322e571e
Pipeline #100120 passed with stages
in 4 minutes and 36 seconds
export * from './lib/disable-control/disable-control.directive';
export * from './lib/disable-control/disable-control.module';
export * from './lib/utils/form.utils';
export { PsuValidators } from './lib/validators/psu.validators';
import { emailValidator } from './email.validator';
import { FormControl } from '@angular/forms';
function getControl(value: string): FormControl {
const c = new FormControl();
c.patchValue(value);
return c;
}
describe('emailValidator', () => {
it('empty string should be valid', () => {
expect(emailValidator(getControl(''))).toBeNull();
});
it('valid email should be valid', () => {
expect(emailValidator(getControl('irock2@gmail.com'))).toBeNull();
});
it('invalid email with special characters ending should be invalid', () => {
expect(emailValidator(getControl('irock2@gmail.co!'))).toEqual({
validateEmail: {
valid: false
},
email: true
});
});
it('invalid email should have error', () => {
expect(emailValidator(getControl('invalid'))).toEqual({
validateEmail: {
valid: false
},
email: true
});
});
});
import { ValidationErrors, AbstractControl } from '@angular/forms';
// tslint:disable-next-line:max-line-length
const RE: RegExp = new RegExp(
"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$"
);
/**
* Validates an email address using the same regular expression the server uses.
*
* @param email the form control to validate
*/
export function emailValidator(email: AbstractControl): ValidationErrors {
const value = email.value;
if (!value) {
return null;
}
return RE.test(email.value)
? null
: {
email: true,
validateEmail: {
valid: false
}
};
}
import { FormControl } from '@angular/forms';
import { minLengthValidator, maxLengthValidator } from './length.validators';
describe('length validators', () => {
function getControl(value: any): FormControl {
const c = new FormControl();
c.patchValue(value);
return c;
}
describe('minLength', () => {
it('should be valid when empty', () => {
expect(minLengthValidator(3)(getControl(''))).toBeNull();
});
it('should be valid when at length', () => {
expect(minLengthValidator(3)(getControl('abc'))).toBeNull();
});
it('should be invalid when less than length', () => {
expect(minLengthValidator(2)(getControl('a'))).toEqual({
minLength: {
requiredLength: 2,
actualLength: 1
}
});
});
it('should be invalid when less than length when trimmed', () => {
expect(minLengthValidator(3)(getControl(' a '))).toEqual({
minLength: {
requiredLength: 3,
actualLength: 1
}
});
});
});
describe('maxLength', () => {
it('should be valid when empty', () => {
expect(maxLengthValidator(3)(getControl(''))).toBeNull();
});
it('should be valid when at length', () => {
expect(maxLengthValidator(3)(getControl('abc'))).toBeNull();
});
it('should be invalid when greater than length', () => {
expect(maxLengthValidator(2)(getControl('abc'))).toEqual({
maxLength: {
requiredLength: 2,
actualLength: 3
}
});
});
it('should be invalid when greater than length when trimmed', () => {
expect(maxLengthValidator(3)(getControl(' abcd '))).toEqual({
maxLength: {
requiredLength: 3,
actualLength: 4
}
});
});
it('should be valid when at length when trimmed', () => {
expect(maxLengthValidator(3)(getControl(' abc '))).toBeNull();
});
});
});
import { ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
function isEmptyValue(value: any): boolean {
return value === undefined || value === null || (typeof value === 'string' && value.trim().length === 0);
}
export function minLengthValidator(minLength: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (isEmptyValue(control.value)) {
return null;
}
let length;
if (control.value) {
if (typeof control.value === 'string') {
length = control.value.trim().length;
} else {
length = control.value.length;
}
} else {
length = 0;
}
return length < minLength
? {
minLength: {
requiredLength: minLength,
actualLength: length
}
}
: null;
};
}
export function maxLengthValidator(maxLength: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (isEmptyValue(control.value)) {
return null;
}
let length;
if (control.value) {
if (typeof control.value === 'string') {
length = control.value.trim().length;
} else {
length = control.value.length;
}
} else {
length = 0;
}
return length > maxLength
? {
maxLength: {
requiredLength: maxLength,
actualLength: length
}
}
: null;
};
}
import { FormControl } from '@angular/forms';
import { nonPsuEmailValidator } from './non-psu-email.validator';
describe('nonPsuEmailValidator', () => {
function getValue(val: string): FormControl {
const ret = new FormControl();
ret.patchValue(val);
return ret;
}
it('should fail email address @psu.edu', () => {
expect(nonPsuEmailValidator(getValue('someone@psu.edu'))).toEqual({
psuEmail: true
});
});
it('should pass email address from a subdomain under psu.edu', () => {
expect(nonPsuEmailValidator(getValue('something@somewhere.psu.edu'))).toBeNull();
});
it('should pass email from another tld', () => {
expect(nonPsuEmailValidator(getValue('someone@google.com'))).toBeNull();
});
it('should pass email from another tricky tld', () => {
expect(nonPsuEmailValidator(getValue('someone@psu.edu.com'))).toBeNull();
});
});
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function nonPsuEmailValidator(email: AbstractControl): ValidationErrors {
const regexp = /^.+@psu.edu$/i;
return regexp.test(email.value) ? { psuEmail: true } : null;
}
import { notAnEmailValidator } from './not-email.validator';
import { FormControl } from '@angular/forms';
describe('notAnEmailValidator', () => {
function getValue(val: string): FormControl {
const ret = new FormControl();
ret.patchValue(val);
return ret;
}
it('when value contains an at sign only', () => {
expect(notAnEmailValidator(getValue('@m'))).toEqual({ notAnEmail: true });
});
it('when value contains an at sign at the beginning', () => {
expect(notAnEmailValidator(getValue('@me'))).toEqual({ notAnEmail: true });
});
it('when value contains an at sign at the end', () => {
expect(notAnEmailValidator(getValue('me@'))).toEqual({ notAnEmail: true });
});
it('when value contains an at sign in the middle', () => {
expect(notAnEmailValidator(getValue('you@me'))).toEqual({
notAnEmail: true
});
});
it('when value does not contain an at sign', () => {
expect(notAnEmailValidator(getValue('rpd123'))).toBeNull();
});
it('when value is empty', () => {
expect(notAnEmailValidator(getValue(''))).toBeNull();
});
});
import { ValidationErrors, AbstractControl } from '@angular/forms';
export function notAnEmailValidator(control: AbstractControl): ValidationErrors {
return /@/.test(control.value)
? {
notAnEmail: true
}
: null;
}
import { FormControl } from '@angular/forms';
import { office365EmailValidator } from './office365-email.validator';
describe('psuEmailValidator', () => {
function getControl(value: string): FormControl {
const c = new FormControl();
c.patchValue(value);
return c;
}
it('empty string should be valid', () => {
expect(office365EmailValidator(getControl(''))).toBeNull();
});
it('non-pennstateoffice365.onmicrosoft.com email should be valid', () => {
expect(office365EmailValidator(getControl('irock@gmail.com'))).toBeNull();
});
it('@pennstateoffice365.onmicrosoft.com email should be invalid', () => {
expect(office365EmailValidator(getControl('me@pennstateoffice365.onmicrosoft.com'))).toEqual({
psuEmail: true
});
});
it('@pennstateoffice365.onmicrosoft.com email with uppercase should be invalid', () => {
expect(office365EmailValidator(getControl('me@pennstateOFFice365.onmicROsoft.com'))).toEqual({
psuEmail: true
});
});
});
import { ValidationErrors, AbstractControl } from '@angular/forms';
/*
Validates that the supplied email is not an office 365 extension.
*/
export function office365EmailValidator(email: AbstractControl): ValidationErrors {
return /^.+@pennstateoffice365.onmicrosoft.com$/i.test(email.value)
? {
psuEmail: true
}
: null;
}
import { FormControl } from '@angular/forms';
import { psuEmailValidator, psuStrictEmailValidator } from './psu-email.validator';
function getControl(value: string): FormControl {
const c = new FormControl();
c.patchValue(value);
return c;
}
describe('psuEmailValidator', () => {
it('empty string should be valid', () => {
expect(psuEmailValidator(getControl(''))).toBeNull();
});
it('non-psu email should be valid', () => {
expect(psuEmailValidator(getControl('irock@gmail.com'))).toBeNull();
});
it('@psu.edu email should be invalid', () => {
expect(psuEmailValidator(getControl('me@psu.edu'))).toEqual({
psuEmail: true
});
});
it('@psu.edu email with uppercase should be invalid', () => {
expect(psuEmailValidator(getControl('me@PSU.EDU'))).toEqual({
psuEmail: true
});
});
it('@engr.psu.edu email should be invalid', () => {
expect(psuEmailValidator(getControl('me@engr.psu.edu'))).toEqual({
psuEmail: true
});
});
it('@engr.psu.edu email with uppercase should be invalid', () => {
expect(psuEmailValidator(getControl('me@ENGR.pSu.eDu'))).toEqual({
psuEmail: true
});
});
it('@garbage.psu.edu.something.else.domain email should be valid', () => {
expect(psuEmailValidator(getControl('me@garbage.psu.edu.something.else.domain'))).toBeNull();
});
it('@garbage.psu.edu.something.else.domain email when upper should be valid', () => {
expect(psuEmailValidator(getControl('me@garbage.PSU.edu.something.ELSE.domain'))).toBeNull();
});
it('@alumni.psu.edu email should be valid', () => {
expect(psuEmailValidator(getControl('me@alumni.psu.edu'))).toBeNull();
});
it('@alumni.psu.edu email when uppercase should be valid', () => {
expect(psuEmailValidator(getControl('ME@ALUMNI.PSU.EDU'))).toBeNull();
});
it('@arl.psu.edu email should be valid', () => {
expect(psuEmailValidator(getControl('me@arl.psu.edu'))).toBeNull();
});
it('@arl.psu.edu email when uppercase should be valid', () => {
expect(psuEmailValidator(getControl('me@ARL.PSU.EDU'))).toBeNull();
});
it('@pennstatehealth.psu.edu email should be valid', () => {
expect(psuEmailValidator(getControl('me@pennstatehealth.psu.edu'))).toBeNull();
});
it('@pennstatehealth.psu.edu email when uppercase should be valid', () => {
expect(psuEmailValidator(getControl('me@pennstaTehealth.psU.edu'))).toBeNull();
});
it('@phs.psu.edu email should be valid', () => {
expect(psuEmailValidator(getControl('me@phs.psu.edu'))).toBeNull();
});
it('@phs.psu.edu email when uppercase should be valid', () => {
expect(psuEmailValidator(getControl('me@Phs.Psu.edu'))).toBeNull();
});
});
describe('psuStrictEmailValidator', () => {
it('empty string should be valid', () => {
expect(psuStrictEmailValidator(getControl(''))).toBeNull();
});
it('non-psu email should be valid', () => {
expect(psuStrictEmailValidator(getControl('irock@gmail.com'))).toBeNull();
});
it('@psu.edu email should be invalid', () => {
expect(psuStrictEmailValidator(getControl('me@psu.edu'))).toEqual({
psuEmail: true
});
});
it('@psu.edu email when uppercase should be invalid', () => {
expect(psuStrictEmailValidator(getControl('ME@PSU.EDU'))).toEqual({
psuEmail: true
});
});
it('@engr.psu.edu email should be valid', () => {
expect(psuStrictEmailValidator(getControl('me@engr.psu.edu'))).toBeNull();
});
it('@engr.psu.edu email when uppercase should be valid', () => {
expect(psuStrictEmailValidator(getControl('me@engr.PSU.edu'))).toBeNull();
});
});
import { ValidationErrors, AbstractControl } from '@angular/forms';
/*
Two regex patterns in the email validator below (ands are ?=)
- check from the beginning of the email until after the @ (and some amount of characters) to see if it ends in .psu.edu
- and from that subgroup (?:) ignore (?!) emails that end in arl.psu.edu, alumni.psu.edu, etc.
- return true if it matches (meaning the email shouldn't be allowed)
- return null if it does not match (meaning the email should be allowed).
*/
export function psuEmailValidator(email: AbstractControl): ValidationErrors {
return _validatePsuEmail(email, /(?=(^.+@.*psu.edu$))(?=^(?:(?!(arl|pennstatehealth|alumni|phs)(.psu.edu)).)*$).*$/i);
}
export function psuStrictEmailValidator(email: AbstractControl): ValidationErrors {
return _validatePsuEmail(email, /^.+@psu.edu$/i);
}
function _validatePsuEmail(email: AbstractControl, exp: RegExp): ValidationErrors {
return exp.test(email.value)
? {
psuEmail: true
}
: null;
}
import { PsuValidators } from './psu.validators';
import { FormControl } from '@angular/forms';
describe('PsuValidators', () => {
function getControl(value: any): FormControl {
const c = new FormControl();
c.patchValue(value);
return c;
}
it('required', () => {
expect(PsuValidators.required(getControl(''))).toEqual({ required: true });
});
it('minLength', () => {
expect(PsuValidators.minLength(3)(getControl('a'))).toEqual({
minLength: {
requiredLength: 3,
actualLength: 1
}
});
});
it('maxLength', () => {
expect(PsuValidators.maxLength(3)(getControl('abcd'))).toEqual({
maxLength: {
requiredLength: 3,
actualLength: 4
}
});
});
it('email', () => {
expect(PsuValidators.email(getControl('test'))).toEqual({
validateEmail: { valid: false },
email: true
});
});
it('psuEmail', () => {
expect(PsuValidators.psuEmail(getControl('me@psu.edu'))).toEqual({
psuEmail: true
});
});
it('psuEmailStrict', () => {
expect(PsuValidators.psuEmailStrict(getControl('me@psu.edu'))).toEqual({
psuEmail: true
});
});
it('office365Email', () => {
expect(PsuValidators.office365Email(getControl('me@pennstateoffice365.onmicrosoft.com'))).toEqual({
psuEmail: true
});
});
it('ssn', () => {
expect(PsuValidators.ssn(getControl('me'))).toEqual({ ssnCheck: true });
});
it('not an email', () => {
expect(PsuValidators.notAnEmail(getControl('me@psu.edu'))).toEqual({
notAnEmail: true
});
});
});
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import { emailValidator } from './email.validator';
import { maxLengthValidator, minLengthValidator } from './length.validators';
import { nonPsuEmailValidator } from './non-psu-email.validator';
import { notAnEmailValidator } from './not-email.validator';
import { office365EmailValidator } from './office365-email.validator';
import { psuEmailValidator, psuStrictEmailValidator } from './psu-email.validator';
import { requiredValidator } from './required.validator';
import { ssnPatternValidator } from './ssn-pattern.validator';
import { ssnValidator } from './ssn.validator';
export class PsuValidators {
public static required(control: AbstractControl): ValidationErrors | null {
return requiredValidator(control);
}
public static minLength(minLength: number): ValidatorFn {
return minLengthValidator(minLength);
}
public static maxLength(maxLength: number): ValidatorFn {
return maxLengthValidator(maxLength);
}
public static ssn(control: AbstractControl): ValidationErrors | null {
return ssnValidator(control);
}
public static ssnPattern(control: AbstractControl): ValidationErrors | null {
return ssnPatternValidator(control);
}
public static office365Email(control: AbstractControl): ValidationErrors | null {
return office365EmailValidator(control);
}
public static email(control: AbstractControl): ValidationErrors | null {
return emailValidator(control);
}
public static psuEmail(control: AbstractControl): ValidationErrors | null {
return psuEmailValidator(control);
}
public static psuEmailStrict(control: AbstractControl): ValidationErrors | null {
return psuStrictEmailValidator(control);
}
public static notAnEmail(control: AbstractControl): ValidationErrors | null {
return notAnEmailValidator(control);
}
public static nonPsuEmail(control: AbstractControl): ValidationErrors | null {
return nonPsuEmailValidator(control);
}