refactor(forms): refactor common validators used in unit tests (#38020)
A util file is added to forms test package: - it exposes simpleAsyncValidator, asyncValidator and asyncValidatorReturningObservable validators - it refactors simpleAsyncValidator and asyncValidator to use common promise creation code - it exposes currentStateOf allowing to get the validation state of a list of AbstractControl Closes #37831 PR Close #38020
This commit is contained in:
parent
8df888dfb4
commit
062b8d90af
|
@ -12,6 +12,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||
import {AbstractControl, CheckboxControlValueAccessor, ControlValueAccessor, DefaultValueAccessor, FormArray, FormArrayName, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, NgControl, NgForm, NgModel, NgModelGroup, SelectControlValueAccessor, SelectMultipleControlValueAccessor, ValidationErrors, Validator, Validators} from '@angular/forms';
|
||||
import {composeValidators, selectValueAccessor} from '@angular/forms/src/directives/shared';
|
||||
import {SpyNgControl, SpyValueAccessor} from './spies';
|
||||
import {asyncValidator} from './util';
|
||||
|
||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||
writtenValue: any;
|
||||
|
@ -30,24 +31,6 @@ class CustomValidatorDirective implements Validator {
|
|||
}
|
||||
}
|
||||
|
||||
function asyncValidator(expected: any, timeout = 0) {
|
||||
return (c: AbstractControl): any => {
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const res = c.value != expected ? {'async': true} : null;
|
||||
if (timeout == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, timeout);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
describe('Form Directives', () => {
|
||||
let defaultAccessor: DefaultValueAccessor;
|
||||
|
|
|
@ -11,29 +11,9 @@ import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/cor
|
|||
import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms';
|
||||
import {Validators} from '@angular/forms/src/validators';
|
||||
import {of} from 'rxjs';
|
||||
import {asyncValidator} from './util';
|
||||
|
||||
(function() {
|
||||
function asyncValidator(expected: string, timeouts = {}) {
|
||||
return (c: AbstractControl) => {
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise<ValidationErrors|null>(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0;
|
||||
const res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, t);
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
describe('FormArray', () => {
|
||||
describe('adding/removing', () => {
|
||||
let a: FormArray;
|
||||
|
|
|
@ -6,43 +6,14 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
import {fakeAsync, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {AbstractControl, AsyncValidatorFn, FormControl, FormGroup, ValidationErrors, Validators} from '@angular/forms';
|
||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
||||
|
||||
import {FormArray} from '@angular/forms/src/model';
|
||||
import {asyncValidator, asyncValidatorReturningObservable} from './util';
|
||||
|
||||
(function() {
|
||||
function asyncValidator(expected: string, timeouts = {}): AsyncValidatorFn {
|
||||
return (c: AbstractControl) => {
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise<ValidationErrors|null>(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0;
|
||||
const res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, t);
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
function asyncValidatorReturningObservable(c: AbstractControl) {
|
||||
const e = new EventEmitter<Record<string, boolean>>();
|
||||
Promise.resolve(null).then(() => {
|
||||
e.emit({'async': true});
|
||||
});
|
||||
return e;
|
||||
}
|
||||
|
||||
function otherAsyncValidator() {
|
||||
return Promise.resolve({'other': true});
|
||||
}
|
||||
|
|
|
@ -6,80 +6,19 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
import {async, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators} from '@angular/forms';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
import {asyncValidator, asyncValidatorReturningObservable, currentStateOf, simpleAsyncValidator} from './util';
|
||||
|
||||
|
||||
(function() {
|
||||
function simpleValidator(c: AbstractControl): ValidationErrors|null {
|
||||
return c.get('one')!.value === 'correct' ? null : {'broken': true};
|
||||
}
|
||||
|
||||
function asyncValidator(expected: string, timeouts = {}) {
|
||||
return (c: AbstractControl) => {
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise<ValidationErrors|null>(res => {
|
||||
resolve = res;
|
||||
});
|
||||
const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0;
|
||||
const res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, t);
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
function simpleAsyncValidator({
|
||||
timeout = 0,
|
||||
shouldFail,
|
||||
customError =
|
||||
{
|
||||
async: true
|
||||
}
|
||||
}: {timeout?: number, shouldFail: boolean, customError?: any}) {
|
||||
return (c: AbstractControl) => {
|
||||
const res = shouldFail ? customError : null;
|
||||
|
||||
if (timeout === 0) {
|
||||
return of(res);
|
||||
}
|
||||
|
||||
let resolve: (result: any) => void = undefined!;
|
||||
const promise = new Promise<ValidationErrors|null>(res => {
|
||||
resolve = res;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(res);
|
||||
}, timeout);
|
||||
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
function currentStateOf(controls: AbstractControl[]):
|
||||
{errors: any; pending: boolean; status: string;}[] {
|
||||
return controls.map(c => ({errors: c.errors, pending: c.pending, status: c.status}));
|
||||
}
|
||||
|
||||
function asyncValidatorReturningObservable(c: AbstractControl) {
|
||||
const e = new EventEmitter();
|
||||
Promise.resolve(null).then(() => {
|
||||
e.emit({'async': true});
|
||||
});
|
||||
return e;
|
||||
}
|
||||
|
||||
function otherObservableValidator() {
|
||||
return of({'other': true});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
import {AbstractControl, AsyncValidatorFn, ValidationErrors} from '@angular/forms';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
function createValidationPromise(
|
||||
result: ValidationErrors|null, timeout: number): Promise<ValidationErrors|null> {
|
||||
return new Promise(resolve => {
|
||||
if (timeout == 0) {
|
||||
resolve(result);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
resolve(result);
|
||||
}, timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise-based async validator that emits, after a delay, either:
|
||||
* - an error `{async: true}` if the control value does not match the expected value
|
||||
* - or null, otherwise
|
||||
* The delay is either:
|
||||
* - defined in `timeouts` parameter, as the association to the control value
|
||||
* - or 0ms otherwise
|
||||
*
|
||||
* @param expected The expected control value
|
||||
* @param timeouts A dictionary associating a control value to when the validation will trigger for
|
||||
* that value
|
||||
*/
|
||||
export function asyncValidator(expected: string, timeouts = {}): AsyncValidatorFn {
|
||||
return (control: AbstractControl) => {
|
||||
const timeout = (timeouts as any)[control.value] ?? 0;
|
||||
const result = control.value != expected ? {async: true} : null;
|
||||
return createValidationPromise(result, timeout);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an async validator that emits null or a custom error after a specified delay.
|
||||
* If the delay is set to 0ms, the validator emits synchronously.
|
||||
*
|
||||
* @param timeout Indicates when the validator will emit
|
||||
* @param shouldFail When true, a validation error is emitted, otherwise null is emitted
|
||||
* @param customError When supplied, overrides the default error `{async: true}`
|
||||
*/
|
||||
export function simpleAsyncValidator({
|
||||
timeout = 0,
|
||||
shouldFail,
|
||||
customError =
|
||||
{
|
||||
async: true
|
||||
}
|
||||
}: {timeout?: number, shouldFail: boolean, customError?: any}): AsyncValidatorFn {
|
||||
const result = shouldFail ? customError : null;
|
||||
return (c: AbstractControl) =>
|
||||
timeout === 0 ? of(result) : createValidationPromise(result, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the asynchronous validation state of each provided control
|
||||
* @param controls A collection of controls
|
||||
*/
|
||||
export function currentStateOf(controls: AbstractControl[]):
|
||||
{errors: any; pending: boolean; status: string;}[] {
|
||||
return controls.map(c => ({errors: c.errors, pending: c.pending, status: c.status}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an `EventEmitter` emitting the default error `{'async': true}`
|
||||
*
|
||||
* @param c The control instance
|
||||
*/
|
||||
export function asyncValidatorReturningObservable(c: AbstractControl): EventEmitter<any> {
|
||||
const e = new EventEmitter();
|
||||
Promise.resolve(null).then(() => {
|
||||
e.emit({'async': true});
|
||||
});
|
||||
return e;
|
||||
}
|
Loading…
Reference in New Issue