From 94225563a68aecb49e713eb8508fb278682cec08 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Thu, 21 Jan 2021 08:49:44 +0100 Subject: [PATCH] Improved test coverage in front end Added create test of ei-coordinator.component. Removed unused ei-type.datasource and related objects. Added test coverage of ei.service. Removed getVersion method since it is not used. Change-Id: I51ab5e6f178ba37497e5fa6618d4ce0cd385a461 Issue-ID: NONRTRIC-395 Signed-off-by: elinuxhenrik --- .gitignore | 1 + .../ei-coordinator.component.spec.ts | 41 ++++++++- .../app/ei-coordinator/ei-coordinator.component.ts | 19 ++--- .../src/app/ei-coordinator/ei-job.datasource.ts | 47 +++-------- .../app/ei-coordinator/ei-producer.datasource.ts | 48 +++-------- .../src/app/ei-coordinator/ei-type.datasource.ts | 98 ---------------------- .../src/app/interfaces/{ei.jobs.ts => ei.types.ts} | 5 -- .../policy-control.component.spec.ts | 2 +- .../src/app/services/ei/ei.service.spec.ts | 70 +++++++++++++++- webapp-frontend/src/app/services/ei/ei.service.ts | 24 +----- 10 files changed, 143 insertions(+), 212 deletions(-) delete mode 100644 webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts rename webapp-frontend/src/app/interfaces/{ei.jobs.ts => ei.types.ts} (94%) diff --git a/.gitignore b/.gitignore index 6926ab1..55ac7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /tmp /out-tsc package-lock.json +coverage # dependencies /node diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts index 59b09e9..50127a3 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts @@ -18,16 +18,51 @@ * ========================LICENSE_END=================================== */ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { MatIconModule, MatTableModule } from '@angular/material'; + +import { of } from 'rxjs'; import { EICoordinatorComponent } from './ei-coordinator.component'; +import { EIJobDataSource } from './ei-job.datasource'; +import { EIProducerDataSource } from './ei-producer.datasource'; +import { UiService } from '../services/ui/ui.service'; describe('EICoordinatorComponent', () => { let component: EICoordinatorComponent; let fixture: ComponentFixture; beforeEach(async(() => { + const jobDataSourceSpy = jasmine.createSpyObj('EIJobDataSource', [ 'connect', 'loadTable', 'disconnect' ]); + const producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', [ 'connect', 'loadTable', 'getProducers', 'disconnect' ]); + + jobDataSourceSpy.connect.and.returnValue(of([])); + jobDataSourceSpy.disconnect(); + producerDataSourceSpy.connect.and.returnValue(of([])); + producerDataSourceSpy.getProducers.and.returnValue(of([])); + producerDataSourceSpy.disconnect(); + TestBed.configureTestingModule({ - declarations: [ EICoordinatorComponent ] + imports: [ + MatIconModule, + MatTableModule, + BrowserAnimationsModule, + ReactiveFormsModule + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ], + declarations: [ + EICoordinatorComponent + ], + providers: [ + { provide: EIJobDataSource, useValue: jobDataSourceSpy }, + { provide: EIProducerDataSource, useValue: producerDataSourceSpy }, + UiService, + FormBuilder, + ] }) .compileComponents(); })); @@ -37,4 +72,8 @@ describe('EICoordinatorComponent', () => { component = fixture.componentInstance; fixture.detectChanges(); }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts index 554779c..90dfef1 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts @@ -17,20 +17,18 @@ * limitations under the License. * ========================LICENSE_END=================================== */ -import { Component, OnInit, ViewChild, Version } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { MatSort } from '@angular/material/sort'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { FormBuilder, FormGroup } from '@angular/forms'; import { MatTableDataSource } from '@angular/material'; import { defer, BehaviorSubject, Observable } from 'rxjs'; -import { map, withLatestFrom, startWith, tap } from 'rxjs/operators'; +import { map, withLatestFrom, startWith } from 'rxjs/operators'; -import { EIService } from '../services/ei/ei.service'; -import { EIJob, EIProducer } from '../interfaces/ei.jobs'; -import { EIProducerDataSource } from './ei-producer.datasource'; +import { EIJob, EIProducer } from '../interfaces/ei.types'; import { EIJobDataSource } from './ei-job.datasource'; -import { NotificationService } from '../services/ui/notification.service'; +import { EIProducerDataSource } from './ei-producer.datasource'; import { UiService } from '../services/ui/ui.service'; class EIJobInfo { @@ -54,8 +52,6 @@ class EIJobInfo { }) export class EICoordinatorComponent implements OnInit { - eiJobsDataSource: EIJobDataSource; - eiProducersDataSource: EIProducerDataSource; producers$: Observable; filteredProducers$: Observable; @ViewChild(MatSort, { static: true }) sort: MatSort; @@ -67,18 +63,15 @@ export class EICoordinatorComponent implements OnInit { eiProducersData: MatTableDataSource; constructor( - private eiSvc: EIService, - private notificationService: NotificationService, + private eiJobsDataSource: EIJobDataSource, + private eiProducersDataSource: EIProducerDataSource, private ui: UiService, private formBuilder: FormBuilder) { this.formGroup = formBuilder.group({ filter: [""] }); } ngOnInit() { - this.eiJobsDataSource = new EIJobDataSource(this.eiSvc, this.sort, this.notificationService); - this.eiProducersDataSource = new EIProducerDataSource(this.eiSvc, this.sort, this.notificationService); this.eiJobsDataSource.loadTable(); - //this.eiProducersDataSource.loadTable(); this.producers$= this.eiProducersDataSource.getProducers(); this.filteredProducers$ = defer(() => this.formGroup.get("filter") diff --git a/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts index b0a348a..9e048b6 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts @@ -18,19 +18,23 @@ * ========================LICENSE_END=================================== */ -import { DataSource } from '@angular/cdk/collections'; import { HttpErrorResponse } from '@angular/common/http'; -import { MatSort } from '@angular/material'; -import { Observable } from 'rxjs/Observable'; +import { Injectable } from '@angular/core'; +import { MatTableDataSource } from '@angular/material'; + import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { merge } from 'rxjs'; import { of } from 'rxjs/observable/of'; import { catchError, finalize, map } from 'rxjs/operators'; -import { EIJob } from '../interfaces/ei.jobs'; + +import { EIJob } from '../interfaces/ei.types'; import { EIService } from '../services/ei/ei.service'; import { NotificationService } from '../services/ui/notification.service'; -export class EIJobDataSource extends DataSource { +@Injectable({ + providedIn: 'root' +}) + +export class EIJobDataSource extends MatTableDataSource { private eiJobSubject = new BehaviorSubject([]); @@ -42,7 +46,6 @@ export class EIJobDataSource extends DataSource { constructor( private eiSvc: EIService, - public sort: MatSort, private notificationService: NotificationService) { super(); } @@ -63,38 +66,12 @@ export class EIJobDataSource extends DataSource { }); } - connect(): Observable { - const dataMutations = [ - this.eiJobSubject.asObservable(), - this.sort.sortChange - ]; - return merge(...dataMutations).pipe(map(() => { - return this.getSortedData([...this.eiJobSubject.getValue()]); - })); + connect(): BehaviorSubject { + return this.eiJobSubject; } disconnect(): void { this.eiJobSubject.complete(); this.loadingSubject.complete(); } - - private getSortedData(data: EIJob[]) { - if (!this.sort || !this.sort.active || this.sort.direction === '') { - return data; - } - - return data.sort((a, b) => { - const isAsc = this.sort.direction === 'asc'; - switch (this.sort.active) { - case 'ei_job_identity': return compare(a.ei_job_identity, b.ei_job_identity, isAsc); - case 'owner': return compare(a.owner, b.owner, isAsc); - case 'ei_type_identity': return compare(a.ei_type_identity, b.ei_type_identity, isAsc); - default: return 0; - } - }); - } -} - -function compare(a: string, b: string, isAsc: boolean) { - return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } diff --git a/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts index 44af50a..2b12021 100644 --- a/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts +++ b/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts @@ -18,19 +18,24 @@ * ========================LICENSE_END=================================== */ -import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { HttpErrorResponse } from '@angular/common/http'; -import { MatSort } from '@angular/material'; +import { Injectable } from '@angular/core'; +import { MatTableDataSource } from '@angular/material'; + import { Observable } from 'rxjs/Observable'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { merge } from 'rxjs'; import { of } from 'rxjs/observable/of'; -import { catchError, finalize, map, tap } from 'rxjs/operators'; -import { EIProducer } from '../interfaces/ei.jobs'; +import { catchError, finalize, tap } from 'rxjs/operators'; + +import { EIProducer } from '../interfaces/ei.types'; import { EIService } from '../services/ei/ei.service'; import { NotificationService } from '../services/ui/notification.service'; -export class EIProducerDataSource extends DataSource { +@Injectable({ + providedIn: 'root' +}) + +export class EIProducerDataSource extends MatTableDataSource { private producerSubject = new BehaviorSubject([]); @@ -42,7 +47,6 @@ export class EIProducerDataSource extends DataSource { constructor( private eiSvc: EIService, - public sort: MatSort, private notificationService: NotificationService) { super(); } @@ -65,14 +69,8 @@ export class EIProducerDataSource extends DataSource { this.connect(); } - connect(): Observable { - const dataMutations = [ - this.producerSubject.asObservable(), - this.sort.sortChange - ]; - return merge(...dataMutations).pipe(map(() => { - return this.getSortedData([...this.producerSubject.getValue()]); - })); + connect(): BehaviorSubject { + return this.producerSubject; } disconnect(): void { @@ -80,28 +78,8 @@ export class EIProducerDataSource extends DataSource { this.loadingSubject.complete(); } - private getSortedData(data: EIProducer[]) { - if (!this.sort || !this.sort.active || this.sort.direction === '') { - return data; - } - - return data.sort((a, b) => { - const isAsc = this.sort.direction === 'asc'; - switch (this.sort.active) { - case 'id': return compare(a.ei_producer_id, b.ei_producer_id, isAsc); - case 'type': return compare(a.ei_producer_types[0], b.ei_producer_types[0], isAsc); - case 'status': return compare(a.status, b.status, isAsc); - default: return 0; - } - }); - } - getProducers(): Observable { return this.eiSvc.getEIProducers() .pipe(tap(console.log)); } } - -function compare(a: string, b: string, isAsc: boolean) { - return (a < b ? -1 : 1) * (isAsc ? 1 : -1); -} diff --git a/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts deleted file mode 100644 index 8d48196..0000000 --- a/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 Nordix Foundation - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================LICENSE_END=================================== - */ - -import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { HttpErrorResponse } from '@angular/common/http'; -import { MatSort } from '@angular/material'; -import { Observable } from 'rxjs/Observable'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { merge } from 'rxjs'; -import { of } from 'rxjs/observable/of'; -import { catchError, finalize, map } from 'rxjs/operators'; -import { EIService } from '../services/ei/ei.service'; -import { EIType } from '../interfaces/ei.jobs'; -import { NotificationService } from '../services/ui/notification.service'; - -export class EITypeDataSource extends DataSource { - - private eiTypeSubject = new BehaviorSubject([]); - - private loadingSubject = new BehaviorSubject(false); - - public loading$ = this.loadingSubject.asObservable(); - - public rowCount = 1; // hide footer during intial load - - constructor(private eiSvc: EIService, - private sort: MatSort, - private notificationService: NotificationService) { - super(); - } - - loadTable() { - this.loadingSubject.next(true); - this.eiSvc.getEITypes() - .pipe( - catchError((her: HttpErrorResponse) => { - this.notificationService.error('Failed to get EI types: ' + her.statusText + ', ' + her.error); - return of([]); - }), - finalize(() => this.loadingSubject.next(false)) - ) - .subscribe((types: EIType[]) => { - console.log("Types: " + types); - this.rowCount = types.length; - this.eiTypeSubject.next(types); - }); - } - - connect(collectionViewer: CollectionViewer): Observable { - const dataMutations = [ - this.eiTypeSubject.asObservable(), - this.sort.sortChange - ]; - return merge(...dataMutations).pipe(map(() => { - return this.getSortedData([...this.eiTypeSubject.getValue()]); - })); - } - - disconnect(collectionViewer: CollectionViewer): void { - this.eiTypeSubject.complete(); - this.loadingSubject.complete(); - } - - private getSortedData(data: EIType[]) { - if (!this.sort.active || this.sort.direction === '') { - return data; - } - - return data.sort((a, b) => { - const isAsc = this.sort.direction === 'asc'; - switch (this.sort.active) { - case 'eiTypeId': return compare(a.description, b.description, isAsc); - default: return 0; - } - }); - } -} - -function compare(a: any, b: any, isAsc: boolean) { - return (a < b ? -1 : 1) * (isAsc ? 1 : -1); -} diff --git a/webapp-frontend/src/app/interfaces/ei.jobs.ts b/webapp-frontend/src/app/interfaces/ei.types.ts similarity index 94% rename from webapp-frontend/src/app/interfaces/ei.jobs.ts rename to webapp-frontend/src/app/interfaces/ei.types.ts index 29c2a8b..2b0d92d 100644 --- a/webapp-frontend/src/app/interfaces/ei.jobs.ts +++ b/webapp-frontend/src/app/interfaces/ei.types.ts @@ -28,11 +28,6 @@ export interface EIJob { owner: string; } -export interface EIType { - id: string; - description: string; -} - export interface EIProducer { ei_producer_id: string; ei_producer_types: string[]; diff --git a/webapp-frontend/src/app/policy-control/policy-control.component.spec.ts b/webapp-frontend/src/app/policy-control/policy-control.component.spec.ts index 1f8275d..a3df258 100644 --- a/webapp-frontend/src/app/policy-control/policy-control.component.spec.ts +++ b/webapp-frontend/src/app/policy-control/policy-control.component.spec.ts @@ -19,9 +19,9 @@ */ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule, MatTableModule } from '@angular/material'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { of } from 'rxjs'; import { NotificationService } from '../services/ui/notification.service'; diff --git a/webapp-frontend/src/app/services/ei/ei.service.spec.ts b/webapp-frontend/src/app/services/ei/ei.service.spec.ts index affa682..9e2584b 100644 --- a/webapp-frontend/src/app/services/ei/ei.service.spec.ts +++ b/webapp-frontend/src/app/services/ei/ei.service.spec.ts @@ -17,18 +17,82 @@ * limitations under the License. * ========================LICENSE_END=================================== */ +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing'; +import { EIJob, EIProducer } from '../../interfaces/ei.types'; import { EIService } from './ei.service'; -import { HttpClientTestingModule } from '@angular/common/http/testing' describe('EIService', () => { + let basePath = 'api/enrichment'; + let service: EIService; + let httpTestingController: HttpTestingController; + beforeEach(() => TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] + imports: [ + HttpClientTestingModule + ], + providers: [ + EIService + ] })); it('should be created', () => { - const service: EIService = TestBed.get(EIService); + service = TestBed.get(EIService); expect(service).toBeTruthy(); }); + + describe('#getEIProducers', () => { + let expectedEIProducers: EIProducer[]; + + beforeEach(() => { + service = TestBed.get(EIService); + httpTestingController = TestBed.get(HttpTestingController); + expectedEIProducers = [ + { ei_producer_id: '1', ei_producer_types: ['EI Type 1'], status: 'ENABLED' }, + { ei_producer_id: '1', ei_producer_types: ['EI Type 1'], status: 'ENABLED' } + ] as EIProducer[]; + }); + + it('should return all producers', () => { + service.getEIProducers().subscribe( + producers => expect(producers).toEqual(expectedEIProducers, 'should return expected EIProducers'), + fail + ); + + const req = httpTestingController.expectOne(basePath + '/' + service.eiProducerPath); + expect(req.request.method).toEqual('GET'); + + req.flush(expectedEIProducers); //Return expectedEITypes + + httpTestingController.verify(); + }); + }); + + describe('#EIJobs', () => { + let expectedEIJobs: EIJob[]; + + beforeEach(() => { + service = TestBed.get(EIService); + httpTestingController = TestBed.get(HttpTestingController); + expectedEIJobs = [ + { ei_job_identity: '1', ei_job_data: 'data', ei_type_identity: 'Type ID 1', target_uri: 'hhtp://url', owner: 'owner'}, + { ei_job_identity: '2', ei_job_data: 'EI Job 2', ei_type_identity: 'Type ID 2', target_uri: 'hhtp://url', owner: 'owner'} + ] as EIJob[]; + }); + + it('should return all jobs', () => { + service.getEIJobs().subscribe( + jobs => expect(jobs).toEqual(expectedEIJobs, 'should return expected Jobs'), + fail + ); + + const req = httpTestingController.expectOne(basePath + '/' + service.eiJobPath); + expect(req.request.method).toEqual('GET'); + + req.flush(expectedEIJobs); //Return expectedEIJobs + + httpTestingController.verify(); + }); + }); }); diff --git a/webapp-frontend/src/app/services/ei/ei.service.ts b/webapp-frontend/src/app/services/ei/ei.service.ts index 976b7b8..d09b5ef 100644 --- a/webapp-frontend/src/app/services/ei/ei.service.ts +++ b/webapp-frontend/src/app/services/ei/ei.service.ts @@ -22,7 +22,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { EIJob, EIType, EIProducer } from '../../interfaces/ei.jobs'; +import { EIJob, EIProducer } from '../../interfaces/ei.types'; import { ControlpanelSuccessTransport } from '../../interfaces/controlpanel.types'; /** @@ -34,9 +34,8 @@ import { ControlpanelSuccessTransport } from '../../interfaces/controlpanel.type export class EIService { private basePath = 'api/enrichment'; - private eiTypePath = 'eitypes'; - private eiJobPath = 'eijobs'; - private eiProducerPath = 'eiproducers'; + eiJobPath = 'eijobs'; + eiProducerPath = 'eiproducers'; private buildPath(...args: any[]) { let result = this.basePath; @@ -50,23 +49,6 @@ export class EIService { // injects to variable httpClient } - /** - * Gets version details - * @returns Observable that should yield a String - */ - getVersion(): Observable { - const url = this.buildPath('version'); - return this.httpClient.get(url).pipe( - // Extract the string here - map(res => res['data']) - ); - } - - getEITypes(): Observable { - const url = this.buildPath(this.eiTypePath); - return this.httpClient.get(url); - } - getEIJobs(): Observable { const url = this.buildPath(this.eiJobPath); return this.httpClient.get(url); -- 2.16.6