diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.spec.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.spec.ts index 038187decc6f18d2bac42ab33d94cdf3a751912a..f6dfce08e009d031361edc19c27e3ac73f97b353 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.spec.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.spec.ts @@ -21,10 +21,17 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { createEmptyStateResource, createStateResource, EMPTY_STRING, InvalidParam, setInvalidParamValidationError, StateResource, } from '@alfa-client/tech-shared'; +import { + createEmptyStateResource, + createStateResource, + EMPTY_STRING, + InvalidParam, + setInvalidParamValidationError, + StateResource, +} from '@alfa-client/tech-shared'; import { Injectable } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, UntypedFormGroup, Validators } from '@angular/forms'; +import { fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, UrlSegment } from '@angular/router'; import { faker } from '@faker-js/faker/.'; import { createDummy, Dummy } from 'libs/tech-shared/test/dummy'; @@ -213,25 +220,30 @@ describe('KeycloakFormService', () => { beforeEach(() => { service._doSubmit = jest.fn().mockReturnValue(singleHot(dummyStateResource)); - service._processInvalidForm = jest.fn().mockReturnValue(of(createEmptyStateResource())); service._processResponseValidationErrors = jest.fn().mockReturnValue(of(createEmptyStateResource())); service.form.setErrors(null); }); describe('on client validation error', () => { - it('should process invalid form', () => { + it('should return empty state resource with loading first', () => { service.form.setErrors({ dummy: 'dummy error' }); - service.submit().subscribe(); - - expect(service._processInvalidForm).toHaveBeenCalled(); + service.submit().subscribe((stateResource: StateResource<Dummy>) => { + expect(stateResource).toEqual(createEmptyStateResource(true)); + }); }); - it('should return invalid form processing result on invalid form', () => { + it('should return empty state resource withouth loading after delay', fakeAsync(() => { service.form.setErrors({ dummy: 'dummy error' }); - expect(service.submit()).toBeObservable(singleColdCompleted(createEmptyStateResource())); - }); + tick(200); + + service.submit().subscribe((stateResource: StateResource<Dummy>) => { + expect(stateResource).toEqual(createEmptyStateResource()); + }); + + flush(); + })); }); it('should call do submit', () => { @@ -265,22 +277,6 @@ describe('KeycloakFormService', () => { }); }); - describe('process invalid form', () => { - beforeEach(() => { - service._showValidationErrorForAllInvalidControls = jest.fn(); - }); - - it('should show validation errors on all invalid controls', () => { - service._processInvalidForm(); - - expect(service._showValidationErrorForAllInvalidControls).toHaveBeenCalledWith(service.form); - }); - - it('should return emit state resource', () => { - expect(service._processInvalidForm()).toBeObservable(singleColdCompleted(createEmptyStateResource())); - }); - }); - describe('process response validation errors', () => { const keycloakHttpError: KeycloakHttpErrorResponse = createKeycloakHttpErrorResponse(); @@ -343,66 +339,6 @@ describe('KeycloakFormService', () => { }); }); - describe('show validation errors on all invalid controls', () => { - it('should update value and validity on invalid control', () => { - const control: AbstractControl = new FormControl(); - control.setErrors({ dummy: 'error' }); - const spy: jest.SpyInstance = jest.spyOn(control, 'updateValueAndValidity'); - - service._showValidationErrorForAllInvalidControls(control); - - expect(spy).toHaveBeenCalled(); - }); - - it('should update value and validity form invalid control from group', () => { - const control: AbstractControl = new FormControl(); - control.setErrors({ dummy: 'error' }); - const spy: jest.SpyInstance = jest.spyOn(service, '_showValidationErrorForAllInvalidControls'); - const group: UntypedFormGroup = new FormGroup({ - someControl: control, - }); - - service._showValidationErrorForAllInvalidControls(group); - - expect(spy).toHaveBeenCalledWith(control); - }); - - it('should update value and validity on invalid control from group', () => { - const control: AbstractControl = new FormControl(); - control.setErrors({ dummy: 'error' }); - const spy: jest.SpyInstance = jest.spyOn(control, 'updateValueAndValidity'); - const group: UntypedFormGroup = new FormGroup({ - someControl: control, - }); - - service._showValidationErrorForAllInvalidControls(group); - - expect(spy).toHaveBeenCalled(); - }); - - it('should update value and validity form invalid control from array', () => { - const control: AbstractControl = new FormControl(); - control.setErrors({ dummy: 'error' }); - const spy: jest.SpyInstance = jest.spyOn(service, '_showValidationErrorForAllInvalidControls'); - const array: FormArray = new FormArray([control]); - - service._showValidationErrorForAllInvalidControls(array); - - expect(spy).toHaveBeenCalledWith(control); - }); - - it('should update value and validity on invalid control from group', () => { - const control: AbstractControl = new FormControl(); - control.setErrors({ dummy: 'error' }); - const spy: jest.SpyInstance = jest.spyOn(control, 'updateValueAndValidity'); - const array: FormArray = new FormArray([control]); - - service._showValidationErrorForAllInvalidControls(array); - - expect(spy).toHaveBeenCalled(); - }); - }); - describe('set validation errors on controls', () => { it('should set invalid param validation error', () => { const invalidParam: InvalidParam = createInvalidParam(); diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts index e1856df17c3952c3fde88e15d205b6838b9083ff..28fd9f9cf9a415eded091ede9c85449e9be57824 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts @@ -21,11 +21,17 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { createEmptyStateResource, InvalidParam, isLoaded, setInvalidParamValidationError, StateResource, } from '@alfa-client/tech-shared'; +import { + createEmptyStateResource, + InvalidParam, + isLoaded, + setInvalidParamValidationError, + StateResource, +} from '@alfa-client/tech-shared'; import { inject, Injectable } from '@angular/core'; -import { AbstractControl, FormArray, FormBuilder, FormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { ActivatedRoute, UrlSegment } from '@angular/router'; -import { catchError, first, Observable, of, tap } from 'rxjs'; +import { catchError, delay, first, Observable, of, startWith, tap } from 'rxjs'; import { ValidationMessageCode } from '../../../../tech-shared/src/lib/validation/tech.validation.messages'; import * as FormUtil from './form.util'; import { ErrorRepresentation, KeycloakErrorMessage, KeycloakFieldName, KeycloakHttpErrorResponse } from './keycloak-error.model'; @@ -73,16 +79,15 @@ export abstract class KeycloakFormService<T> { public submit(): Observable<StateResource<T>> { if (this.form.invalid) { - return this._processInvalidForm(); + return this.creatDelayedEmptyStateResource(); } return this._doSubmit().pipe( catchError((keycloakError: KeycloakHttpErrorResponse) => this._processResponseValidationErrors(keycloakError)), ); } - _processInvalidForm(): Observable<StateResource<T>> { - this._showValidationErrorForAllInvalidControls(this.form); - return of(createEmptyStateResource<T>()); + private creatDelayedEmptyStateResource(): Observable<StateResource<T>> { + return of(createEmptyStateResource<T>()).pipe(delay(200), startWith(createEmptyStateResource<T>(true))); } _processResponseValidationErrors(keycloakError: KeycloakHttpErrorResponse): Observable<StateResource<T>> { @@ -97,13 +102,6 @@ export abstract class KeycloakFormService<T> { return of(createEmptyStateResource<T>()); } - _showValidationErrorForAllInvalidControls(control: AbstractControl): void { - if (control.invalid) control.updateValueAndValidity(); - if (control instanceof FormGroup || control instanceof FormArray) { - Object.values(control.controls).forEach((control) => this._showValidationErrorForAllInvalidControls(control)); - } - } - _setValidationErrorsOnControls(invalidParams: InvalidParam[]): void { invalidParams.forEach((invalidParam: InvalidParam) => { setInvalidParamValidationError(this.form, invalidParam); diff --git a/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts index 2a11953641df481a4aaac5e547983b36afa6c047..3e91b04d688d9969bdb842218e83b9af8a64e9fc 100644 --- a/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts @@ -45,9 +45,7 @@ export abstract class FormControlEditorAbstractComponent implements ControlValue } ngOnInit(): void { - this._changesSubscr = this.fieldControl.valueChanges.subscribe((value: unknown) => { - this._fieldControlOnChangeHandler(value); - }); + this._changesSubscr = this.fieldControl.valueChanges.subscribe(this._fieldControlOnChangeHandler); if (this.control) { this._statusSubscr = this.control.statusChanges.subscribe(() => {