From 019b3033eda29a532a08d3f1d4ee71c12bda975d Mon Sep 17 00:00:00 2001 From: Suyash Patil <127177049+suyashpatil78@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:36:47 +0530 Subject: [PATCH] test: 100% coverage of generic-fields-form (#2455) * test: 100% coverage of generic-fields-form * minor --- .../generic-fields-form.component.spec.ts | 110 ++++++++++++++++-- .../generic-fields-form.component.ts | 56 +++++---- 2 files changed, 124 insertions(+), 42 deletions(-) diff --git a/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.spec.ts b/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.spec.ts index d01c8a75b2..9daa87cfa5 100644 --- a/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.spec.ts +++ b/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.spec.ts @@ -2,25 +2,111 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { GenericFieldsFormComponent } from './generic-fields-form.component'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { dependentCustomProperties } from 'src/app/core/mock-data/custom-property.data'; +import { AllowedPaymentModes } from 'src/app/core/models/allowed-payment-modes.enum'; -xdescribe('GenericFieldsFormComponent', () => { +describe('GenericFieldsFormComponent', () => { let component: GenericFieldsFormComponent; let fixture: ComponentFixture; - beforeEach( - waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [GenericFieldsFormComponent], - imports: [IonicModule.forRoot()], - }).compileComponents(); + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GenericFieldsFormComponent], + imports: [IonicModule.forRoot()], + providers: [FormBuilder], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); - fixture = TestBed.createComponent(GenericFieldsFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }) - ); + fixture = TestBed.createComponent(GenericFieldsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('ngOnInit():', () => { + it('should emit categoryChanged event if category option changes', () => { + spyOn(component.categoryChanged, 'emit'); + component.ngOnInit(); + component.genericFieldsFormGroup.controls.category.setValue(32108); + expect(component.categoryChanged.emit).toHaveBeenCalledOnceWith(32108); + }); + + it('should assign projectDependentFields to projectDependentFieldsMapping.projectId if project option changes', () => { + component.projectDependentFieldsMapping = { + 3943: dependentCustomProperties, + }; + component.ngOnInit(); + expect(component.projectDependentFields).toEqual([]); + component.genericFieldsFormGroup.controls.project.setValue(3943); + expect(component.projectDependentFields).toEqual(dependentCustomProperties); + }); + + it('should assign costCenterDependentFields to costCenterDependentFieldsMapping.costCenterId if cost center option changes', () => { + component.costCenterDependentFieldsMapping = { + 13795: dependentCustomProperties, + }; + component.ngOnInit(); + expect(component.costCenterDependentFields).toEqual([]); + component.genericFieldsFormGroup.controls.costCenter.setValue(13795); + expect(component.costCenterDependentFields).toEqual(dependentCustomProperties); + }); + + it('should emit paymentModeChanged event if payment mode option changes', () => { + spyOn(component.paymentModeChanged, 'emit'); + component.ngOnInit(); + component.genericFieldsFormGroup.controls.paymentMode.setValue(AllowedPaymentModes.PERSONAL_ACCOUNT); + expect(component.paymentModeChanged.emit).toHaveBeenCalledOnceWith(AllowedPaymentModes.PERSONAL_ACCOUNT); + }); + + it('should emit receiptChanged event if receipt option changes', () => { + spyOn(component.receiptChanged, 'emit'); + component.ngOnInit(); + component.genericFieldsFormGroup.controls.receipt_ids.setValue('txErhlkzewZF'); + expect(component.receiptChanged.emit).toHaveBeenCalledOnceWith('txErhlkzewZF'); + }); + + it('should emit fieldsTouched event if any field is touched', () => { + spyOn(component.fieldsTouched, 'emit'); + spyOn(component, 'isFieldTouched').and.returnValues(true); + component.ngOnInit(); + component.genericFieldsFormGroup.controls.amount.setValue(100); + expect(component.fieldsTouched.emit).toHaveBeenCalledOnceWith(['amount']); + }); + }); + + it('ngOnDestroy(): should unsubscribe from onChangeSub', () => { + component.onChangeSub = jasmine.createSpyObj('onChangeSub', ['unsubscribe']); + component.ngOnDestroy(); + expect(component.onChangeSub.unsubscribe).toHaveBeenCalledTimes(1); + }); + + it('writeValue(): should patch value to form control', () => { + component.genericFieldsFormGroup = new FormBuilder().group({ + location_1: new FormControl(), + }); + const newValue = new FormBuilder().group({ + location_1: new FormControl(200), + }); + spyOn(component.genericFieldsFormGroup, 'patchValue'); + component.writeValue(newValue); + expect(component.genericFieldsFormGroup.patchValue).toHaveBeenCalledOnceWith(newValue); + }); + + it('registerOnChange(): should subscribe to value changes', () => { + const onChange = (): void => {}; + spyOn(component.genericFieldsFormGroup.valueChanges, 'subscribe'); + component.registerOnChange(onChange); + expect(component.genericFieldsFormGroup.valueChanges.subscribe).toHaveBeenCalledOnceWith(onChange); + }); + + it('registerOnTouched(): should set onTouched', () => { + const onTouched = (): void => {}; + component.registerOnTouched(onTouched); + expect(component.onTouched).toEqual(onTouched); + }); }); diff --git a/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.ts b/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.ts index e5028ce9ba..f0d4754114 100644 --- a/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.ts +++ b/src/app/fyle/merge-expense/generic-fields-form/generic-fields-form.component.ts @@ -1,28 +1,22 @@ import { Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, TemplateRef } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Subscription, noop } from 'rxjs'; import { filter } from 'rxjs/operators'; import { CorporateCardExpense } from 'src/app/core/models/v2/corporate-card-expense.model'; -import { - FormControl, - FormGroup, - ControlValueAccessor, - NG_VALUE_ACCESSOR, - FormBuilder, - Validators, -} from '@angular/forms'; +import { FormGroup, ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder, Validators } from '@angular/forms'; import { FileObject } from 'src/app/core/models/file-obj.model'; import { CustomProperty } from 'src/app/core/models/custom-properties.model'; +import { AllowedPaymentModes } from 'src/app/core/models/allowed-payment-modes.enum'; type Option = Partial<{ label: string; - value: any; + value: string; }>; type OptionsData = Partial<{ options: Option[]; areSameValues: boolean; name: string; - value: any; + value: string; }>; @Component({ @@ -58,7 +52,7 @@ export class GenericFieldsFormComponent implements OnInit, ControlValueAccessor, @Input() purposeOptionsData: OptionsData; - @Input() categoryDependentTemplate: TemplateRef; + @Input() categoryDependentTemplate: TemplateRef; @Input() CCCTxns: CorporateCardExpense[]; @@ -70,11 +64,11 @@ export class GenericFieldsFormComponent implements OnInit, ControlValueAccessor, @Output() fieldsTouched = new EventEmitter(); - @Output() categoryChanged = new EventEmitter(); + @Output() categoryChanged = new EventEmitter(); - @Output() receiptChanged = new EventEmitter(); + @Output() receiptChanged = new EventEmitter(); - @Output() paymentModeChanged = new EventEmitter(); + @Output() paymentModeChanged = new EventEmitter(); genericFieldsFormGroup: FormGroup; @@ -84,9 +78,13 @@ export class GenericFieldsFormComponent implements OnInit, ControlValueAccessor, costCenterDependentFields: CustomProperty[] = []; + onTouched: () => void = noop; + constructor(private formBuilder: FormBuilder, private injector: Injector) {} - ngOnInit() { + isFieldTouched = (fieldName: string): boolean => this.genericFieldsFormGroup.get(fieldName).touched; + + ngOnInit(): void { this.genericFieldsFormGroup = this.formBuilder.group({ amount: [, Validators.required], receipt_ids: [], @@ -102,34 +100,34 @@ export class GenericFieldsFormComponent implements OnInit, ControlValueAccessor, purpose: [], }); - this.genericFieldsFormGroup.controls.category.valueChanges.subscribe((categoryId) => { + this.genericFieldsFormGroup.controls.category.valueChanges.subscribe((categoryId: number) => { this.categoryChanged.emit(categoryId); }); this.genericFieldsFormGroup.controls.project.valueChanges .pipe(filter((projectId) => !!projectId)) - .subscribe((projectId) => { + .subscribe((projectId: number) => { this.projectDependentFields = this.projectDependentFieldsMapping[projectId]; }); this.genericFieldsFormGroup.controls.costCenter.valueChanges .pipe(filter((costCenterId) => !!costCenterId)) - .subscribe((costCenterId) => { + .subscribe((costCenterId: number) => { this.costCenterDependentFields = this.costCenterDependentFieldsMapping[costCenterId]; }); - this.genericFieldsFormGroup.controls.paymentMode.valueChanges.subscribe((paymentMode) => { + this.genericFieldsFormGroup.controls.paymentMode.valueChanges.subscribe((paymentMode: AllowedPaymentModes) => { this.paymentModeChanged.emit(paymentMode); }); - this.genericFieldsFormGroup.controls.receipt_ids.valueChanges.subscribe((receiptIds) => { + this.genericFieldsFormGroup.controls.receipt_ids.valueChanges.subscribe((receiptIds: string) => { this.receiptChanged.emit(receiptIds); }); - this.genericFieldsFormGroup.valueChanges.subscribe((formControlNames) => { - const touchedItems = []; + this.genericFieldsFormGroup.valueChanges.subscribe((formControlNames: FormGroup) => { + const touchedItems: string[] = []; Object.keys(formControlNames).forEach((key) => { - if (this.genericFieldsFormGroup.get(key).touched) { + if (this.isFieldTouched(key)) { touchedItems.push(key); } }); @@ -137,23 +135,21 @@ export class GenericFieldsFormComponent implements OnInit, ControlValueAccessor, }); } - onTouched = () => {}; - ngOnDestroy(): void { - this.onChangeSub.unsubscribe(); + this.onChangeSub?.unsubscribe(); } - writeValue(value: any) { + writeValue(value: FormGroup): void { if (value) { this.genericFieldsFormGroup.patchValue(value); } } - registerOnChange(onChange): void { + registerOnChange(onChange: () => void): void { this.onChangeSub = this.genericFieldsFormGroup.valueChanges.subscribe(onChange); } - registerOnTouched(onTouched): void { + registerOnTouched(onTouched: () => void): void { this.onTouched = onTouched; } }