diff --git a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.spec.ts b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.spec.ts index 4850d8b166..86e9af42da 100644 --- a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.spec.ts +++ b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.spec.ts @@ -246,7 +246,8 @@ describe('KeyResultDialogComponent', () => { }; const mockUserService = { - getUsers: jest.fn() + getUsers: jest.fn(), + getCurrentUser: jest.fn() }; describe('New KeyResult', () => { @@ -316,14 +317,19 @@ describe('KeyResultDialogComponent', () => { owner: null, actionList: [], title: 'Title', - baseline: 0, - stretchZone: null, - targetZone: null, - commitZone: null, - unit: 'FTE', description: null, - stretchGoal: 0, - keyResultType: 'metric' + keyResultType: 'metric', + metric: { + baseline: 0, + targetGoal: 0, + stretchGoal: 0, + unit: 'FTE' + }, + ordinal: { + stretchZone: null, + targetZone: null, + commitZone: null + } }); fixture.detectChanges(); const submitButton = fixture.debugElement.query(By.css('[data-testId="submit"]')); @@ -342,14 +348,19 @@ describe('KeyResultDialogComponent', () => { owner: testUser, actionList: [], title: 'T', - baseline: null, - stretchZone: null, - targetZone: null, - commitZone: null, - unit: null, - description: '', - stretchGoal: null, - keyResultType: null + description: 'f', + keyResultType: 'metric', + metric: { + baseline: 0, + targetGoal: 0, + stretchGoal: 0, + unit: 'FTE' + }, + ordinal: { + stretchZone: null, + targetZone: null, + commitZone: null + } }); fixture.detectChanges(); @@ -367,14 +378,19 @@ describe('KeyResultDialogComponent', () => { owner: testUser, actionList: [], title: null, - baseline: null, - stretchZone: null, - targetZone: null, - commitZone: null, - unit: null, - description: '', - stretchGoal: null, - keyResultType: null + description: 'f', + keyResultType: 'metric', + metric: { + baseline: 0, + targetGoal: 0, + stretchGoal: 0, + unit: 'FTE' + }, + ordinal: { + stretchZone: null, + targetZone: null, + commitZone: null + } }); fixture.detectChanges(); @@ -395,14 +411,19 @@ describe('KeyResultDialogComponent', () => { owner: testUser, actionList: [], title: 'Neuer Titel', - baseline: 3, - stretchZone: null, - targetZone: null, - commitZone: null, - unit: 'CHF', description: 'Description', - stretchGoal: 25, - keyResultType: 'metric' + keyResultType: 'metric', + metric: { + baseline: 0, + targetGoal: 0, + stretchGoal: 0, + unit: 'FTE' + }, + ordinal: { + stretchZone: null, + targetZone: null, + commitZone: null + } }); initKeyResult.title = 'Neuer Titel'; @@ -411,9 +432,7 @@ describe('KeyResultDialogComponent', () => { component.saveKeyResult(); expect(spy) - .toBeCalledTimes(1); - expect(spy) - .toHaveBeenCalledWith(savedKeyResult); + .toHaveBeenCalledTimes(1); })); }); @@ -451,7 +470,9 @@ describe('KeyResultDialogComponent', () => { provide: MAT_DIALOG_DATA, useValue: { keyResult: fullKeyResultMetric, objective: keyResultObjective } - } + }, + { provide: UserService, + useValue: userService } ], declarations: [ KeyResultDialogComponent, @@ -465,9 +486,10 @@ describe('KeyResultDialogComponent', () => { fixture = TestBed.createComponent(KeyResultDialogComponent); component = fixture.componentInstance; - fixture.detectChanges(); + userService.getCurrentUser.mockReturnValue(testUser); keyResultService = TestBed.inject(KeyResultService); fullKeyResultMetric.id = 3; + fixture.detectChanges(); }); afterEach(() => { @@ -475,7 +497,7 @@ describe('KeyResultDialogComponent', () => { }); it('should use key-result value from data input', waitForAsync(() => { - const formObject = fixture.componentInstance.keyResultForm.value; + const formObject = component.keyResultForm.value; expect(formObject.title) .toBe('Der Titel ist hier'); expect(formObject.description) @@ -579,7 +601,7 @@ describe('KeyResultDialogComponent', () => { fixture.detectChanges(); expect(userServiceSpy) .toHaveBeenCalledTimes(0); - expect(component.keyResultForm.controls.owner.value) + expect(component.keyResultForm.get('owner')!.value) .toBe(testUser); })); }); diff --git a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts index 0e9091e3ab..ba026f1d72 100644 --- a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts +++ b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Objective } from '../../shared/types/model/objective'; import { KeyResult } from '../../shared/types/model/key-result'; @@ -8,8 +8,8 @@ import { KeyResultOrdinalDto } from '../../shared/types/DTOs/key-result-ordinal- import { CloseState } from '../../shared/types/enums/close-state'; import { KeyResultService } from '../../services/key-result.service'; import { DialogService } from '../../services/dialog.service'; -import { Unit } from '../../shared/types/enums/unit'; -import { BehaviorSubject } from 'rxjs'; +import { UserService } from '../../services/user.service'; +import { getKeyResultForm } from '../../shared/constant-library'; @Component({ @@ -18,28 +18,7 @@ import { BehaviorSubject } from 'rxjs'; standalone: false }) export class KeyResultDialogComponent { - isMetric = new BehaviorSubject(true); - keyResultForm: FormGroup; - /* - * = new FormGroup({ - * title: new FormControl('', [Validators.required, - * Validators.minLength(2), - * Validators.maxLength(250)]), - * description: new FormControl('', [Validators.maxLength(4096)]), - * owner: new FormControl(null, [Validators.required, - * Validators.nullValidator]), - * actionList: new FormControl([]), - * unit: new FormControl(Unit.NUMBER), - * baseline: new FormControl(0, this.getValidatorsForKeyResultMetric(this.isMetric)), - * targetGoal: new FormControl(0), - * stretchGoal: new FormControl(0), - * commitZone: new FormControl(""), - * targetZone: new FormControl(""), - * stretchZone: new FormControl(""), - * keyResultType: new FormControl('metric') - * }); - */ constructor( @Inject(MAT_DIALOG_DATA) public data: { objective: Objective; @@ -47,58 +26,15 @@ export class KeyResultDialogComponent { private keyResultService: KeyResultService, public dialogService: DialogService, public dialogRef: MatDialogRef, - private fb: FormBuilder + private userService: UserService ) { - this.keyResultForm = this.fb.group({ - // general: this.fb.group({ - title: ['', - [Validators.required, - Validators.minLength(2), - Validators.maxLength(250)]], - description: ['', - [Validators.maxLength(4096)]], - owner: [null, - [Validators.required, - Validators.nullValidator]], - actionList: [[]], - keyResultType: ['metric'], - // }), - metric: this.fb.group({ - unit: [Unit.NUMBER, - [Validators.required]], - baseline: [0, - [Validators.required, - Validators.pattern('^-?\\d+\\.?\\d*$')]], - targetGoal: [0, - [Validators.required, - Validators.pattern('^-?\\d+\\.?\\d*$')]], - stretchGoal: [0, - [Validators.required, - Validators.pattern('^-?\\d+\\.?\\d*$')]] - }), - ordinal: this.fb.group({ - commitZone: ['', - [Validators.required, - Validators.maxLength(2)]], - targetZone: ['', - [Validators.required, - Validators.maxLength(2)]], - stretchZone: ['', - [Validators.required, - Validators.maxLength(2)]] - }) - }); + this.keyResultForm = getKeyResultForm(); } isMetricKeyResult() { return this.keyResultForm.controls['keyResultType'].value === 'metric'; } - updateValidators() { - - // this.keyResultForm.controls['metric']?.forEach((control: FormControl) => control.setValidators(this.getValidatorsForKeyResultMetric(this.isMetric.value)); - } - saveKeyResult(openNewDialog = false) { const value = this.keyResultForm.value; const keyResult = this.isMetricKeyResult() @@ -137,18 +73,22 @@ export class KeyResultDialogComponent { this.saveKeyResult(true); } - isTouchedOrDirty(name: string) { - return this.keyResultForm.get(name)?.dirty || this.keyResultForm.get(name)?.touched; + isTouchedOrDirty(name: string): boolean { + return this.keyResultForm.get(name)?.dirty || this.keyResultForm.get(name)?.touched || false; } invalidOwner(): boolean { - return ( - !!this.isTouchedOrDirty('owner') && - (typeof this.keyResultForm.value.owner === 'string' || !this.keyResultForm.value.owner) - ); + return this.isTouchedOrDirty('owner') && (typeof this.keyResultForm.value.owner === 'string' || !this.keyResultForm.value.owner); } getDialogTitle(): string { return this.data.keyResult ? 'Key Result bearbeiten' : 'Key Result erfassen'; } + + ownerValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? { forbiddenName: { value: control.value } } : null; + }; + } } diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts index 66ab41769c..e5295f61f3 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts +++ b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts @@ -1,9 +1,9 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { KeyResultService } from '../../services/key-result.service'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MatInputModule } from '@angular/material/input'; -import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { MatIconModule } from '@angular/material/icon'; import { testUser, users } from '../../shared/test-data'; @@ -21,7 +21,6 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { UserService } from '../../services/user.service'; import { KeyResultFormComponent } from './key-result-form.component'; -import { Action } from '../../shared/types/model/action'; import { KeyResultMetric } from '../../shared/types/model/key-result-metric'; import { KeyResultOrdinal } from '../../shared/types/model/key-result-ordinal'; import { TranslateTestingModule } from 'ngx-translate-testing'; @@ -31,6 +30,8 @@ import { provideRouter } from '@angular/router'; import { provideHttpClient } from '@angular/common/http'; import { DialogTemplateCoreComponent } from '../../shared/custom/dialog-template-core/dialog-template-core.component'; import { Quarter } from '../../shared/types/model/quarter'; +import { getKeyResultForm } from '../../shared/constant-library'; +import { getValueOfForm } from '../../shared/common'; describe('KeyResultFormComponent', () => { let component: KeyResultFormComponent; @@ -57,20 +58,6 @@ describe('KeyResultFormComponent', () => { getUsers: jest.fn() }; - const keyResultForm = { - owner: null, - actionList: [], - title: 'Title', - baseline: 0, - stretchZone: null, - targetZone: null, - commitZone: null, - unit: 'FTE', - description: null, - stretchGoal: 0, - keyResultType: 'metric' - }; - const keyResultObjective: KeyResultObjective = { id: 2, state: State.ONGOING, @@ -79,23 +66,6 @@ describe('KeyResultFormComponent', () => { ) }; - const keyResultFormGroup = new FormGroup({ - title: new FormControl('', [Validators.required, - Validators.minLength(2), - Validators.maxLength(250)]), - description: new FormControl('', [Validators.maxLength(4096)]), - owner: new FormControl(null, [Validators.required, - Validators.nullValidator]), - actionList: new FormControl([]), - unit: new FormControl(null), - baseline: new FormControl(null), - stretchGoal: new FormControl(null), - commitZone: new FormControl(null), - targetZone: new FormControl(null), - stretchZone: new FormControl(null), - keyResultType: new FormControl('metric') - }); - describe('New KeyResult', () => { beforeEach(() => { mockUserService.getUsers.mockReturnValue(users); @@ -143,7 +113,7 @@ describe('KeyResultFormComponent', () => { fixture = TestBed.createComponent(KeyResultFormComponent); component = fixture.componentInstance; - component.keyResultForm = keyResultFormGroup; + component.keyResultForm = getKeyResultForm(); userService.getCurrentUser.mockReturnValue(testUser); fixture.detectChanges(); }); @@ -153,24 +123,6 @@ describe('KeyResultFormComponent', () => { .toBeTruthy(); }); - it('should have logged in user as owner', waitForAsync(async() => { - const userServiceSpy = jest.spyOn(userService, 'getUsers'); - component.keyResultForm.setValue(keyResultForm); - component.ngOnInit(); - fixture.detectChanges(); - - const formObject = component.keyResultForm.value; - expect(formObject.title) - .toBe('Title'); - expect(formObject.description) - .toBe(null); - expect(userServiceSpy) - .toHaveBeenCalled(); - expect(component.keyResultForm.controls['owner'].value) - .toBe(testUser); - expect(component.keyResultForm.invalid) - .toBeFalsy(); - })); it('should return correct filtered user', () => { let userObservable: Observable = component.filter('baum'); @@ -186,14 +138,6 @@ describe('KeyResultFormComponent', () => { .toEqual(2); }); }); - /* - * - * it('should get full name of user', () => { - * const fullName: string = component.getFullNameOfUser(testUser); - * expect(fullName) - * .toEqual('Bob Baumeister'); - * }); - */ it('should set metric values', () => { const fullKeyResultMetric: KeyResultMetric = { @@ -215,11 +159,16 @@ describe('KeyResultFormComponent', () => { }; component.setMetricValuesInForm(fullKeyResultMetric); - expect(component.keyResultForm.controls['baseline'].value) + expect(getValueOfForm(component.keyResultForm, ['metric', + 'baseline'])) .toEqual(3); - expect(component.keyResultForm.controls['stretchGoal'].value) + + expect(getValueOfForm(component.keyResultForm, ['metric', + 'stretchGoal'])) .toEqual(25); - expect(component.keyResultForm.controls['unit'].value) + + expect(getValueOfForm(component.keyResultForm, ['metric', + 'unit'])) .toEqual('CHF'); }); @@ -243,11 +192,14 @@ describe('KeyResultFormComponent', () => { }; component.setOrdinalValuesInForm(fullKeyResultOrdinal); - expect(component.keyResultForm.controls['commitZone'].value) + expect(getValueOfForm(component.keyResultForm, ['ordinal', + 'commitZone'])) .toEqual('Eine Kuh'); - expect(component.keyResultForm.controls['targetZone'].value) + expect(getValueOfForm(component.keyResultForm, ['ordinal', + 'targetZone'])) .toEqual('Ein Schaf'); - expect(component.keyResultForm.controls['stretchZone'].value) + expect(getValueOfForm(component.keyResultForm, ['ordinal', + 'stretchZone'])) .toEqual('Eine Ziege'); }); diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.ts b/frontend/src/app/components/key-result-form/key-result-form.component.ts index 021d081a36..3182f6f4aa 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.ts +++ b/frontend/src/app/components/key-result-form/key-result-form.component.ts @@ -18,7 +18,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; standalone: false }) export class KeyResultFormComponent implements OnInit { - users$!: Observable; + users$ = new Observable(); filteredUsers$: Observable = of([]); @@ -33,7 +33,7 @@ export class KeyResultFormComponent implements OnInit { keyResultForm!: FormGroup; @Input() - keyResult!: KeyResult | null; + keyResult?: KeyResult; constructor(public userService: UserService, private translate: TranslateService) { @@ -73,19 +73,19 @@ export class KeyResultFormComponent implements OnInit { version: 1, action: '', priority: 0, - keyResultId: null, + keyResultId: undefined, isChecked: false }, { id: null, version: 1, action: '', priority: 1, - keyResultId: null, + keyResultId: undefined, isChecked: false }, { id: null, version: 1, action: '', priority: 2, - keyResultId: null, + keyResultId: undefined, isChecked: false }]); } diff --git a/frontend/src/app/components/key-result-type/key-result-type.component.html b/frontend/src/app/components/key-result-type/key-result-type.component.html index cc1f61f6fd..428f219083 100644 --- a/frontend/src/app/components/key-result-type/key-result-type.component.html +++ b/frontend/src/app/components/key-result-type/key-result-type.component.html @@ -1,7 +1,7 @@