Add test coverage to NoTypePolicyInstanceDialog 85/5685/1
authorelinuxhenrik <henrik.b.andersson@est.tech>
Tue, 23 Feb 2021 11:54:22 +0000 (12:54 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Tue, 23 Feb 2021 11:54:26 +0000 (12:54 +0100)
Change-Id: I8b403959ea76b0306824806c1ce551fc0d0ad564
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Issue-ID: NONRTRIC-406

webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts
webapp-frontend/src/app/interceptor.mock.ts
webapp-frontend/src/app/interfaces/ric.ts
webapp-frontend/src/app/mock/ric2.json
webapp-frontend/src/app/mock/rics.json [deleted file]
webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.html
webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.spec.ts [new file with mode: 0644]
webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.ts
webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html
webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts
webapp-frontend/src/app/services/policy/policy.service.ts

index b1007c2..9a47b4b 100644 (file)
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { 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 {HarnessLoader} from '@angular/cdk/testing';
+import { HarnessLoader } from '@angular/cdk/testing';
 import { MatIconModule } from '@angular/material/icon';
 import { MatInputHarness } from '@angular/material/input/testing'
 import { MatTableModule } from '@angular/material/table';
index 9c31ed8..72423ca 100644 (file)
@@ -36,7 +36,6 @@ import * as eiproducerstatus2 from './mock/ei-producer-status2.json';
 import * as policytypesList from './mock/policy-types.json';
 import * as policytypes1 from './mock/policy-type1.json';
 import * as policyinstanceedit from './mock/policy-instance-edit.json';
-import * as rics from './mock/rics.json';
 import * as ric1 from './mock/ric1.json';
 import * as ric2 from './mock/ric2.json';
 
@@ -105,14 +104,6 @@ const urls = [
         url: '/a1-policy/v2/rics?policytype_id=',
         json: ric2
     },
-    {
-        url: 'api/policy/rics?policyType=1',
-        json: rics
-    },
-    {
-        url: 'api/policy/rics?policyType=2',
-        json: rics
-    },
     {
         url: '/ei-producer/v1/eiproducers',
         json: eiProducerIds
index 7c115af..4ee2b15 100644 (file)
 
 // Models of data used by the Policy Control
 
+export interface Rics {
+  rics: Ric[]
+}
+
 export interface Ric {
   ric_id: string;
   managed_element_ids: any[];
index f6e36e8..c320f48 100644 (file)
                 ""
             ],
             "state": "AVAILABLE"
+        },
+        {
+            "ric_id": "ric3",
+            "managed_element_ids": [
+                "kista_1",
+                "kista_2"
+            ],
+            "policytype_ids": [
+                ""
+            ],
+            "state": "AVAILABLE"
         }
     ]
 }
\ No newline at end of file
diff --git a/webapp-frontend/src/app/mock/rics.json b/webapp-frontend/src/app/mock/rics.json
deleted file mode 100644 (file)
index 0fa4446..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[
-    "ric1",
-    "ric2"
-]
\ No newline at end of file
index 5605271..a24264f 100644 (file)
@@ -19,7 +19,7 @@
   -->
 
 <div class="text-muted logo" fxLayout="row" fxLayoutGap="50px" fxLayoutAlign="space-around center">
-    <div *ngIf="policyInstanceId">[{{this.ric}}] Instance ID: {{policyInstanceId}}</div>
+    <div id="instanceInfo" *ngIf="policyInstanceId">[{{this.ric}}] Instance ID: {{policyInstanceId}}</div>
 </div>
 <div class="mat-elevation-z8 header row" [ngClass]="{'header-dark': darkMode}">
     <div class="logo">
@@ -27,8 +27,7 @@
         <svg class="logo__icon" viewBox="150.3 22.2 1000 50">
             <text class="logo__text" [ngClass]="{'logo__text-dark': darkMode}" font-size="30" font-weight="600"
                 letter-spacing=".1em" transform="translate(149 56)">
-                <tspan *ngIf="!this.policyInstanceId">Create new policy instance of </tspan>
-                <tspan>< No type></tspan>
+                <tspan *ngIf="!this.policyInstanceId">Create new policy instance of &lt; No type &gt;</tspan>
             </text>
         </svg>
     </div>
 
     <mat-card class="card" [ngClass]="{'card-dark': darkMode}">
         <mat-form-field *ngIf="!this.policyInstanceId" appearance="fill">
-            <mat-select id="ricSelector" formControlName="ricSelector" matInput required [(value)]="this.ric"
+            <mat-select id="ricSelector" formControlName="ricSelector" matInput required [value]="this.ric"
                 placeholder="Target"
                 matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
-                <mat-option *ngFor="let ric of this.allRics.rics" [value]="ric">
-                    {{ric.ric_id}}
+                <mat-option *ngFor="let ric of this.allRicIds" [value]="ric">
+                    {{ric}}
                 </mat-option>
             </mat-select>
             <div *ngIf="ricSelector.invalid && (ricSelector.dirty || ricSelector.touched)" class="alert mat-error"
             </div>
         </mat-form-field>
         <div mat-dialog-actions>
-            <button (click)="formatJsonInput();" class="mat-raised-button" [disabled]="!policyJsonTextArea.valid">
+            <button id="formatButton" (click)="formatJsonInput();" mat-raised-button [disabled]="!policyJsonTextArea.valid">
                 Format JSON
             </button>
-            <button mat-raised-button class="mat-raised-button" [mat-dialog-close]="false">Close</button>
-            <button mat-raised-button (click)="this.onSubmit()" class="submitBtn" [disabled]="!instanceForm.valid">
+            <button id="closeButton"  mat-raised-button [mat-dialog-close]="false">Close</button>
+            <button id="submitButton" mat-raised-button (click)="this.onSubmit()" class="submitBtn" [disabled]="!instanceForm.valid">
                 Submit
             </button>
         </div>
diff --git a/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.spec.ts b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.spec.ts
new file mode 100644 (file)
index 0000000..3a940c7
--- /dev/null
@@ -0,0 +1,188 @@
+/*-
+ * ========================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 { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { HarnessLoader } from "@angular/cdk/testing";
+import { MatButtonModule } from '@angular/material/button';
+import { MatButtonHarness } from '@angular/material/button/testing';
+import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSelectHarness } from '@angular/material/select/testing';
+import { MatInputModule } from '@angular/material/input';
+import { MatInputHarness } from '@angular/material/input/testing';
+import { of } from "rxjs/observable/of";
+import { ReactiveFormsModule } from "@angular/forms";
+import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
+import { ToastrModule } from "ngx-toastr";
+
+import { PolicyService } from "../services/policy/policy.service";
+import { ErrorDialogService } from "../services/ui/error-dialog.service";
+import { UiService } from "../services/ui/ui.service";
+import { NoTypePolicyInstanceDialogComponent } from "./no-type-policy-instance-dialog.component";
+import { Ric } from "../interfaces/ric";
+
+describe('NoTypePolicyInstanceDialogComponent', () => {
+  let component: NoTypePolicyInstanceDialogComponent;
+  let fixture: ComponentFixture<NoTypePolicyInstanceDialogComponent>;
+  let loader: HarnessLoader;
+  let policyServiceSpy: jasmine.SpyObj<PolicyService>;
+  let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
+  const ric1: Ric = { ric_id: 'ric1', managed_element_ids: [ 'me1' ], policytype_ids: [ 'type1' ], state: ''};
+  const ric2: Ric = { ric_id: 'ric2', managed_element_ids: [ 'me1' ], policytype_ids: [ 'type1' ], state: ''};
+
+  beforeEach(async () => {
+    policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'putPolicy', 'getRics' ]);
+    errDialogServiceSpy = jasmine.createSpyObj('ErrorDialogService', [ 'displayError' ]);
+
+    policyServiceSpy.getRics.and.returnValue(of({ rics: [ ric1, ric2 ] }));
+
+    TestBed.configureTestingModule({
+      imports: [
+        BrowserAnimationsModule,
+        MatButtonModule,
+        MatDialogModule,
+        MatInputModule,
+        MatSelectModule,
+        ReactiveFormsModule,
+        ToastrModule.forRoot()
+      ],
+      declarations: [
+        NoTypePolicyInstanceDialogComponent
+      ],
+      providers: [
+        { provide: MatDialogRef, useValue: component },
+        { provide: PolicyService, useValue: policyServiceSpy },
+        { provide: ErrorDialogService, useValue: errDialogServiceSpy },
+        { provide: MAT_DIALOG_DATA, useValue: true },
+        UiService
+      ]
+    });
+  });
+
+  describe('content when creating policy', () => {
+    beforeEach(async () => {
+      ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader));
+    });
+
+    it('should contain oran logo and create title and no instance info', async () => {
+      let ele = fixture.debugElement.nativeElement.querySelector('img');
+      expect(ele.src).toContain('assets/oran-logo.png');
+
+      ele = fixture.debugElement.nativeElement.querySelector('text');
+      expect(ele.childNodes[0].childNodes[0].textContent).toEqual('Create new policy instance of < No type >');
+
+      ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo');
+      expect(ele).toBeFalsy();
+    });
+
+    it('should contain enabled Target selection with no ric selected and json should be empty', async () => {
+      let ricSelector: MatSelectHarness = await loader.getHarness(MatSelectHarness.with({selector: '#ricSelector'}));
+
+      expect(await ricSelector.isEmpty()).toBeTruthy();
+      expect(await ricSelector.isDisabled()).toBeFalsy();
+      await ricSelector.open();
+      const count = (await ricSelector.getOptions()).length;
+      expect(count).toEqual(2);
+
+      let jsonTextArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({selector: '#policyJsonTextArea'}));
+      expect(await jsonTextArea.isDisabled()).toBeFalsy();
+      const actualJson: string = await jsonTextArea.getValue();
+      expect(actualJson).toEqual('');
+    });
+
+    it('should contain disabled Format and Submit buttons and enabled Close button', async () => {
+      component.ngOnInit();
+
+      let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#formatButton'}));
+      expect(await formatButton.isDisabled()).toBeTruthy();
+      expect(await formatButton.getText()).toEqual('Format JSON');
+
+      let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#closeButton'}));
+      expect(await closeButton.isDisabled()).toBeFalsy();
+      expect(await closeButton.getText()).toEqual('Close');
+
+      let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#submitButton'}));
+      expect(await submitButton.isDisabled()).toBeTruthy();
+      expect(await submitButton.getText()).toEqual('Submit');
+    });
+  });
+
+  describe('content when editing policy', () => {
+    beforeEach(async () => {
+      const policyData = {
+        createSchema: "{}",
+        instanceId: "instanceId",
+        instanceJson: '{"qosObjectives": {"priorityLevel": 3100}}',
+        name: "name",
+        ric: "ric1"
+    };
+      TestBed.overrideProvider(MAT_DIALOG_DATA, {useValue: policyData }); // Should be provided with a policy
+      ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader));
+    });
+
+    it('should contain oran logo and instance info', async () => {
+        let ele = fixture.debugElement.nativeElement.querySelector('img');
+        expect(ele.src).toContain('assets/oran-logo.png');
+
+        ele = fixture.debugElement.nativeElement.querySelector('text');
+        expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
+
+        ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo');
+        expect(ele).toBeTruthy();
+        expect(ele.innerText).toEqual('[ric1] Instance ID: instanceId');
+    });
+
+    it('should contain json and no Target selection', async () => {
+        let ele = fixture.debugElement.nativeElement.querySelector('#ricSelector');
+        expect(ele).toBeFalsy();
+
+        let jsonTextArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({selector: '#policyJsonTextArea'}));
+        expect(await jsonTextArea.isDisabled()).toBeFalsy();
+        const actualJson: string = await jsonTextArea.getValue();
+        expect(actualJson).toContain('qosObjectives');
+    });
+
+    it('should contain enabled Format, Submit and Close buttons', async () => {
+      let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#formatButton'}));
+      expect(await formatButton.isDisabled()).toBeFalsy();
+      expect(await formatButton.getText()).toEqual('Format JSON');
+
+      let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#closeButton'}));
+      expect(await closeButton.isDisabled()).toBeFalsy();
+      expect(await closeButton.getText()).toEqual('Close');
+
+      let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#submitButton'}));
+      expect(await submitButton.isDisabled()).toBeFalsy();
+      expect(await submitButton.getText()).toEqual('Submit');
+    });
+
+  });
+});
+
+function compileAndGetComponents(fixture: ComponentFixture<NoTypePolicyInstanceDialogComponent>, component: NoTypePolicyInstanceDialogComponent, loader: HarnessLoader) {
+  TestBed.compileComponents();
+
+  fixture = TestBed.createComponent(NoTypePolicyInstanceDialogComponent);
+  component = fixture.componentInstance;
+  fixture.detectChanges();
+  loader = TestbedHarnessEnvironment.loader(fixture);
+  return { fixture, component, loader };
+}
index 288ba2e..1d525c2 100644 (file)
  */
 import { Component, Inject, OnInit } from '@angular/core';
 import { FormControl, FormGroup, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
-import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { PolicyService } from '../services/policy/policy.service';
 import { NotificationService } from '../services/ui/notification.service';
 import { UiService } from '../services/ui/ui.service';
 import { HttpErrorResponse } from '@angular/common/http';
 import { ErrorDialogService } from '../services/ui/error-dialog.service';
 import * as uuid from 'uuid';
-import { Ric } from '../interfaces/ric';
+import { Rics } from '../interfaces/ric';
 import { CreatePolicyInstance } from '../interfaces/policy.types';
 
 @Component({
@@ -41,9 +41,10 @@ export class NoTypePolicyInstanceDialogComponent implements OnInit {
   policyJson: string;
   darkMode: boolean;
   ric: string;
-  allRics: Ric[];
+  allRicIds: string[] = [];
 
   constructor(
+    public dialogRef: MatDialogRef<NoTypePolicyInstanceDialogComponent>,
     private policySvc: PolicyService,
     private errorService: ErrorDialogService,
     private notificationService: NotificationService,
@@ -68,7 +69,7 @@ export class NoTypePolicyInstanceDialogComponent implements OnInit {
       ])
     });
     if (!this.policyInstanceId) {
-      this.fetchRics();
+      this.getRicIds();
     }
   }
 
@@ -81,11 +82,12 @@ export class NoTypePolicyInstanceDialogComponent implements OnInit {
       this.policyInstanceId = uuid.v4();
     }
     const self: NoTypePolicyInstanceDialogComponent = this;
-    let createPolicyInstance = this.createPolicyInstance(this.policyJsonTextArea.value);
+    let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(this.policyJsonTextArea.value);
     this.policySvc.putPolicy(createPolicyInstance).subscribe(
       {
         next(_) {
           self.notificationService.success('Policy without type:' + self.policyInstanceId + ' submitted');
+          self.dialogRef.close();
         },
         error(error: HttpErrorResponse) {
           self.errorService.displayError('Submit failed: ' + error.error);
@@ -94,7 +96,7 @@ export class NoTypePolicyInstanceDialogComponent implements OnInit {
       });
   }
 
-  private createPolicyInstance(policyJson: string) {
+  private createPolicyInstance(policyJson: string): CreatePolicyInstance {
     let createPolicyInstance = {} as CreatePolicyInstance;
     createPolicyInstance.policy_data = JSON.parse(policyJson);
     createPolicyInstance.policy_id = this.policyInstanceId;
@@ -104,22 +106,19 @@ export class NoTypePolicyInstanceDialogComponent implements OnInit {
     return createPolicyInstance;
   }
 
-  private fetchRics() {
+  getRicIds() {
     const self: NoTypePolicyInstanceDialogComponent = this;
     this.policySvc.getRics('').subscribe(
       {
-        next(value: Ric[]) {
-          self.allRics = value;
-          console.log(value);
-        },
-        error(error: HttpErrorResponse) {
-          self.errorService.displayError('Fetching of rics failed: ' + error.message);
-        },
-        complete() { }
+        next(value: Rics) {
+          value.rics.forEach(ric => {
+            self.allRicIds.push(ric.ric_id);
+          });
+        }
       });
   }
 
-  private formatJsonString(jsonToFormat: any) {
+  private formatJsonString(jsonToFormat: any): string {
     return JSON.stringify(jsonToFormat, null, 2);
   }
 
@@ -135,11 +134,13 @@ export function jsonValidator(): ValidatorFn {
   };
 }
 
-export function isJsonValid(json: string) {
+export function isJsonValid(json: string): boolean {
   try {
     if (json != null) {
       JSON.parse(json);
       return true;
+    } else {
+      return false;
     }
   } catch (jsonError) {
     return false;
index a533ed0..76a9ae0 100644 (file)
@@ -45,7 +45,7 @@
             <mat-select id="ricSelector" formControlName="ricSelector" matInput required [(value)]="this.ric"
                 placeholder="Target"
                 matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
-                <mat-option *ngFor="let ric of this.allRics.rics" [value]="ric">
+                <mat-option *ngFor="let ric of this.allRics" [value]="ric">
                     {{ric.ric_id}}
                 </mat-option>
             </mat-select>
index 70c1057..eaaeff5 100644 (file)
@@ -31,7 +31,7 @@ import { UiService } from '../services/ui/ui.service';
 import { HttpErrorResponse } from '@angular/common/http';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
 import { ChangeDetectorRef } from '@angular/core';
-import { Ric } from '../interfaces/ric';
+import { Ric, Rics } from '../interfaces/ric';
 
 
 @Component({
@@ -93,14 +93,10 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
         const self: PolicyInstanceDialogComponent = this;
         this.dataService.getRics(this.policyTypeName).subscribe(
             {
-                next(value: Ric[]) {
-                    self.allRics = value;
+                next(value: Rics) {
+                    self.allRics = value.rics;
                     console.log(value);
-                },
-                error(error: HttpErrorResponse) {
-                    self.errorService.displayError('Fetching of rics failed: ' + error.message);
-                },
-                complete() { }
+                }
             });
     }
 
@@ -148,12 +144,13 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
         }
         const policyJson: string = this.prettyLiveFormData;
         const self: PolicyInstanceDialogComponent = this;
-        let createPolicyInstance = this.createPolicyInstance(policyJson);
+        let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(policyJson);
         this.dataService.putPolicy(createPolicyInstance).subscribe(
             {
                 next(_) {
                     self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId +
                         ' submitted');
+                    self.dialogRef.close();
                 },
                 error(error: HttpErrorResponse) {
                     self.errorService.displayError('Submit failed: ' + error.error);
index 5ffff13..72375e8 100644 (file)
@@ -24,7 +24,7 @@ import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { CreatePolicyInstance, PolicyInstance, PolicyInstanceAck, PolicyInstances, PolicyStatus, PolicyType, PolicyTypes } from '../../interfaces/policy.types';
 import { ControlpanelSuccessTransport } from '../../interfaces/controlpanel.types';
-import { Ric } from 'src/app/interfaces/ric';
+import { Ric, Rics } from 'src/app/interfaces/ric';
 
 /**
  * Services for calling the policy endpoints.
@@ -109,7 +109,7 @@ export class PolicyService {
     }
 
 
-    getRics(policyTypeId: string): Observable<Ric[]> {
+    getRics(policyTypeId: string): Observable<Rics> {
         const url = this.buildPath('rics') + '?policytype_id=' + policyTypeId;
         return this.httpClient.get<any>(url);
     }