Merge "Fix PolicyControlComponent"
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / policy / policy-instance-dialog / policy-instance-dialog.component.spec.ts
index 156d653..91edaa9 100644 (file)
  */
 
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { By } from "@angular/platform-browser";
+import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
 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 { MatInputModule } from '@angular/material/input';
+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 { MatInputModule } from "@angular/material/input";
+import { of } from "rxjs";
 import { ReactiveFormsModule } from "@angular/forms";
 import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
 import { ToastrModule } from "ngx-toastr";
+import { MockComponent } from "ng-mocks";
 
-import { PolicyService } from "../../services/policy/policy.service";
-import { ErrorDialogService } from "../../services/ui/error-dialog.service";
-import { UiService } from "../../services/ui/ui.service";
+import { PolicyService } from "@services/policy/policy.service";
+import { ErrorDialogService } from "@services/ui/error-dialog.service";
+import { UiService } from "@services/ui/ui.service";
 import { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component";
-import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
+import { TypedPolicyEditorComponent } from "@policy/typed-policy-editor/typed-policy-editor.component";
+import { RicSelectorComponent } from "@policy/ric-selector/ric-selector.component";
+import { NoTypePolicyEditorComponent } from "@policy/no-type-policy-editor/no-type-policy-editor.component";
+import { CreatePolicyInstance } from "@interfaces/policy.types";
+import { NotificationService } from "@services/ui/notification.service";
+import * as uuid from "uuid";
+
+describe("PolicyInstanceDialogComponent", () => {
+  const untypedSchema = JSON.parse("{}");
+  const typedSchema =
+    JSON.parse('{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}');
 
-describe('PolicyInstanceDialogComponent', () => {
   let component: PolicyInstanceDialogComponent;
   let fixture: ComponentFixture<PolicyInstanceDialogComponent>;
   let loader: HarnessLoader;
+  let dialogRefSpy: MatDialogRef<PolicyInstanceDialogComponent>;
   let policyServiceSpy: jasmine.SpyObj<PolicyService>;
   let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
+  let notificationServiceSpy: NotificationService;
 
   beforeEach(async () => {
-    policyServiceSpy = jasmine.createSpyObj('PolicyService', ['putPolicy']);
-    errDialogServiceSpy = jasmine.createSpyObj('ErrorDialogService', ['displayError']);
+    dialogRefSpy = jasmine.createSpyObj("MatDialogRef", ["close"]);
+    policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]);
+    errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [
+      "displayError",
+    ]);
+    notificationServiceSpy = jasmine.createSpyObj("NotificationService", [
+      "success",
+    ]);
 
     TestBed.configureTestingModule({
       imports: [
@@ -55,116 +80,385 @@ describe('PolicyInstanceDialogComponent', () => {
         MatInputModule,
         MatSelectModule,
         ReactiveFormsModule,
-        ToastrModule.forRoot()
-      ],
-      schemas: [
-        CUSTOM_ELEMENTS_SCHEMA
+        ToastrModule.forRoot(),
       ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
       declarations: [
-        PolicyInstanceDialogComponent
+        PolicyInstanceDialogComponent,
+        MockComponent(RicSelectorComponent),
+        MockComponent(NoTypePolicyEditorComponent),
+        MockComponent(TypedPolicyEditorComponent),
       ],
       providers: [
-        { provide: MatDialogRef, useValue: component },
+        ChangeDetectorRef,
+        { provide: MatDialogRef, useValue: dialogRefSpy },
         { provide: PolicyService, useValue: policyServiceSpy },
         { provide: ErrorDialogService, useValue: errDialogServiceSpy },
+        { provide: NotificationService, useValue: notificationServiceSpy },
         { provide: MAT_DIALOG_DATA, useValue: true },
-        UiService
-      ]
+        UiService,
+      ],
     });
   });
 
-  describe('content when creating policy without type', () => {
+  it("should set correct dark mode from UIService", () => {
+    const policyData = {
+      createSchema: untypedSchema,
+    };
+    TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
+    ({ fixture, component, loader } = compileAndGetComponents(
+      fixture,
+      component,
+      loader
+    ));
+    const uiService: UiService = TestBed.inject(UiService);
+    expect(component.darkMode).toBeTruthy();
+
+    uiService.darkModeState.next(false);
+    fixture.detectChanges();
+    expect(component.darkMode).toBeFalsy();
+  });
+
+  describe("creating policy without type", () => {
     beforeEach(async () => {
       const policyData = {
-        createSchema: '{}'
+        createSchema: untypedSchema,
       };
       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
-      ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader));
+      ({ fixture, component, loader } = compileAndGetComponents(
+        fixture,
+        component,
+        loader
+      ));
+      jasmine.addCustomEqualityTester(policyTester);
     });
 
-    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');
+    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.textContent).toEqual('Create new policy instance of type < No Type >');
+      ele = fixture.debugElement.nativeElement.querySelector("text");
+      expect(ele.textContent).toEqual(
+        "Create new policy instance of type < No Type >"
+      );
 
-      ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo');
+      ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
       expect(ele).toBeFalsy();
     });
 
-    it('should contain ric select', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
-      expect(ele).toBeTruthy();
+    it("should contain ric select with no policy type and no ric selected", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      ).componentInstance;
+      expect(ricSelector).toBeTruthy();
+      expect(ricSelector.policyTypeName).toBeFalsy();
+      expect(component.policyInstance.ric_id).toBeFalsy();
     });
 
-    it('should contain json editor', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor');
-      expect(ele).toBeTruthy();
+    it("should contain json editor with no JSON", async () => {
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
+        By.directive(NoTypePolicyEditorComponent)
+      ).componentInstance;
+      expect(noTypePolicyEditor).toBeTruthy();
+      expect(noTypePolicyEditor.policyJson).toBeFalsy();
+    });
+
+    it("should contain enabled Close button and Submit button", async () => {
+      component.ngOnInit();
+
+      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.getText()).toEqual("Submit");
+    });
+
+    it("should enable Submit button when ric is selected and json is valid", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      ).componentInstance;
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
+        By.directive(NoTypePolicyEditorComponent)
+      ).componentInstance;
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
+
+      noTypePolicyEditor.validJson.emit(null);
+      expect(await submitButton.isDisabled()).toBeTruthy();
+
+      ricSelector.selectedRic.emit("ric1");
+      expect(await submitButton.isDisabled()).toBeTruthy();
+
+      noTypePolicyEditor.validJson.emit("{}");
+      expect(await submitButton.isDisabled()).toBeFalsy();
+    });
+
+    it("should generate policy ID when submitting new policy", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      ).componentInstance;
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
+        By.directive(NoTypePolicyEditorComponent)
+      ).componentInstance;
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
+
+      spyOn(uuid, "v4").and.returnValue("1234567890");
+      ricSelector.selectedRic.emit("ric1");
+      noTypePolicyEditor.validJson.emit("{}");
+      await submitButton.click();
+
+      const policyInstance = {} as CreatePolicyInstance;
+      policyInstance.policy_data = JSON.parse("{}");
+      policyInstance.policy_id = "1234567890";
+      policyInstance.ric_id = "ric1";
+      policyInstance.service_id = "controlpanel";
+      expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
+    });
+  });
+
+  describe("content when creating policy with type", () => {
+    beforeEach(async () => {
+      const policyData = {
+        name: "Type 1",
+        createSchema: typedSchema,
+      };
+      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 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.textContent).toEqual(
+        "Create new policy instance of type Type 1"
+      );
+
+      ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
+      expect(ele).toBeFalsy();
+    });
+
+    it("should contain ric select with provided policy type", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      ).componentInstance;
+      expect(ricSelector).toBeTruthy();
+      expect(ricSelector.policyTypeName).toEqual("Type 1");
+    });
+
+    it("should contain typed json editor with empty JSON, schema and dark mode true", async () => {
+      const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
+        By.directive(TypedPolicyEditorComponent)
+      ).componentInstance;
+      expect(typedPolicyEditor).toBeTruthy();
+      expect(typedPolicyEditor.jsonObject).toBeFalsy();
+      expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
+      expect(typedPolicyEditor.darkMode).toBeTruthy();
     });
 
-    it('should contain enabled Close button and disabled Submit button', async () => {
+    it("should contain enabled Close button and Submit button", async () => {
       component.ngOnInit();
 
-      let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#closeButton' }));
+      let closeButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#closeButton" })
+      );
       expect(await closeButton.isDisabled()).toBeFalsy();
-      expect(await closeButton.getText()).toEqual('Close');
+      expect(await closeButton.getText()).toEqual("Close");
+
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
+      expect(await submitButton.getText()).toEqual("Submit");
+    });
+
+    it("should enable Submit button when ric is selected and json is valid", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      ).componentInstance;
+      const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
+        By.directive(TypedPolicyEditorComponent)
+      ).componentInstance;
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
+
+      typedPolicyEditor.validJson.emit(null);
+      expect(await submitButton.isDisabled()).toBeTruthy();
 
-      let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#submitButton' }));
-      // expect(await submitButton.isDisabled()).toBeTruthy();
-      expect(await submitButton.getText()).toEqual('Submit');
+      ricSelector.selectedRic.emit("ric1");
+      expect(await submitButton.isDisabled()).toBeTruthy();
+
+      typedPolicyEditor.validJson.emit("{}");
+      expect(await submitButton.isDisabled()).toBeFalsy();
     });
   });
 
-  describe('content when editing policy without type', () => {
+  describe("content when editing policy without type", () => {
+    const instanceJson = JSON.parse('{"qosObjectives": {"priorityLevel": 3100}}');
     beforeEach(async () => {
       const policyData = {
-        createSchema: '{}',
-        instanceId: 'instanceId',
-        instanceJson: '{"qosObjectives": {"priorityLevel": 3100}}',
-        name: 'name',
-        ric: 'ric1'
+        createSchema: untypedSchema,
+        instanceId: "instanceId",
+        instanceJson: instanceJson,
+        ric: "ric1",
       };
       TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy
-      ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader));
+      ({ fixture, component, loader } = compileAndGetComponents(
+        fixture,
+        component,
+        loader
+      ));
+      jasmine.addCustomEqualityTester(policyTester);
     });
 
-    it('should contain oran logo and instance info', async () => {
-      let ele = fixture.debugElement.nativeElement.querySelector('img');
-      expect(ele.src).toContain('assets/oran-logo.png');
+    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');
+      ele = fixture.debugElement.nativeElement.querySelector("text");
       expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title
 
-      ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo');
+      ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
       expect(ele).toBeTruthy();
-      expect(ele.innerText).toEqual('[ric1] Instance ID: instanceId');
+      expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId");
     });
 
-    it('should not contain ric select', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
-      expect(ele).toBeFalsy();
+    it("should not contain ric select", async () => {
+      const ricSelector = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      );
+      expect(ricSelector).toBeFalsy();
     });
 
-    it('should contain json editor', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor');
-      expect(ele).toBeTruthy();
+    it("should contain json editor with json data", async () => {
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(
+        By.directive(NoTypePolicyEditorComponent)
+      ).componentInstance;
+      expect(noTypePolicyEditor).toBeTruthy();
+      expect(noTypePolicyEditor.policyJson).toEqual(
+        instanceJson
+      );
     });
 
-    it('should contain enabled Close and Submit buttons', async () => {
-      let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#closeButton' }));
+    it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
+      let closeButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#closeButton" })
+      );
       expect(await closeButton.isDisabled()).toBeFalsy();
-      expect(await closeButton.getText()).toEqual('Close');
+      expect(await closeButton.getText()).toEqual("Close");
 
-      let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#submitButton' }));
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
       expect(await submitButton.isDisabled()).toBeFalsy();
-      expect(await submitButton.getText()).toEqual('Submit');
+      expect(await submitButton.getText()).toEqual("Submit");
     });
 
+    it("should submit policy with correct data, close dialog and notify user about success", async () => {
+      policyServiceSpy.putPolicy.and.returnValue(of("ok"));
+      let submitButton: MatButtonHarness = await loader.getHarness(
+        MatButtonHarness.with({ selector: "#submitButton" })
+      );
+
+      await submitButton.click();
+
+      const policyInstance = {} as CreatePolicyInstance;
+      policyInstance.policy_data = instanceJson;
+      policyInstance.policy_id = "instanceId";
+      policyInstance.ric_id = "ric1";
+      policyInstance.service_id = "controlpanel";
+      expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
+
+      expect(dialogRefSpy.close).toHaveBeenCalled();
+      expect(notificationServiceSpy.success).toHaveBeenCalledWith(
+        "Policy instanceId submitted"
+      );
+    });
+  });
+
+  describe("content when editing policy with type", () => {
+    const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
+    beforeEach(async () => {
+      const policyData = {
+        createSchema: typedSchema,
+        instanceId: "instanceId",
+        instanceJson: instanceJson,
+        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 not contain ric select", async () => {
+      const ricSelector = fixture.debugElement.query(
+        By.directive(RicSelectorComponent)
+      );
+      expect(ricSelector).toBeFalsy();
+    });
+
+    it("should contain typed json editor with instance JSON, schema and dark mode true", async () => {
+      const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(
+        By.directive(TypedPolicyEditorComponent)
+      ).componentInstance;
+      expect(typedPolicyEditor).toBeTruthy();
+      expect(unescapeQuotes(typedPolicyEditor.jsonObject)).toEqual(
+        instanceJson
+      );
+      expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema);
+      expect(typedPolicyEditor.darkMode).toBeTruthy();
+    });
+
+    it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
+      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<PolicyInstanceDialogComponent>, component: PolicyInstanceDialogComponent, loader: HarnessLoader) {
+function compileAndGetComponents(
+  fixture: ComponentFixture<PolicyInstanceDialogComponent>,
+  component: PolicyInstanceDialogComponent,
+  loader: HarnessLoader
+) {
   TestBed.compileComponents();
 
   fixture = TestBed.createComponent(PolicyInstanceDialogComponent);
@@ -173,3 +467,23 @@ function compileAndGetComponents(fixture: ComponentFixture<PolicyInstanceDialogC
   loader = TestbedHarnessEnvironment.loader(fixture);
   return { fixture, component, loader };
 }
+
+function unescapeQuotes(string: string): string {
+  return string.replace(/\\"/g, '"');
+}
+
+function policyTester(first, second) {
+  if (typeof first[0] === "object" && typeof second[0] === "object") {
+    const policy1 = first[0] as CreatePolicyInstance;
+    const policy2 = second[0] as CreatePolicyInstance;
+    return (
+      typeof policy1.policy_data === "object" &&
+      typeof policy2.policy_data === "object" &&
+      JSON.stringify(policy1.policy_data) === JSON.stringify(policy2.policy_data) &&
+      policy1.policy_id === policy2.policy_id &&
+      policy1.policytype_id === policy2.policytype_id &&
+      policy1.ric_id === policy2.ric_id &&
+      policy1.service_id === policy2.service_id
+    );
+  }
+}