From 10763b1d592e8deedcc635ac6c314af04add8a6d Mon Sep 17 00:00:00 2001 From: Wilson Wong Date: Wed, 29 Mar 2023 11:51:01 -0700 Subject: [PATCH] BRS-1087: Add unit tests for components site-metrics and metrics-filter (#258) * BRS-1087: Add unit tests for components site-metrics and metrics-filter * BRS-1087: Remove commented out line --- .../metrics-filter.component.spec.ts | 84 +++++++++- .../metrics-filter.component.ts | 1 - .../site-metrics.component.spec.ts | 149 +++++++++++++++++- .../site-metrics/site-metrics.component.ts | 1 - src/app/shared/utils/mock-data.ts | 14 ++ 5 files changed, 236 insertions(+), 13 deletions(-) diff --git a/src/app/metrics/metrics-filter/metrics-filter.component.spec.ts b/src/app/metrics/metrics-filter/metrics-filter.component.spec.ts index 5fbf1bd8..158ed4b3 100644 --- a/src/app/metrics/metrics-filter/metrics-filter.component.spec.ts +++ b/src/app/metrics/metrics-filter/metrics-filter.component.spec.ts @@ -1,21 +1,55 @@ import { HttpClientModule } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Constants } from '../../shared/utils/constants'; import { ConfigService } from 'src/app/services/config.service'; +import { DataService } from 'src/app/services/data.service'; +import { MockData } from 'src/app/shared/utils/mock-data'; +import { BehaviorSubject } from 'rxjs'; import { MetricsFilterComponent } from './metrics-filter.component'; +import { SharedMetricsModule } from '../../shared/components/metrics/shared-metrics.module'; +import { DsFormsModule } from '../../shared/components/ds-forms/ds-forms.module' describe('MetricsFilterComponent', () => { let component: MetricsFilterComponent; let fixture: ComponentFixture; + let mockData = MockData.mockParkFacility_1 + + let mockPark1 = MockData.mockPark_1; + let mockPark2 = MockData.mockPark_2; + + let mockFacility1 = MockData.mockFacility_1 + let mockFacility2 = MockData.mockFacility_2 + let mockFacility3 = MockData.mockFacility_3 + + let mockDataService = { + watchItem: (id) => { + if (id === Constants.dataIds.PARK_AND_FACILITY_LIST) { + return new BehaviorSubject(mockData); + } else if (id === Constants.dataIds.METRICS_FILTERS_PARAMS) { + return new BehaviorSubject([mockFacility1,mockFacility2,mockFacility3]) + } + return new BehaviorSubject(null); + }, + }; + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ MetricsFilterComponent ], - imports: [ReactiveFormsModule, FormsModule, HttpClientModule], - providers: [ConfigService] - }) - .compileComponents(); + declarations: [MetricsFilterComponent], + imports: [ + ReactiveFormsModule, + FormsModule, + HttpClientModule, + SharedMetricsModule, + DsFormsModule + ], + providers: [ + ConfigService, + { provide: DataService, useValue: mockDataService }, + ], + }).compileComponents(); fixture = TestBed.createComponent(MetricsFilterComponent); component = fixture.componentInstance; @@ -25,4 +59,44 @@ describe('MetricsFilterComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have initial values', () => { + expect(component.timeSpanOptions).toEqual(['year', 'month', 'week']); + expect(component.timeSpanLabels).toEqual(['12M', '30D', '7D']); + expect(component.fileTypeOptions).toEqual([ + { value: 'pdf', display: 'PDF' }, + { value: 'csv', display: 'CSV' }, + { value: 'json', display: 'JSON' }, + ]); + }); + + it('should toggle outputs to true', () => { + component.selectAllExports(false); + component.selectAllExports(true); + + expect(component.fields.exportBusiestDays.value).toBeTrue(); + expect(component.fields.exportPassActivityByDay.value).toBeTrue(); + expect(component.fields.exportPassTrendsByHour.value).toBeTrue(); + expect(component.fields.exportPassBreakdownByStatus.value).toBeTrue(); + expect(component.fields.exportReturningGuests.value).toBeTrue(); + }); + + it('should toggle outputs to false', () => { + component.selectAllExports(true); + component.selectAllExports(false); + + expect(component.fields.exportBusiestDays.value).toBeFalse(); + expect(component.fields.exportPassActivityByDay.value).toBeFalse(); + expect(component.fields.exportPassTrendsByHour.value).toBeFalse(); + expect(component.fields.exportPassBreakdownByStatus.value).toBeFalse(); + expect(component.fields.exportReturningGuests.value).toBeFalse(); + }); + + it('should populate park list', () => { + let tempParkOptions = [] + for (const park of Object.keys(component.parkFacilitiesList)) { + tempParkOptions.push({ value: park, display: component.parkFacilitiesList[park].name }); + } + expect(component.parkOptions).toEqual(tempParkOptions) + }) }); diff --git a/src/app/metrics/metrics-filter/metrics-filter.component.ts b/src/app/metrics/metrics-filter/metrics-filter.component.ts index d4351bb8..70d94845 100644 --- a/src/app/metrics/metrics-filter/metrics-filter.component.ts +++ b/src/app/metrics/metrics-filter/metrics-filter.component.ts @@ -134,7 +134,6 @@ export class MetricsFilterComponent extends BaseFormComponent { } selectAllExports(select: boolean) { - console.log('select:', select); if (select) { this.fields.exportBusiestDays.setValue(true); this.fields.exportPassActivityByDay.setValue(true); diff --git a/src/app/metrics/site-metrics/site-metrics.component.spec.ts b/src/app/metrics/site-metrics/site-metrics.component.spec.ts index fc659197..a1bb5f32 100644 --- a/src/app/metrics/site-metrics/site-metrics.component.spec.ts +++ b/src/app/metrics/site-metrics/site-metrics.component.spec.ts @@ -1,27 +1,164 @@ import { HttpClientModule } from '@angular/common/http'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + ComponentFixture, + TestBed, + fakeAsync, + tick, +} from '@angular/core/testing'; import { ConfigService } from 'src/app/services/config.service'; +import { Constants } from '../../shared/utils/constants'; +import { BehaviorSubject } from 'rxjs'; +import { DataService } from 'src/app/services/data.service'; +import { MockData } from 'src/app/shared/utils/mock-data'; import { SiteMetricsComponent } from './site-metrics.component'; +import { SharedMetricsModule } from '../../shared/components/metrics/shared-metrics.module'; describe('SiteMetricsComponent', () => { let component: SiteMetricsComponent; let fixture: ComponentFixture; + let buildSpy; + const testSk = '37bbecdf38089efe13af862dc9d6f460' + + let testMetricsData1 = MockData.metricsData1; + let testMetricsData2 = MockData.metricsData2; + + let testSubject = new BehaviorSubject(testMetricsData1); + + let fakeDataService = { + watchItem: () => { + return testSubject; + }, + }; + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ SiteMetricsComponent ], - imports: [HttpClientModule], - providers: [ConfigService] - }) - .compileComponents(); + declarations: [SiteMetricsComponent], + imports: [HttpClientModule, SharedMetricsModule], + providers: [ + ConfigService, + { provide: DataService, useValue: fakeDataService }, + ], + }).compileComponents(); fixture = TestBed.createComponent(SiteMetricsComponent); component = fixture.componentInstance; + buildSpy = spyOn(component, 'buildCharts').and.callThrough(); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); + expect(component.isGenerating).toBeFalse(); + expect(buildSpy).toHaveBeenCalledTimes(0); + }); + + it('should load metrics data into component', async () => { + let data = testMetricsData1; + expect(component.passActive).toEqual(data.active); + expect(component.passExpired).toEqual(data.expired); + expect(component.passReserved).toEqual(data.reserved); + expect(component.passExpired).toEqual(data.expired); + }); + + it('should update metrics with new data', async () => { + let data = testMetricsData2; + + component.buildCharts(data); + + await fixture.isStable(); + expect(component.passActive).toEqual(data.active); + expect(component.passExpired).toEqual(data.expired); + expect(component.passReserved).toEqual(data.reserved); + expect(component.passExpired).toEqual(data.expired); + }); + + it('should handle generate export report click', fakeAsync(async () => { + let getPassExportSpy = spyOn(component, 'getPassExport'); + let apiSpy = spyOn(component['apiService'], 'get').and.returnValue( + new BehaviorSubject({ + status: 'Export job created', + sk: '37bbecdf38089efe13af862dc9d6f460', + }) + ); + + const buttons = + fixture.debugElement.nativeElement.querySelectorAll('button'); + buttons[0].click(); + tick(1200); + await fixture.isStable(); + fixture.detectChanges(); + + expect(getPassExportSpy).toHaveBeenCalledTimes(1); + expect(apiSpy).toHaveBeenCalledTimes(1); + expect(component.isGenerating).toBeTrue(); + expect(component.signedURL).toBeNull(); + expect(component.statusMessage).toBeTruthy(); + })); + + it('should test when API call finds no export report', async () => { + let toastService = spyOn(component['toastService'], 'addMessage') + let apiSpy = spyOn(component['apiService'], 'get').and.returnValue( + new BehaviorSubject({ + status: 'Job not found', + sk: testSk + }) + ); + + component.getPassExport(testSk) + await fixture.isStable(); + fixture.detectChanges(); + + expect(apiSpy).toHaveBeenCalledTimes(1); + expect(component.isGenerating).toBeFalse(); + expect(component.signedURL).toBeUndefined(); + expect(component.buttonText).toBe('Export Pass Data'); + expect(toastService).toHaveBeenCalledWith( + `Sorry, that didn't work. Please try again.`, + 'Export Service', + Constants.ToastTypes.ERROR + ) + }) + + it('should test when API call finds export report ready', fakeAsync(async () => { + let testSignedURL = 'www.google.ca' + let toastService = spyOn(component['toastService'], 'addMessage') + let apiSpy = spyOn(component['apiService'], 'get').and.returnValue( + new BehaviorSubject({ + status: 'Job complete', + sk: testSk, + signedURL: testSignedURL, + jobObj: { + progressPercentage: 100 + } + }) + ); + let windowSpy = spyOn(window, 'open') + + component.getPassExport(testSk) + await fixture.isStable(); + fixture.detectChanges(); + + expect(apiSpy).toHaveBeenCalledTimes(1); + expect(component.isGenerating).toBeFalse(); + expect(component.signedURL).toEqual(testSignedURL); + expect(component.buttonText).toBe('Export Pass Data'); + expect(toastService).toHaveBeenCalledWith( + `Your report is downloading.`, + 'Export Service', + Constants.ToastTypes.SUCCESS + ) + + tick(5500) + expect(windowSpy).toHaveBeenCalledOnceWith( + testSignedURL, '_blank' + ) + })) + + it('should unsubscribe on destroy', async () => { + const subSpy = spyOn(component['subscriptions'], 'unsubscribe'); + component.ngOnDestroy(); + expect(subSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/src/app/metrics/site-metrics/site-metrics.component.ts b/src/app/metrics/site-metrics/site-metrics.component.ts index 24bd933f..0a660025 100644 --- a/src/app/metrics/site-metrics/site-metrics.component.ts +++ b/src/app/metrics/site-metrics/site-metrics.component.ts @@ -80,7 +80,6 @@ export class SiteMetricsComponent implements OnDestroy { labels.push(item); data.push(res[item]); passTotal += res[item]; - console.log(item); switch (item) { case Constants.stateLabelDictionary.active.state: this.passActive = res[item]; diff --git a/src/app/shared/utils/mock-data.ts b/src/app/shared/utils/mock-data.ts index a93057d3..85182f4d 100644 --- a/src/app/shared/utils/mock-data.ts +++ b/src/app/shared/utils/mock-data.ts @@ -255,4 +255,18 @@ export class MockData { }, }, }; + + public static readonly metricsData1 = { + 'active': 2, + 'reserved': 30, + 'cancelled': 400, + 'expired': 5000 + } + + public static readonly metricsData2 = { + 'active': 1, + 'reserved': 20, + 'cancelled': 300, + 'expired': 4000 + } }