Fix create policy without type
[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.policytype_id = "";
222       policyInstance.ric_id = "ric1";
223       policyInstance.service_id = "controlpanel";
224       expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
225     });
226   });
227
228   describe("content when creating policy with type", () => {
229     beforeEach(async () => {
230       const policyData = {
231         name: "Type 1",
232         createSchema: typedSchema,
233       };
234       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
235       ({ fixture, component, loader } = compileAndGetComponents(
236         fixture,
237         component,
238         loader
239       ));
240     });
241
242     it("should contain oran logo and create title and no instance info", async () => {
243       let ele = fixture.debugElement.nativeElement.querySelector("img");
244       expect(ele.src).toContain("assets/oran-logo.png");
245
246       ele = fixture.debugElement.nativeElement.querySelector("text");
247       expect(ele.textContent).toEqual(
248         "Create new policy instance of type Type 1"
249       );
250
251       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
252       expect(ele).toBeFalsy();
253     });
254
255     it("should contain ric select with provided policy type", async () => {
256       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
257         By.directive(RicSelectorComponent)
258       ).componentInstance;
259       expect(ricSelector).toBeTruthy();
260       expect(ricSelector.policyTypeName).toEqual("Type 1");
261     });
262
263     it("should contain typed json editor with empty JSON, schema and dark mode true", async () => {
264       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
265         By.directive(TypedPolicyEditorComponent)
266       ).componentInstance;
267       expect(typedPolicyEditor).toBeTruthy();
268       expect(typedPolicyEditor.jsonObject).toBeFalsy();
269       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
270       expect(typedPolicyEditor.darkMode).toBeTruthy();
271     });
272
273     it("should contain enabled Close button and Submit button", async () => {
274       component.ngOnInit();
275
276       let closeButton: MatButtonHarness = await loader.getHarness(
277         MatButtonHarness.with({ selector: "#closeButton" })
278       );
279       expect(await closeButton.isDisabled()).toBeFalsy();
280       expect(await closeButton.getText()).toEqual("Close");
281
282       let submitButton: MatButtonHarness = await loader.getHarness(
283         MatButtonHarness.with({ selector: "#submitButton" })
284       );
285       expect(await submitButton.getText()).toEqual("Submit");
286     });
287
288     it("should enable Submit button when ric is selected and json is valid", async () => {
289       const ricSelector: RicSelectorComponent = fixture.debugElement.query(
290         By.directive(RicSelectorComponent)
291       ).componentInstance;
292       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
293         By.directive(TypedPolicyEditorComponent)
294       ).componentInstance;
295       let submitButton: MatButtonHarness = await loader.getHarness(
296         MatButtonHarness.with({ selector: "#submitButton" })
297       );
298
299       typedPolicyEditor.validJson.emit(null);
300       expect(await submitButton.isDisabled()).toBeTruthy();
301
302       ricSelector.selectedRic.emit("ric1");
303       expect(await submitButton.isDisabled()).toBeTruthy();
304
305       typedPolicyEditor.validJson.emit("{}");
306       expect(await submitButton.isDisabled()).toBeFalsy();
307     });
308   });
309
310   describe("content when editing policy without type", () => {
311     const instanceJson = JSON.parse('{"qosObjectives": {"priorityLevel": 3100}}');
312     beforeEach(async () => {
313       const policyData = {
314         createSchema: untypedSchema,
315         instanceId: "instanceId",
316         instanceJson: instanceJson,
317         ric: "ric1",
318       };
319       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
320       ({ fixture, component, loader } = compileAndGetComponents(
321         fixture,
322         component,
323         loader
324       ));
325       jasmine.addCustomEqualityTester(policyTester);
326     });
327
328     it("should contain oran logo and instance info", async () => {
329       let ele = fixture.debugElement.nativeElement.querySelector("img");
330       expect(ele.src).toContain("assets/oran-logo.png");
331
332       ele = fixture.debugElement.nativeElement.querySelector("text");
333       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
334
335       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
336       expect(ele).toBeTruthy();
337       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
338     });
339
340     it("should not contain ric select", async () => {
341       const ricSelector = fixture.debugElement.query(
342         By.directive(RicSelectorComponent)
343       );
344       expect(ricSelector).toBeFalsy();
345     });
346
347     it("should contain json editor with json data", async () => {
348       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
349         By.directive(NoTypePolicyEditorComponent)
350       ).componentInstance;
351       expect(noTypePolicyEditor).toBeTruthy();
352       expect(noTypePolicyEditor.policyJson).toEqual(
353         instanceJson
354       );
355     });
356
357     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
358       let closeButton: MatButtonHarness = await loader.getHarness(
359         MatButtonHarness.with({ selector: "#closeButton" })
360       );
361       expect(await closeButton.isDisabled()).toBeFalsy();
362       expect(await closeButton.getText()).toEqual("Close");
363
364       let submitButton: MatButtonHarness = await loader.getHarness(
365         MatButtonHarness.with({ selector: "#submitButton" })
366       );
367       expect(await submitButton.isDisabled()).toBeFalsy();
368       expect(await submitButton.getText()).toEqual("Submit");
369     });
370
371     it("should submit policy with correct data, close dialog and notify user about success", async () => {
372       policyServiceSpy.putPolicy.and.returnValue(of("ok"));
373       let submitButton: MatButtonHarness = await loader.getHarness(
374         MatButtonHarness.with({ selector: "#submitButton" })
375       );
376
377       await submitButton.click();
378
379       const policyInstance = {} as CreatePolicyInstance;
380       policyInstance.policy_data = instanceJson;
381       policyInstance.policy_id = "instanceId";
382       policyInstance.policytype_id = "";
383       policyInstance.ric_id = "ric1";
384       policyInstance.service_id = "controlpanel";
385       expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
386
387       expect(dialogRefSpy.close).toHaveBeenCalled();
388       expect(notificationServiceSpy.success).toHaveBeenCalledWith(
389         "Policy instanceId submitted"
390       );
391     });
392   });
393
394   describe("content when editing policy with type", () => {
395     const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
396     beforeEach(async () => {
397       const policyData = {
398         createSchema: typedSchema,
399         instanceId: "instanceId",
400         instanceJson: instanceJson,
401         name: "name",
402         ric: "ric1",
403       };
404       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
405       ({ fixture, component, loader } = compileAndGetComponents(
406         fixture,
407         component,
408         loader
409       ));
410     });
411
412     it("should contain oran logo and instance info", async () => {
413       let ele = fixture.debugElement.nativeElement.querySelector("img");
414       expect(ele.src).toContain("assets/oran-logo.png");
415
416       ele = fixture.debugElement.nativeElement.querySelector("text");
417       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
418
419       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
420       expect(ele).toBeTruthy();
421       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
422     });
423
424     it("should not contain ric select", async () => {
425       const ricSelector = fixture.debugElement.query(
426         By.directive(RicSelectorComponent)
427       );
428       expect(ricSelector).toBeFalsy();
429     });
430
431     it("should contain typed json editor with instance JSON, schema and dark mode true", async () => {
432       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
433         By.directive(TypedPolicyEditorComponent)
434       ).componentInstance;
435       expect(typedPolicyEditor).toBeTruthy();
436       expect(unescapeQuotes(typedPolicyEditor.jsonObject)).toEqual(
437         instanceJson
438       );
439       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
440       expect(typedPolicyEditor.darkMode).toBeTruthy();
441     });
442
443     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
444       let closeButton: MatButtonHarness = await loader.getHarness(
445         MatButtonHarness.with({ selector: "#closeButton" })
446       );
447       expect(await closeButton.isDisabled()).toBeFalsy();
448       expect(await closeButton.getText()).toEqual("Close");
449
450       let submitButton: MatButtonHarness = await loader.getHarness(
451         MatButtonHarness.with({ selector: "#submitButton" })
452       );
453       expect(await submitButton.isDisabled()).toBeFalsy();
454       expect(await submitButton.getText()).toEqual("Submit");
455     });
456   });
457 });
458
459 function compileAndGetComponents(
460   fixture: ComponentFixture<PolicyInstanceDialogComponent>,
461   component: PolicyInstanceDialogComponent,
462   loader: HarnessLoader
463 ) {
464   TestBed.compileComponents();
465
466   fixture = TestBed.createComponent(PolicyInstanceDialogComponent);
467   component = fixture.componentInstance;
468   fixture.detectChanges();
469   loader = TestbedHarnessEnvironment.loader(fixture);
470   return { fixture, component, loader };
471 }
472
473 function unescapeQuotes(string: string): string {
474   return string.replace(/\\"/g, '"');
475 }
476
477 function policyTester(first, second) {
478   if (typeof first[0] === "object" && typeof second[0] === "object") {
479     const policy1 = first[0] as CreatePolicyInstance;
480     const policy2 = second[0] as CreatePolicyInstance;
481     return (
482       typeof policy1.policy_data === "object" &&
483       typeof policy2.policy_data === "object" &&
484       JSON.stringify(policy1.policy_data) === JSON.stringify(policy2.policy_data) &&
485       policy1.policy_id === policy2.policy_id &&
486       policy1.policytype_id === policy2.policytype_id &&
487       policy1.ric_id === policy2.ric_id &&
488       policy1.service_id === policy2.service_id
489     );
490   }
491 }