b4c273245bd78db112217c9c9b2ea5a515633b99
[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 { ComponentFixture, TestBed } from "@angular/core/testing";
23 import { HarnessLoader } from "@angular/cdk/testing";
24 import { MatButtonModule } from "@angular/material/button";
25 import { MatButtonHarness } from "@angular/material/button/testing";
26 import {
27   MatDialogModule,
28   MatDialogRef,
29   MAT_DIALOG_DATA,
30 } from "@angular/material/dialog";
31 import { MatSelectModule } from "@angular/material/select";
32 import { MatInputModule } from "@angular/material/input";
33 import {
34   AbstractControl,
35   FormControl,
36   FormGroup,
37   ReactiveFormsModule,
38   Validators,
39 } from "@angular/forms";
40 import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
41 import { ToastrModule } from "ngx-toastr";
42
43 import { PolicyService } from "../../services/policy/policy.service";
44 import { ErrorDialogService } from "../../services/ui/error-dialog.service";
45 import { UiService } from "../../services/ui/ui.service";
46 import { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component";
47 import {
48   ChangeDetectorRef,
49   Component,
50   CUSTOM_ELEMENTS_SCHEMA,
51   Input,
52 } from "@angular/core";
53 import { TypedPolicyEditorComponent } from "../typed-policy-editor/typed-policy-editor.component";
54 import { RicSelectorComponent } from "../ric-selector/ric-selector.component";
55 import { NoTypePolicyEditorComponent } from "../no-type-policy-editor/no-type-policy-editor.component";
56 import { By } from "@angular/platform-browser";
57
58 describe("PolicyInstanceDialogComponent", () => {
59   const untypedSchema = "{}";
60   const typedSchema =
61     '{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}';
62
63   let component: PolicyInstanceDialogComponent;
64   let fixture: ComponentFixture<PolicyInstanceDialogComponent>;
65   let loader: HarnessLoader;
66   let policyServiceSpy: jasmine.SpyObj<PolicyService>;
67   let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
68
69   beforeEach(async () => {
70     policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]);
71     errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [
72       "displayError",
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         RicSelectorStubComponent,
89         NoTypePolicyEditorStubComponent,
90         TypedPolicyEditorStubComponent,
91       ],
92       providers: [
93         ChangeDetectorRef,
94         { provide: MatDialogRef, useValue: component },
95         { provide: PolicyService, useValue: policyServiceSpy },
96         { provide: ErrorDialogService, useValue: errDialogServiceSpy },
97         { provide: MAT_DIALOG_DATA, useValue: true },
98         UiService,
99       ],
100     });
101   });
102
103   describe("content when creating policy without type", () => {
104     beforeEach(async () => {
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     });
115
116     it("should contain oran logo and create title and no instance info", async () => {
117       let ele = fixture.debugElement.nativeElement.querySelector("img");
118       expect(ele.src).toContain("assets/oran-logo.png");
119
120       ele = fixture.debugElement.nativeElement.querySelector("text");
121       expect(ele.textContent).toEqual(
122         "Create new policy instance of type < No Type >"
123       );
124
125       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
126       expect(ele).toBeFalsy();
127     });
128
129     it("should contain ric select with instance form and no policy type", async () => {
130       const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance;
131       expect(ricSelector).toBeTruthy();
132       expect(ricSelector.instanceForm).toBeTruthy();
133       expect(ricSelector.policyTypeName).toBeFalsy();
134     });
135
136     it("should contain json editor with instance form and empty JSON", async () => {
137       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance;
138       expect(noTypePolicyEditor).toBeTruthy();
139       expect(noTypePolicyEditor.instanceForm).toBeTruthy();
140       expect(noTypePolicyEditor.policyJson).toEqual("{}");
141     });
142
143     it("should contain enabled Close button and disabled Submit button", async () => {
144       component.ngOnInit();
145       // Add an empty value with required validator to set the dialog's instance form to be invalid.
146       const value: any = null;
147       component.instanceForm.addControl(
148         "dummy",
149         new FormControl(value, [Validators.required])
150       );
151       expect(component.instanceForm.valid).toBeFalsy();
152
153       let closeButton: MatButtonHarness = await loader.getHarness(
154         MatButtonHarness.with({ selector: "#closeButton" })
155       );
156       expect(await closeButton.isDisabled()).toBeFalsy();
157       expect(await closeButton.getText()).toEqual("Close");
158
159       let submitButton: MatButtonHarness = await loader.getHarness(
160         MatButtonHarness.with({ selector: "#submitButton" })
161       );
162       expect(await submitButton.isDisabled()).toBeTruthy();
163       expect(await submitButton.getText()).toEqual("Submit");
164     });
165   });
166
167   describe("content when creating policy with type", () => {
168     beforeEach(async () => {
169       const policyData = {
170         name: "Type 1",
171         createSchema: typedSchema,
172       };
173       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
174       ({ fixture, component, loader } = compileAndGetComponents(
175         fixture,
176         component,
177         loader
178       ));
179     });
180
181     it("should contain oran logo and create title and no instance info", async () => {
182       let ele = fixture.debugElement.nativeElement.querySelector("img");
183       expect(ele.src).toContain("assets/oran-logo.png");
184
185       ele = fixture.debugElement.nativeElement.querySelector("text");
186       expect(ele.textContent).toEqual(
187         "Create new policy instance of type Type 1"
188       );
189
190       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
191       expect(ele).toBeFalsy();
192     });
193
194     it("should contain ric select with instance form and provided policy type", async () => {
195       const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance;
196       expect(ricSelector).toBeTruthy();
197       expect(ricSelector.instanceForm).toBeTruthy();
198       expect(ricSelector.policyTypeName).toEqual("Type 1");
199     });
200
201     it("should contain typed json editor with empty JSON, schema and dark mode true", async () => {
202       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(By.directive(TypedPolicyEditorComponent)).componentInstance;
203       expect(typedPolicyEditor).toBeTruthy();
204       expect(typedPolicyEditor.jsonObject).toBeFalsy();
205       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
206       expect(typedPolicyEditor.darkMode).toBeTruthy();
207     });
208
209     it("should contain enabled Close button and disabled Submit button", async () => {
210       component.ngOnInit();
211       // Add an empty value with required validator to set the dialog's instance form to be invalid.
212       const value: any = null;
213       component.instanceForm.addControl(
214         "dummy",
215         new FormControl(value, [Validators.required])
216       );
217       expect(component.instanceForm.valid).toBeFalsy();
218
219       let closeButton: MatButtonHarness = await loader.getHarness(
220         MatButtonHarness.with({ selector: "#closeButton" })
221       );
222       expect(await closeButton.isDisabled()).toBeFalsy();
223       expect(await closeButton.getText()).toEqual("Close");
224
225       let submitButton: MatButtonHarness = await loader.getHarness(
226         MatButtonHarness.with({ selector: "#submitButton" })
227       );
228       expect(await submitButton.isDisabled()).toBeTruthy();
229       expect(await submitButton.getText()).toEqual("Submit");
230     });
231   });
232
233   describe("content when editing policy without type", () => {
234     const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
235     beforeEach(async () => {
236       const policyData = {
237         createSchema: untypedSchema,
238         instanceId: "instanceId",
239         instanceJson: instanceJson,
240         name: "Type 1",
241         ric: "ric1",
242       };
243       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
244       ({ fixture, component, loader } = compileAndGetComponents(
245         fixture,
246         component,
247         loader
248       ));
249     });
250
251     it("should contain oran logo and instance info", async () => {
252       let ele = fixture.debugElement.nativeElement.querySelector("img");
253       expect(ele.src).toContain("assets/oran-logo.png");
254
255       ele = fixture.debugElement.nativeElement.querySelector("text");
256       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
257
258       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
259       expect(ele).toBeTruthy();
260       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
261     });
262
263     it("should not contain ric select", async () => {
264       const ricSelector = fixture.debugElement.query(By.directive(RicSelectorComponent));
265       expect(ricSelector).toBeFalsy();
266     });
267
268     it("should contain json editor with form and json data", async () => {
269       const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance;
270       expect(noTypePolicyEditor).toBeTruthy();
271       expect(noTypePolicyEditor.instanceForm).toBeTruthy();
272       expect(unescapeQuotes(noTypePolicyEditor.policyJson)).toEqual('"' + instanceJson + '"');
273     });
274
275     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
276       expect(component.instanceForm.valid).toBeTruthy();
277
278       let closeButton: MatButtonHarness = await loader.getHarness(
279         MatButtonHarness.with({ selector: "#closeButton" })
280       );
281       expect(await closeButton.isDisabled()).toBeFalsy();
282       expect(await closeButton.getText()).toEqual("Close");
283
284       let submitButton: MatButtonHarness = await loader.getHarness(
285         MatButtonHarness.with({ selector: "#submitButton" })
286       );
287       expect(await submitButton.isDisabled()).toBeFalsy();
288       expect(await submitButton.getText()).toEqual("Submit");
289     });
290   });
291
292   describe("content when editing policy with type", () => {
293     const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
294     beforeEach(async () => {
295       const policyData = {
296         createSchema: typedSchema,
297         instanceId: "instanceId",
298         instanceJson: instanceJson,
299         name: "name",
300         ric: "ric1",
301       };
302       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
303       ({ fixture, component, loader } = compileAndGetComponents(
304         fixture,
305         component,
306         loader
307       ));
308     });
309
310     it("should contain oran logo and instance info", async () => {
311       let ele = fixture.debugElement.nativeElement.querySelector("img");
312       expect(ele.src).toContain("assets/oran-logo.png");
313
314       ele = fixture.debugElement.nativeElement.querySelector("text");
315       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
316
317       ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
318       expect(ele).toBeTruthy();
319       expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
320     });
321
322     it("should not contain ric select", async () => {
323       const ricSelector = fixture.debugElement.query(By.directive(RicSelectorComponent));
324       expect(ricSelector).toBeFalsy();
325     });
326
327     it("should contain typed json editor with instance JSON, schema and dark mode true", async () => {
328       const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(By.directive(TypedPolicyEditorComponent)).componentInstance;
329       expect(typedPolicyEditor).toBeTruthy();
330       expect(unescapeQuotes(typedPolicyEditor.jsonObject)).toEqual(instanceJson);
331       expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
332       expect(typedPolicyEditor.darkMode).toBeTruthy();
333     });
334
335     it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
336       expect(component.instanceForm.valid).toBeTruthy();
337
338       let closeButton: MatButtonHarness = await loader.getHarness(
339         MatButtonHarness.with({ selector: "#closeButton" })
340       );
341       expect(await closeButton.isDisabled()).toBeFalsy();
342       expect(await closeButton.getText()).toEqual("Close");
343
344       let submitButton: MatButtonHarness = await loader.getHarness(
345         MatButtonHarness.with({ selector: "#submitButton" })
346       );
347       expect(await submitButton.isDisabled()).toBeFalsy();
348       expect(await submitButton.getText()).toEqual("Submit");
349     });
350   });
351 });
352
353 function compileAndGetComponents(
354   fixture: ComponentFixture<PolicyInstanceDialogComponent>,
355   component: PolicyInstanceDialogComponent,
356   loader: HarnessLoader
357 ) {
358   TestBed.compileComponents();
359
360   fixture = TestBed.createComponent(PolicyInstanceDialogComponent);
361   component = fixture.componentInstance;
362   fixture.detectChanges();
363   loader = TestbedHarnessEnvironment.loader(fixture);
364   return { fixture, component, loader };
365 }
366
367 function unescapeQuotes(string: string): string {
368   return string.replace(/\\"/g, '"');
369 }
370
371 @Component({
372   selector: "nrcp-ric-selector",
373   template: "",
374   providers: [
375     {
376       provide: RicSelectorComponent,
377       useClass: RicSelectorStubComponent,
378     },
379   ],
380 })
381 class RicSelectorStubComponent {
382   @Input() instanceForm: FormGroup;
383   @Input() policyTypeName: string = "";
384
385   get selectedRic(): string {
386     return "ric1";
387   }
388 }
389
390 @Component({
391   selector: "nrcp-no-type-policy-editor",
392   template: "",
393   providers: [
394     {
395       provide: NoTypePolicyEditorComponent,
396       useClass: NoTypePolicyEditorStubComponent,
397     },
398   ],
399 })
400 class NoTypePolicyEditorStubComponent {
401   @Input() instanceForm: FormGroup;
402   @Input() policyJson: string;
403
404   get policyJsonTextArea(): AbstractControl {
405     const textArea = { value: "{}" } as AbstractControl;
406     return textArea;
407   }
408 }
409
410 @Component({
411   selector: "nrcp-typed-policy-editor",
412   template: "",
413   providers: [
414     {
415       provide: TypedPolicyEditorComponent,
416       useClass: TypedPolicyEditorStubComponent,
417     },
418   ],
419 })
420 class TypedPolicyEditorStubComponent {
421   @Input() jsonSchemaObject: any = {};
422   @Input() jsonObject: any = {};
423   @Input() darkMode: boolean;
424
425   prettyLiveFormData = '"A": "string"';
426   get formIsValid(): boolean {
427     return true;
428   }
429 }