Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions packages/forms/test/directives_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
22 changes: 1 addition & 21 deletions packages/forms/test/form_array_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 2 additions & 31 deletions packages/forms/test/form_control_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
}
Expand Down
65 changes: 2 additions & 63 deletions packages/forms/test/form_group_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
}
Expand Down
87 changes: 87 additions & 0 deletions packages/forms/test/util.ts
Original file line number Diff line number Diff line change
@@ -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;
}