Merge "Fix PolicyControlComponent"
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / policy / policy-instance-dialog / policy-instance-dialog.component.spec.ts
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2019 Nordix Foundation
6  * %%
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20
21 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
22 import { By } from "@angular/platform-browser";
23 import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
24 import { ComponentFixture, TestBed } from "@angular/core/testing";
25 import { HarnessLoader } from "@angular/cdk/testing";
26 import { MatButtonModule } from "@angular/material/button";
27 import { MatButtonHarness } from "@angular/material/button/testing";
28 import {
29   MatDialogModule,
30   MatDialogRef,
31   MAT_DIALOG_DATA,
32 } from "@angular/material/dialog";
33 import { MatSelectModule } from "@angular/material/select";
34 import { MatInputModule } from "@angular/material/input";
35 import { of } from "rxjs";
36 import { ReactiveFormsModule } from "@angular/forms";
37 import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
38 import { ToastrModule } from "ngx-toastr";
39 import { MockComponent } from "ng-mocks";
40
41 import { PolicyService } from "@services/policy/policy.service";
42 import { ErrorDialogService } from "@services/ui/error-dialog.service";
43 import { UiService } from "@services/ui/ui.service";
44 import { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component";
45 import { TypedPolicyEditorComponent } from "@policy/typed-policy-editor/typed-policy-editor.component";
46 import { RicSelectorComponent } from "@policy/ric-selector/ric-selector.component";
47 import { NoTypePolicyEditorComponent } from "@policy/no-type-policy-editor/no-type-policy-editor.component";
48 import { CreatePolicyInstance } from "@interfaces/policy.types";
49 import { NotificationService } from "@services/ui/notification.service";
50 import * as uuid from "uuid";
51
52 describe("PolicyInstanceDialogComponent", () => {
53   const untypedSchema = JSON.parse("{}");
54   const typedSchema =
55     JSON.parse('{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}');
56
57   let component: PolicyInstanceDialogComponent;
58   let fixture: ComponentFixture<PolicyInstanceDialogComponent>;
59   let loader: HarnessLoader;
60   let dialogRefSpy: MatDialogRef<PolicyInstanceDialogComponent>;
61   let policyServiceSpy: jasmine.SpyObj<PolicyService>;
62   let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
63   let notificationServiceSpy: NotificationService;
64
65   beforeEach(async () => {
66     dialogRefSpy = jasmine.createSpyObj("MatDialogRef", ["close"]);
67     policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]);
68     errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [
69       "displayError",
70     ]);
71     notificationServiceSpy = jasmine.createSpyObj("NotificationService", [
72       "success",
73     ]);
74
75     TestBed.configureTestingModule({
76       imports: [
77         BrowserAnimationsModule,
78         MatButtonModule,
79         MatDialogModule,
80         MatInputModule,
81         MatSelectModule,
82         ReactiveFormsModule,
83         ToastrModule.forRoot(),
84       ],
85       schemas: [CUSTOM_ELEMENTS_SCHEMA],
86       declarations: [
87         PolicyInstanceDialogComponent,
88         MockComponent(RicSelectorComponent),
89         MockComponent(NoTypePolicyEditorComponent),
90         MockComponent(TypedPolicyEditorComponent),
91       ],
92       providers: [
93         ChangeDetectorRef,
94         { provide: MatDialogRef, useValue: dialogRefSpy },
95         { provide: PolicyService, useValue: policyServiceSpy },
96         { provide: ErrorDialogService, useValue: errDialogServiceSpy },
97         { provide: NotificationService, useValue: notificationServiceSpy },
98         { provide: MAT_DIALOG_DATA, useValue: true },
99         UiService,
100       ],
101     });
102   });
103
104   it("should set correct dark mode from UIService", () => {
105     const policyData = {
106       createSchema: untypedSchema,
107     };
108     TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
109     ({ fixture, component, loader } = compileAndGetComponents(
110       fixture,
111       component,
112       loader
113     ));
114     const uiService: UiService = TestBed.inject(UiService);
115     expect(component.darkMode).toBeTruthy();
116
117     uiService.darkModeState.next(false);
118     fixture.detectChanges();
119     expect(component.darkMode).toBeFalsy();
120   });
121
122   describe("creating policy without type", () => {
123     beforeEach(async () => {
124       const policyData = {
125         createSchema: untypedSchema,
126       };
127       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
128       ({ fixture, component, loader } = compileAndGetComponents(
129         fixture,
130         component,
131         loader
132       ));
133       jasmine.addCustomEqualityTester(policyTester);
134     });
135
136     it("should contain oran logo and create title and no instance info", async () => {
137       let ele = fixture.debugElement.nativeElement.querySelector("img");
138       expect(ele.src).toContain("assets/oran-logo.png");
139
140       ele = fixture.debugElement.nativeElement.querySelector("text");
141       expect(ele.textContent).toEqual(
142         "Create new policy instance of type < No Type >"
143       );
144
145       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
146       expect(ele).toBeFalsy();
147     });
148
149     it("should contain ric select with no policy type and no ric selected", async () => {
150       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
151         By.directive(RicSelectorComponent)
152       ).componentInstance;
153       expect(ricSelector).toBeTruthy();
154       expect(ricSelector.policyTypeName).toBeFalsy();
155       expect(component.policyInstance.ric_id).toBeFalsy();
156     });
157
158     it("should contain json editor with no JSON", async () => {
159       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
160         By.directive(NoTypePolicyEditorComponent)
161       ).componentInstance;
162       expect(noTypePolicyEditor).toBeTruthy();
163       expect(noTypePolicyEditor.policyJson).toBeFalsy();
164     });
165
166     it("should contain enabled Close button and Submit button", async () => {
167       component.ngOnInit();
168
169       let closeButton: MatButtonHarness = await loader.getHarness(
170         MatButtonHarness.with({ selector: "#closeButton" })
171       );
172       expect(await closeButton.isDisabled()).toBeFalsy();
173       expect(await closeButton.getText()).toEqual("Close");
174
175       let submitButton: MatButtonHarness = await loader.getHarness(
176         MatButtonHarness.with({ selector: "#submitButton" })
177       );
178       expect(await submitButton.getText()).toEqual("Submit");
179     });
180
181     it("should enable Submit button when ric is selected and json is valid", async () => {
182       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
183         By.directive(RicSelectorComponent)
184       ).componentInstance;
185       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
186         By.directive(NoTypePolicyEditorComponent)
187       ).componentInstance;
188       let submitButton: MatButtonHarness = await loader.getHarness(
189         MatButtonHarness.with({ selector: "#submitButton" })
190       );
191
192       noTypePolicyEditor.validJson.emit(null);
193       expect(await submitButton.isDisabled()).toBeTruthy();
194
195       ricSelector.selectedRic.emit("ric1");
196       expect(await submitButton.isDisabled()).toBeTruthy();
197
198       noTypePolicyEditor.validJson.emit("{}");
199       expect(await submitButton.isDisabled()).toBeFalsy();
200     });
201
202     it("should generate policy ID when submitting new policy", async () => {
203       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
204         By.directive(RicSelectorComponent)
205       ).componentInstance;
206       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
207         By.directive(NoTypePolicyEditorComponent)
208       ).componentInstance;
209       let submitButton: MatButtonHarness = await loader.getHarness(
210         MatButtonHarness.with({ selector: "#submitButton" })
211       );
212
213       spyOn(uuid, "v4").and.returnValue("1234567890");
214       ricSelector.selectedRic.emit("ric1");
215       noTypePolicyEditor.validJson.emit("{}");
216       await submitButton.click();
217
218       const policyInstance = {} as CreatePolicyInstance;
219       policyInstance.policy_data = JSON.parse("{}");
220       policyInstance.policy_id = "1234567890";
221       policyInstance.ric_id = "ric1";
222       policyInstance.service_id = "controlpanel";
223       expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
224     });
225   });
226
227   describe("content when creating policy with type", () => {
228     beforeEach(async () => {
229       const policyData = {
230         name: "Type 1",
231         createSchema: typedSchema,
232       };
233       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
234       ({ fixture, component, loader } = compileAndGetComponents(
235         fixture,
236         component,
237         loader
238       ));
239     });
240
241     it("should contain oran logo and create title and no instance info", async () => {
242       let ele = fixture.debugElement.nativeElement.querySelector("img");
243       expect(ele.src).toContain("assets/oran-logo.png");
244
245       ele = fixture.debugElement.nativeElement.querySelector("text");
246       expect(ele.textContent).toEqual(
247         "Create new policy instance of type Type 1"
248       );
249
250       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
251       expect(ele).toBeFalsy();
252     });
253
254     it("should contain ric select with provided policy type", async () => {
255       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
256         By.directive(RicSelectorComponent)
257       ).componentInstance;
258       expect(ricSelector).toBeTruthy();
259       expect(ricSelector.policyTypeName).toEqual("Type 1");
260     });
261
262     it("should contain typed json editor with empty JSON, schema and dark mode true", async () => {
263       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
264         By.directive(TypedPolicyEditorComponent)
265       ).componentInstance;
266       expect(typedPolicyEditor).toBeTruthy();
267       expect(typedPolicyEditor.jsonObject).toBeFalsy();
268       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
269       expect(typedPolicyEditor.darkMode).toBeTruthy();
270     });
271
272     it("should contain enabled Close button and Submit button", async () => {
273       component.ngOnInit();
274
275       let closeButton: MatButtonHarness = await loader.getHarness(
276         MatButtonHarness.with({ selector: "#closeButton" })
277       );
278       expect(await closeButton.isDisabled()).toBeFalsy();
279       expect(await closeButton.getText()).toEqual("Close");
280
281       let submitButton: MatButtonHarness = await loader.getHarness(
282         MatButtonHarness.with({ selector: "#submitButton" })
283       );
284       expect(await submitButton.getText()).toEqual("Submit");
285     });
286
287     it("should enable Submit button when ric is selected and json is valid", async () => {
288       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
289         By.directive(RicSelectorComponent)
290       ).componentInstance;
291       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
292         By.directive(TypedPolicyEditorComponent)
293       ).componentInstance;
294       let submitButton: MatButtonHarness = await loader.getHarness(
295         MatButtonHarness.with({ selector: "#submitButton" })
296       );
297
298       typedPolicyEditor.validJson.emit(null);
299       expect(await submitButton.isDisabled()).toBeTruthy();
300
301       ricSelector.selectedRic.emit("ric1");
302       expect(await submitButton.isDisabled()).toBeTruthy();
303
304       typedPolicyEditor.validJson.emit("{}");
305       expect(await submitButton.isDisabled()).toBeFalsy();
306     });
307   });
308
309   describe("content when editing policy without type", () => {
310     const instanceJson = JSON.parse('{"qosObjectives": {"priorityLevel": 3100}}');
311     beforeEach(async () => {
312       const policyData = {
313         createSchema: untypedSchema,
314         instanceId: "instanceId",
315         instanceJson: instanceJson,
316         ric: "ric1",
317       };
318       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
319       ({ fixture, component, loader } = compileAndGetComponents(
320         fixture,
321         component,
322         loader
323       ));
324       jasmine.addCustomEqualityTester(policyTester);
325     });
326
327     it("should contain oran logo and instance info", async () => {
328       let ele = fixture.debugElement.nativeElement.querySelector("img");
329       expect(ele.src).toContain("assets/oran-logo.png");
330
331       ele = fixture.debugElement.nativeElement.querySelector("text");
332       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
333
334       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
335       expect(ele).toBeTruthy();
336       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
337     });
338
339     it("should not contain ric select", async () => {
340       const ricSelector = fixture.debugElement.query(
341         By.directive(RicSelectorComponent)
342       );
343       expect(ricSelector).toBeFalsy();
344     });
345
346     it("should contain json editor with json data", async () => {
347       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
348         By.directive(NoTypePolicyEditorComponent)
349       ).componentInstance;
350       expect(noTypePolicyEditor).toBeTruthy();
351       expect(noTypePolicyEditor.policyJson).toEqual(
352         instanceJson
353       );
354     });
355
356     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
357       let closeButton: MatButtonHarness = await loader.getHarness(
358         MatButtonHarness.with({ selector: "#closeButton" })
359       );
360       expect(await closeButton.isDisabled()).toBeFalsy();
361       expect(await closeButton.getText()).toEqual("Close");
362
363       let submitButton: MatButtonHarness = await loader.getHarness(
364         MatButtonHarness.with({ selector: "#submitButton" })
365       );
366       expect(await submitButton.isDisabled()).toBeFalsy();
367       expect(await submitButton.getText()).toEqual("Submit");
368     });
369
370     it("should submit policy with correct data, close dialog and notify user about success", async () => {
371       policyServiceSpy.putPolicy.and.returnValue(of("ok"));
372       let submitButton: MatButtonHarness = await loader.getHarness(
373         MatButtonHarness.with({ selector: "#submitButton" })
374       );
375
376       await submitButton.click();
377
378       const policyInstance = {} as CreatePolicyInstance;
379       policyInstance.policy_data = instanceJson;
380       policyInstance.policy_id = "instanceId";
381       policyInstance.ric_id = "ric1";
382       policyInstance.service_id = "controlpanel";
383       expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
384
385       expect(dialogRefSpy.close).toHaveBeenCalled();
386       expect(notificationServiceSpy.success).toHaveBeenCalledWith(
387         "Policy instanceId submitted"
388       );
389     });
390   });
391
392   describe("content when editing policy with type", () => {
393     const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
394     beforeEach(async () => {
395       const policyData = {
396         createSchema: typedSchema,
397         instanceId: "instanceId",
398         instanceJson: instanceJson,
399         name: "name",
400         ric: "ric1",
401       };
402       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
403       ({ fixture, component, loader } = compileAndGetComponents(
404         fixture,
405         component,
406         loader
407       ));
408     });
409
410     it("should contain oran logo and instance info", async () => {
411       let ele = fixture.debugElement.nativeElement.querySelector("img");
412       expect(ele.src).toContain("assets/oran-logo.png");
413
414       ele = fixture.debugElement.nativeElement.querySelector("text");
415       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
416
417       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
418       expect(ele).toBeTruthy();
419       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
420     });
421
422     it("should not contain ric select", async () => {
423       const ricSelector = fixture.debugElement.query(
424         By.directive(RicSelectorComponent)
425       );
426       expect(ricSelector).toBeFalsy();
427     });
428
429     it("should contain typed json editor with instance JSON, schema and dark mode true", async () => {
430       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
431         By.directive(TypedPolicyEditorComponent)
432       ).componentInstance;
433       expect(typedPolicyEditor).toBeTruthy();
434       expect(unescapeQuotes(typedPolicyEditor.jsonObject)).toEqual(
435         instanceJson
436       );
437       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
438       expect(typedPolicyEditor.darkMode).toBeTruthy();
439     });
440
441     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
442       let closeButton: MatButtonHarness = await loader.getHarness(
443         MatButtonHarness.with({ selector: "#closeButton" })
444       );
445       expect(await closeButton.isDisabled()).toBeFalsy();
446       expect(await closeButton.getText()).toEqual("Close");
447
448       let submitButton: MatButtonHarness = await loader.getHarness(
449         MatButtonHarness.with({ selector: "#submitButton" })
450       );
451       expect(await submitButton.isDisabled()).toBeFalsy();
452       expect(await submitButton.getText()).toEqual("Submit");
453     });
454   });
455 });
456
457 function compileAndGetComponents(
458   fixture: ComponentFixture<PolicyInstanceDialogComponent>,
459   component: PolicyInstanceDialogComponent,
460   loader: HarnessLoader
461 ) {
462   TestBed.compileComponents();
463
464   fixture = TestBed.createComponent(PolicyInstanceDialogComponent);
465   component = fixture.componentInstance;
466   fixture.detectChanges();
467   loader = TestbedHarnessEnvironment.loader(fixture);
468   return { fixture, component, loader };
469 }
470
471 function unescapeQuotes(string: string): string {
472   return string.replace(/\\"/g, '"');
473 }
474
475 function policyTester(first, second) {
476   if (typeof first[0] === "object" && typeof second[0] === "object") {
477     const policy1 = first[0] as CreatePolicyInstance;
478     const policy2 = second[0] as CreatePolicyInstance;
479     return (
480       typeof policy1.policy_data === "object" &&
481       typeof policy2.policy_data === "object" &&
482       JSON.stringify(policy1.policy_data) === JSON.stringify(policy2.policy_data) &&
483       policy1.policy_id === policy2.policy_id &&
484       policy1.policytype_id === policy2.policytype_id &&
485       policy1.ric_id === policy2.ric_id &&
486       policy1.service_id === policy2.service_id
487     );
488   }
489 }