Merge "Include datasource of jobs and producers into component"
authorPatrik Buhr <patrik.buhr@est.tech>
Wed, 17 Mar 2021 12:33:31 +0000 (12:33 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Wed, 17 Mar 2021 12:33:31 +0000 (12:33 +0000)
20 files changed:
webapp-frontend/package.json
webapp-frontend/src/app/interceptor.mock.ts
webapp-frontend/src/app/mock/no-type-policies.json [new file with mode: 0644]
webapp-frontend/src/app/mock/policy-instance-notype.json
webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts
webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html [deleted file]
webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss [deleted file]
webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts [deleted file]
webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts [deleted file]
webapp-frontend/src/app/policy/policy-control.component.ts
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.scss
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts [new file with mode: 0644]
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts
webapp-frontend/src/app/policy/policy-instance/policy-instance.component.ts
webapp-frontend/src/app/policy/policy.module.ts
webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts
webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html
webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts
webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts

index 96af51f..8ed13a4 100644 (file)
@@ -28,7 +28,6 @@
     "angular6-json-schema-form": "^8.0.0",
     "bootstrap": "^4.4.1",
     "chart.js": "^2.9.3",
-    "core-js": "^2.6.11",
     "hammerjs": "^2.0.8",
     "jquery": "^3.5.1",
     "lodash-es": "^4.17.15",
@@ -36,6 +35,7 @@
     "ng2-completer": "^2.0.8",
     "ngx-cookie-service": "^3.1.3",
     "ngx-toastr": "^11.0.0",
+    "popper.js": "^1.16.1",
     "rxjs": "6.5.3",
     "rxjs-compat": "6.3.3",
     "tslib": "^1.10.0",
index 29bcf6a..749d762 100644 (file)
 import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
 import { Injectable, Injector } from '@angular/core';
 import { Observable, of } from 'rxjs';
-import * as policyinstances1 from './mock/policy-instance-1.json';
-import * as policies from './mock/policies.json';
-import * as policyinstances2 from './mock/policy-instance-2.json';
-import * as policyinstances1Status from './mock/policy-instance-1-status.json';
-import * as policyinstances2Status from './mock/policy-instance-2-status.json';
+import * as policyinstance1 from './mock/policy-instance-1.json';
+import * as noTypePolicies from './mock/no-type-policies.json';
+import * as typedPolicies from './mock/policies.json';
+import * as policyinstance2 from './mock/policy-instance-2.json';
+import * as noTypePolicyinstance from './mock/policy-instance-notype.json';
+import * as policyinstance1Status from './mock/policy-instance-1-status.json';
+import * as policyinstance2Status from './mock/policy-instance-2-status.json';
 import * as eijobsProd1 from './mock/ei-jobs-producer1.json';
 import * as eijobsProd2 from './mock/ei-jobs-producer2.json';
 import * as eiProducerIds from './mock/ei-producerids.json';
@@ -54,27 +56,35 @@ const urls = [
     },
     {
         url: '/a1-policy/v2/policies?policytype_id=',
-        json: policies
+        json: noTypePolicies
     },
     {
         url: '/a1-policy/v2/policies?policytype_id=1',
-        json: policies
+        json: typedPolicies
+    },
+    {
+        url: '/a1-policy/v2/policies/2001',
+        json: noTypePolicyinstance
     },
     {
         url: '/a1-policy/v2/policies/2000',
-        json: policyinstances1
+        json: policyinstance1
     },
     {
         url: '/a1-policy/v2/policies/2100',
-        json: policyinstances2
+        json: policyinstance2
+    },
+    {
+        url: '/a1-policy/v2/policies/2001/status',
+        json: policyinstance1Status
     },
     {
         url: '/a1-policy/v2/policies/2000/status',
-        json: policyinstances1Status
+        json: policyinstance1Status
     },
     {
         url: '/a1-policy/v2/policies/2100/status',
-        json: policyinstances2Status
+        json: policyinstance2Status
     },
     {
         url: '/a1-policy/v2/policies/2000?type=',
diff --git a/webapp-frontend/src/app/mock/no-type-policies.json b/webapp-frontend/src/app/mock/no-type-policies.json
new file mode 100644 (file)
index 0000000..6f00441
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "policy_ids": [
+    "2001"
+  ]
+}
\ No newline at end of file
index 7c91060..a3d95e7 100644 (file)
@@ -1,18 +1,17 @@
-[
-    {
-      "id": "2001",
-      "type": "",
-      "ric": "ric1",
-      "json": {
-        "scope": {
-          "ueId": "ue3100",
-          "qosId": "qos3100"
-        },
-        "qosObjectives": {
-          "priorityLevel": 3100
-        }
-      },
-      "service": "service1",
-      "lastModified": "2020-12-08T21:12:43.719084Z"
+{
+  "policy_id": "2001",
+  "policytype_id": "",
+  "ric_id": "ric1",
+  "policy_data": {
+    "scope": {
+      "ueId": "ue3200",
+      "qosId": "qos3200"
+    },
+    "qosObjectives": {
+      "priorityLevel": 3100
     }
-  ]
\ No newline at end of file
+  },
+  "service_id": "service1",
+  "transient": false,
+  "status_notification_uri": ""
+}
\ No newline at end of file
index 529caee..fb3a6a6 100644 (file)
 //   ========================LICENSE_END===================================
 //
 
-import { HarnessLoader } from '@angular/cdk/testing';
-import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
-import { Component, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { FormBuilder, FormGroup } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatButtonHarness } from '@angular/material/button/testing';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatInputHarness } from '@angular/material/input/testing';
-import { BrowserModule } from '@angular/platform-browser';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { HarnessLoader } from "@angular/cdk/testing";
+import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
+import { Component, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { FormBuilder, FormGroup } from "@angular/forms";
+import { MatButtonModule } from "@angular/material/button";
+import { MatButtonHarness } from "@angular/material/button/testing";
+import { MatFormFieldModule } from "@angular/material/form-field";
+import { MatInputModule } from "@angular/material/input";
+import { MatInputHarness } from "@angular/material/input/testing";
+import { BrowserModule } from "@angular/platform-browser";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 
-import { NoTypePolicyEditorComponent } from './no-type-policy-editor.component';
+import { NoTypePolicyEditorComponent } from "./no-type-policy-editor.component";
 
-describe('NoTypePolicyEditorComponent', () => {
+describe("NoTypePolicyEditorComponent", () => {
   let formGroup: FormGroup = new FormGroup({});
 
   let component: TestNoTypePolicyEditorComponentHostComponent;
@@ -47,65 +47,74 @@ describe('NoTypePolicyEditorComponent', () => {
         BrowserAnimationsModule,
         MatButtonModule,
         MatFormFieldModule,
-        MatInputModule
-      ],
-      schemas: [
-        CUSTOM_ELEMENTS_SCHEMA
+        MatInputModule,
       ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
       declarations: [
         NoTypePolicyEditorComponent,
-        TestNoTypePolicyEditorComponentHostComponent
+        TestNoTypePolicyEditorComponentHostComponent,
       ],
-      providers: [
-        FormBuilder
-      ]
-    })
-    .compileComponents();
+      providers: [FormBuilder],
+    }).compileComponents();
 
-    fixture = TestBed.createComponent(TestNoTypePolicyEditorComponentHostComponent);
+    fixture = TestBed.createComponent(
+      TestNoTypePolicyEditorComponentHostComponent
+    );
     component = fixture.componentInstance;
     fixture.detectChanges();
     loader = TestbedHarnessEnvironment.loader(fixture);
   });
 
-  it('should create', () => {
+  it("should create", () => {
     expect(component).toBeTruthy();
   });
 
-  it('should be added to form group with required validator', async () => {
-    let textArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#policyJsonTextArea' }));
+  it("should be added to form group with required validator", async () => {
+    let textArea: MatInputHarness = await loader.getHarness(
+      MatInputHarness.with({ selector: "#policyJsonTextArea" })
+    );
 
-    expect(formGroup.get('policyJsonTextArea')).toBeTruthy();
+    expect(formGroup.get("policyJsonTextArea")).toBeTruthy();
     expect(await textArea.isRequired()).toBeTruthy();
   });
 
-  it('should contain provided policy json and enabled Format button', async () => {
-    let textArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#policyJsonTextArea' }));
+  it("should contain provided policy json and enabled Format button", async () => {
+    let textArea: MatInputHarness = await loader.getHarness(
+      MatInputHarness.with({ selector: "#policyJsonTextArea" })
+    );
     expect(await textArea.getValue()).toEqual('{"A":"A"}');
 
-    console.log('Validity:',formGroup.valid);
-    let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' }));
+    let formatButton: MatButtonHarness = await loader.getHarness(
+      MatButtonHarness.with({ selector: "#formatButton" })
+    );
     expect(await formatButton.isDisabled()).toBeFalsy();
   });
 
-  it('Format button should be disabled when json not valid', async () => {
-    const ele = formGroup.get('policyJsonTextArea');
-    ele.setValue('{');
+  it("Format button should be disabled when json not valid", async () => {
+    const ele = formGroup.get("policyJsonTextArea");
+    ele.setValue("{");
 
-    let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' }));
+    let formatButton: MatButtonHarness = await loader.getHarness(
+      MatButtonHarness.with({ selector: "#formatButton" })
+    );
     expect(await formatButton.isDisabled()).toBeTruthy();
   });
 
-  it('should format unformatted json', async () => {
-    const textArea = formGroup.get('policyJsonTextArea');
+  it("should format unformatted json", async () => {
+    const textArea = formGroup.get("policyJsonTextArea");
     textArea.setValue('{"A":"A"}');
     component.noTypePolicyEditorComponent.formatJsonInput();
-    expect(component.noTypePolicyEditorComponent.policyJson).toEqual('{\n  "A": "A"\n}');
+    expect(component.noTypePolicyEditorComponent.policyJson).toEqual(
+      '{\n  "A": "A"\n}'
+    );
   });
 
   @Component({
     selector: `no-type-policy-editor-host-component`,
-    template: `<nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>`
+    template: `<nrcp-no-type-policy-editor
+      [policyJson]="this.policyJson"
+      [instanceForm]="instanceForm"
+    ></nrcp-no-type-policy-editor>`,
   })
   class TestNoTypePolicyEditorComponentHostComponent {
     @ViewChild(NoTypePolicyEditorComponent)
diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html
deleted file mode 100644 (file)
index afba575..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<!--
-  ========================LICENSE_START=================================
-  O-RAN-SC
-  %%
-  Copyright (C) 2020 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===================================
-  -->
-
-<div class="text-muted logo" fxLayout="row" fxLayoutGap="50px" fxLayoutAlign="space-around center">
-    <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">
-        <img src="assets/oran-logo.png" width="30px" height="30px" style="position: relative; z-index: 50" />
-        <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 &lt; No type &gt;</tspan>
-            </text>
-        </svg>
-    </div>
-</div>
-
-<form [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
-    fxLayoutAlign.lt-sm="flex-start center">
-
-    <mat-card class="card" [ngClass]="{'card-dark': darkMode}">
-        <nrcp-ric-selector *ngIf="!this.policyInstanceId" [instanceForm]="instanceForm"></nrcp-ric-selector>
-        <h4>
-            Properties
-        </h4>
-        <nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>
-        <div mat-dialog-actions>
-            <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>
-    </mat-card>
-</form>
\ No newline at end of file
diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss
deleted file mode 100644 (file)
index accb527..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*-
- * ========================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===================================
- */
-
-
-.logo__text {
-    fill: #2B244D;
-}
-
-.logo__text-dark {
-    fill: #ffffff;
-}
-
-.header-dark {
-    background: #2B244D;
-}
-
-.logo {
-    margin-left: 10px;
-}
-
-.logo__icon {
-    height: 2rem;
-    margin-left: 1rem;
-}
-
-.submitBtn {
-    background-color: #4CAF50;
-    /* Green */
-    margin-right: 10px;
-}
-
-.header {
-    background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%);
-    font-size: 40px;
-    margin-top: 10px;
-    margin-bottom: 10px;
-    margin-left: 2px;
-    margin-right: 2px;
-}
-
-.header-dark {
-    background: #2B244D;
-}
-
-
-.card {
-    height: 100%;
-    width: 100%;
-    margin-left: 2px;
-    margin-right: 2px;
-    background-color: grayscale($color: #eeeaea);
-}
-
-.card-dark {
-    background-color: #1c1c24;
-}
diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts
deleted file mode 100644 (file)
index fc07a5b..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*-
- * ========================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 { MatInputModule } from '@angular/material/input';
-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 { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
-
-describe('NoTypePolicyInstanceDialogComponent', () => {
-  let component: NoTypePolicyInstanceDialogComponent;
-  let fixture: ComponentFixture<NoTypePolicyInstanceDialogComponent>;
-  let loader: HarnessLoader;
-  let policyServiceSpy: jasmine.SpyObj<PolicyService>;
-  let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
-
-  beforeEach(async () => {
-    policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'putPolicy' ]);
-    errDialogServiceSpy = jasmine.createSpyObj('ErrorDialogService', [ 'displayError' ]);
-
-    TestBed.configureTestingModule({
-      imports: [
-        BrowserAnimationsModule,
-        MatButtonModule,
-        MatDialogModule,
-        MatInputModule,
-        MatSelectModule,
-        ReactiveFormsModule,
-        ToastrModule.forRoot()
-      ],
-      schemas: [
-        CUSTOM_ELEMENTS_SCHEMA
-      ],
-      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 ric select', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
-      expect(ele).toBeTruthy();
-    });
-
-    it('should contain json editor', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor');
-      expect(ele).toBeTruthy();
-    });
-
-    it('should contain enabled Close button and disabled 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.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 not contain ric select', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
-      expect(ele).toBeFalsy();
-    });
-
-    it('should contain json editor', async () => {
-      const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor');
-      expect(ele).toBeTruthy();
-    });
-
-    it('should contain enabled Close and Submit buttons', 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<NoTypePolicyInstanceDialogComponent>, component: NoTypePolicyInstanceDialogComponent, loader: HarnessLoader) {
-  TestBed.compileComponents();
-
-  fixture = TestBed.createComponent(NoTypePolicyInstanceDialogComponent);
-  component = fixture.componentInstance;
-  fixture.detectChanges();
-  loader = TestbedHarnessEnvironment.loader(fixture);
-  return { fixture, component, loader };
-}
diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts
deleted file mode 100644 (file)
index 855d718..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*-
- * ========================LICENSE_START=================================
- * O-RAN-SC
- * %%
- * Copyright (C) 2020 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 { Component, Inject, OnInit, ViewChild } from '@angular/core';
-import { FormGroup } from '@angular/forms';
-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 { CreatePolicyInstance } from '../../interfaces/policy.types';
-import { RicSelectorComponent } from '../ric-selector/ric-selector.component';
-import { formatJsonString, NoTypePolicyEditorComponent } from '../no-type-policy-editor/no-type-policy-editor.component';
-
-@Component({
-  selector: 'nrcp-no-type-policy-instance-dialog',
-  templateUrl: './no-type-policy-instance-dialog.component.html',
-  styleUrls: ['./no-type-policy-instance-dialog.component.scss']
-})
-export class NoTypePolicyInstanceDialogComponent implements OnInit {
-  instanceForm: FormGroup;
-  @ViewChild(RicSelectorComponent)
-  private ricSelectorComponent: RicSelectorComponent;
-  @ViewChild(NoTypePolicyEditorComponent)
-  private policyEditorComponent: NoTypePolicyEditorComponent;
-  policyInstanceId: string; // null if not yet created
-  policyJson: string;
-  darkMode: boolean;
-  ric: string;
-  allRicIds: string[] = [];
-
-  constructor(
-    public dialogRef: MatDialogRef<NoTypePolicyInstanceDialogComponent>,
-    private policySvc: PolicyService,
-    private errorService: ErrorDialogService,
-    private notificationService: NotificationService,
-    @Inject(MAT_DIALOG_DATA) private data,
-    private ui: UiService) {
-    this.policyInstanceId = data.instanceId;
-    this.policyJson = data.instanceJson ? formatJsonString(data.instanceJson) : '{}';
-    this.ric = data.ric;
-  }
-
-  ngOnInit() {
-    this.ui.darkModeState.subscribe((isDark) => {
-      this.darkMode = isDark;
-    });
-    this.instanceForm = new FormGroup({});
-  }
-
-  onSubmit() {
-    if (this.policyInstanceId == null) {
-      this.policyInstanceId = uuid.v4();
-    }
-    const self: NoTypePolicyInstanceDialogComponent = this;
-    let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(this.policyEditorComponent.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);
-        },
-        complete() { }
-      });
-  }
-
-  private createPolicyInstance(policyJson: string): CreatePolicyInstance {
-    let createPolicyInstance = {} as CreatePolicyInstance;
-    createPolicyInstance.policy_data = JSON.parse(policyJson);
-    createPolicyInstance.policy_id = this.policyInstanceId;
-    createPolicyInstance.policytype_id = '';
-    createPolicyInstance.ric_id = this.ricSelectorComponent ? this.ricSelectorComponent.selectedRic : this.ric;
-    createPolicyInstance.service_id = 'controlpanel';
-    return createPolicyInstance;
-  }
-}
index 01c38b6..28253c8 100644 (file)
@@ -23,7 +23,6 @@ import { MatDialog } from '@angular/material/dialog';
 
 import { BehaviorSubject, Observable } from 'rxjs';
 
-import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog/no-type-policy-instance-dialog.component';
 import { PolicyTypeSchema } from '../interfaces/policy.types';
 import { PolicyTypeDataSource } from './policy-type/policy-type.datasource';
 import { getPolicyDialogProperties } from './policy-instance-dialog/policy-instance-dialog.component';
@@ -67,14 +66,8 @@ export class PolicyControlComponent implements OnInit {
     }
 
     createPolicyInstance(policyTypeSchema: PolicyTypeSchema): void {
-        let dialogRef;
-        if (this.isSchemaEmpty(policyTypeSchema)) {
-            dialogRef = this.dialog.open(NoTypePolicyInstanceDialogComponent,
-                getPolicyDialogProperties(policyTypeSchema, null, this.darkMode));
-        } else {
-            dialogRef = this.dialog.open(PolicyInstanceDialogComponent,
-                getPolicyDialogProperties(policyTypeSchema, null, this.darkMode));
-        }
+        let dialogRef = this.dialog.open(PolicyInstanceDialogComponent,
+            getPolicyDialogProperties(policyTypeSchema, null, this.darkMode));
         const info: PolicyTypeInfo = this.getPolicyTypeInfo(policyTypeSchema);
         dialogRef.afterClosed().subscribe(
             (_) => {
@@ -88,10 +81,6 @@ export class PolicyControlComponent implements OnInit {
         info.isExpanded.next(!info.isExpanded.getValue());
     }
 
-    private isSchemaEmpty(policyTypeSchema: PolicyTypeSchema): boolean {
-        return policyTypeSchema.schemaObject === '{}';
-    }
-
     getPolicyTypeInfo(policyTypeSchema: PolicyTypeSchema): PolicyTypeInfo {
         let info: PolicyTypeInfo = this.policyTypeInfo.get(policyTypeSchema.name);
         if (!info) {
index 695dc6d..6e79e78 100644 (file)
@@ -2,7 +2,7 @@
   ========================LICENSE_START=================================
   O-RAN-SC
   %%
-  Copyright (C) 2019 Nordix Foundation
+  Copyright (C) 2020 Nordix Foundation
   %%
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
   -->
 
 <div class="text-muted logo" fxLayout="row" fxLayoutGap="50px" fxLayoutAlign="space-around center">
-    <div *ngIf="policyInstanceId">[{{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,9 +27,9 @@
         <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="!policyInstanceId">Create new policy instance of type </tspan>
-                <tspan *ngIf="jsonSchemaObject.title"> {{jsonSchemaObject.title}}</tspan>
-                <tspan *ngIf="!jsonSchemaObject.title"> {{policyTypeName}}</tspan>
+                <tspan *ngIf="!this.policyInstanceId">Create new policy instance of type </tspan>
+                <tspan *ngIf="policyTypeName">{{policyTypeName}}</tspan>
+                <tspan *ngIf="!policyTypeName">&lt; No Type &gt;</tspan>
             </text>
         </svg>
     </div>
 
 <div class="text-muted" *ngIf="jsonSchemaObject.description">{{jsonSchemaObject.description}}</div>
 
-<div [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
+<form [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
     fxLayoutAlign.lt-sm="flex-start center">
 
     <mat-card class="card" [ngClass]="{'card-dark': darkMode}">
-        <mat-form-field *ngIf="!policyInstanceId" appearance="fill">
-            <mat-select id="ricSelector" formControlName="ricSelector" matInput required [(value)]="ric"
-                placeholder="Target"
-                matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
-                <mat-option *ngFor="let ric of allRics" [value]="ric">
-                    {{ric.ric_id}}
-                </mat-option>
-            </mat-select>
-            <div *ngIf="ricSelector.invalid && (ricSelector.dirty || ricSelector.touched)">
-                <div *ngIf="ricSelector.errors.required">
-                    <mat-error role="alert">This field is required.</mat-error>
-                </div>
-            </div>
-        </mat-form-field>
-
-        <nrcp-typed-policy-editor [jsonSchemaObject]="jsonSchemaObject" [jsonObject]="data.instanceJson" [darkMode]="darkMode"></nrcp-typed-policy-editor>
-        <hr />
-        <button mat-raised-button (click)="onClose()">Close</button>
-        <button mat-raised-button (click)="onSubmit()" [disabled]="!isJsonFormValid || !ric"
-            class="submitBtn">Submit</button>
-        <hr />
+        <nrcp-ric-selector *ngIf="!policyInstanceId" [instanceForm]="instanceForm" [policyTypeName]="policyTypeName"></nrcp-ric-selector>
+        <h4 *ngIf="!typeHasSchema()">
+            Properties
+        </h4>
+        <nrcp-no-type-policy-editor *ngIf="!typeHasSchema()" [instanceForm]="instanceForm" [policyJson]="policyJson"></nrcp-no-type-policy-editor>
+        <nrcp-typed-policy-editor *ngIf="typeHasSchema()" [jsonSchemaObject]="jsonSchemaObject" [jsonObject]="policyJson" [darkMode]="darkMode"></nrcp-typed-policy-editor>
+        <div mat-dialog-actions>
+            <button id="closeButton"  mat-raised-button [mat-dialog-close]="false">Close</button>
+            <button id="submitButton" mat-raised-button (click)="onSubmit()" class="submitBtn" [disabled]="!isFormValid()">
+                Submit
+            </button>
+        </div>
     </mat-card>
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
index 7ecfff1..accb527 100644 (file)
     fill: #ffffff;
 }
 
-.text__dark {
-    color: white;
-}
-
-.header {
-    background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%);
-    font-size: 40px;
-    margin-top: 10px;
-    margin-bottom: 10px;
-    margin-left: 2px;
-    margin-right: 2px;
-}
-
 .header-dark {
     background: #2B244D;
 }
     margin-right: 10px;
 }
 
+.header {
+    background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%);
+    font-size: 40px;
+    margin-top: 10px;
+    margin-bottom: 10px;
+    margin-left: 2px;
+    margin-right: 2px;
+}
+
+.header-dark {
+    background: #2B244D;
+}
+
+
 .card {
     height: 100%;
     width: 100%;
@@ -69,4 +70,4 @@
 
 .card-dark {
     background-color: #1c1c24;
-}
\ No newline at end of file
+}
diff --git a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts
new file mode 100644 (file)
index 0000000..b4c2732
--- /dev/null
@@ -0,0 +1,429 @@
+/*-
+ * ========================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 { MatInputModule } from "@angular/material/input";
+import {
+  AbstractControl,
+  FormControl,
+  FormGroup,
+  ReactiveFormsModule,
+  Validators,
+} 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 { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component";
+import {
+  ChangeDetectorRef,
+  Component,
+  CUSTOM_ELEMENTS_SCHEMA,
+  Input,
+} from "@angular/core";
+import { TypedPolicyEditorComponent } from "../typed-policy-editor/typed-policy-editor.component";
+import { RicSelectorComponent } from "../ric-selector/ric-selector.component";
+import { NoTypePolicyEditorComponent } from "../no-type-policy-editor/no-type-policy-editor.component";
+import { By } from "@angular/platform-browser";
+
+describe("PolicyInstanceDialogComponent", () => {
+  const untypedSchema = "{}";
+  const typedSchema =
+    '{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}';
+
+  let component: PolicyInstanceDialogComponent;
+  let fixture: ComponentFixture<PolicyInstanceDialogComponent>;
+  let loader: HarnessLoader;
+  let policyServiceSpy: jasmine.SpyObj<PolicyService>;
+  let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
+
+  beforeEach(async () => {
+    policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]);
+    errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [
+      "displayError",
+    ]);
+
+    TestBed.configureTestingModule({
+      imports: [
+        BrowserAnimationsModule,
+        MatButtonModule,
+        MatDialogModule,
+        MatInputModule,
+        MatSelectModule,
+        ReactiveFormsModule,
+        ToastrModule.forRoot(),
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
+      declarations: [
+        PolicyInstanceDialogComponent,
+        RicSelectorStubComponent,
+        NoTypePolicyEditorStubComponent,
+        TypedPolicyEditorStubComponent,
+      ],
+      providers: [
+        ChangeDetectorRef,
+        { provide: MatDialogRef, useValue: component },
+        { provide: PolicyService, useValue: policyServiceSpy },
+        { provide: ErrorDialogService, useValue: errDialogServiceSpy },
+        { provide: MAT_DIALOG_DATA, useValue: true },
+        UiService,
+      ],
+    });
+  });
+
+  describe("content when creating policy without type", () => {
+    beforeEach(async () => {
+      const policyData = {
+        createSchema: untypedSchema,
+      };
+      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 < No Type >"
+      );
+
+      ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo");
+      expect(ele).toBeFalsy();
+    });
+
+    it("should contain ric select with instance form and no policy type", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance;
+      expect(ricSelector).toBeTruthy();
+      expect(ricSelector.instanceForm).toBeTruthy();
+      expect(ricSelector.policyTypeName).toBeFalsy();
+    });
+
+    it("should contain json editor with instance form and empty JSON", async () => {
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance;
+      expect(noTypePolicyEditor).toBeTruthy();
+      expect(noTypePolicyEditor.instanceForm).toBeTruthy();
+      expect(noTypePolicyEditor.policyJson).toEqual("{}");
+    });
+
+    it("should contain enabled Close button and disabled Submit button", async () => {
+      component.ngOnInit();
+      // Add an empty value with required validator to set the dialog's instance form to be invalid.
+      const value: any = null;
+      component.instanceForm.addControl(
+        "dummy",
+        new FormControl(value, [Validators.required])
+      );
+      expect(component.instanceForm.valid).toBeFalsy();
+
+      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 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 instance form and provided policy type", async () => {
+      const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance;
+      expect(ricSelector).toBeTruthy();
+      expect(ricSelector.instanceForm).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 () => {
+      component.ngOnInit();
+      // Add an empty value with required validator to set the dialog's instance form to be invalid.
+      const value: any = null;
+      component.instanceForm.addControl(
+        "dummy",
+        new FormControl(value, [Validators.required])
+      );
+      expect(component.instanceForm.valid).toBeFalsy();
+
+      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 without type", () => {
+    const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}';
+    beforeEach(async () => {
+      const policyData = {
+        createSchema: untypedSchema,
+        instanceId: "instanceId",
+        instanceJson: instanceJson,
+        name: "Type 1",
+        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 json editor with form and json data", async () => {
+      const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance;
+      expect(noTypePolicyEditor).toBeTruthy();
+      expect(noTypePolicyEditor.instanceForm).toBeTruthy();
+      expect(unescapeQuotes(noTypePolicyEditor.policyJson)).toEqual('"' + instanceJson + '"');
+    });
+
+    it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
+      expect(component.instanceForm.valid).toBeTruthy();
+
+      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");
+    });
+  });
+
+  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 () => {
+      expect(component.instanceForm.valid).toBeTruthy();
+
+      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
+) {
+  TestBed.compileComponents();
+
+  fixture = TestBed.createComponent(PolicyInstanceDialogComponent);
+  component = fixture.componentInstance;
+  fixture.detectChanges();
+  loader = TestbedHarnessEnvironment.loader(fixture);
+  return { fixture, component, loader };
+}
+
+function unescapeQuotes(string: string): string {
+  return string.replace(/\\"/g, '"');
+}
+
+@Component({
+  selector: "nrcp-ric-selector",
+  template: "",
+  providers: [
+    {
+      provide: RicSelectorComponent,
+      useClass: RicSelectorStubComponent,
+    },
+  ],
+})
+class RicSelectorStubComponent {
+  @Input() instanceForm: FormGroup;
+  @Input() policyTypeName: string = "";
+
+  get selectedRic(): string {
+    return "ric1";
+  }
+}
+
+@Component({
+  selector: "nrcp-no-type-policy-editor",
+  template: "",
+  providers: [
+    {
+      provide: NoTypePolicyEditorComponent,
+      useClass: NoTypePolicyEditorStubComponent,
+    },
+  ],
+})
+class NoTypePolicyEditorStubComponent {
+  @Input() instanceForm: FormGroup;
+  @Input() policyJson: string;
+
+  get policyJsonTextArea(): AbstractControl {
+    const textArea = { value: "{}" } as AbstractControl;
+    return textArea;
+  }
+}
+
+@Component({
+  selector: "nrcp-typed-policy-editor",
+  template: "",
+  providers: [
+    {
+      provide: TypedPolicyEditorComponent,
+      useClass: TypedPolicyEditorStubComponent,
+    },
+  ],
+})
+class TypedPolicyEditorStubComponent {
+  @Input() jsonSchemaObject: any = {};
+  @Input() jsonObject: any = {};
+  @Input() darkMode: boolean;
+
+  prettyLiveFormData = '"A": "string"';
+  get formIsValid(): boolean {
+    return true;
+  }
+}
index 3c2ea2b..096346e 100644 (file)
@@ -2,7 +2,7 @@
  * ========================LICENSE_START=================================
  * O-RAN-SC
  * %%
- * Copyright (C) 2019 Nordix Foundation
+ * Copyright (C) 2020 Nordix Foundation
  * %%
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core';
-import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import * as uuid from 'uuid';
-import { CreatePolicyInstance, PolicyInstance, PolicyTypeSchema } from '../../interfaces/policy.types';
-import { PolicyService } from '../../services/policy/policy.service';
-import { ErrorDialogService } from '../../services/ui/error-dialog.service';
-import { NotificationService } from './../../services/ui/notification.service';
-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, Rics } from '../../interfaces/ric';
-import { TypedPolicyEditorComponent } from '../typed-policy-editor/typed-policy-editor.component';
-
+import {
+  AfterViewInit,
+  ChangeDetectorRef,
+  Component,
+  Inject,
+  OnInit,
+  ViewChild,
+} from "@angular/core";
+import { FormGroup } from "@angular/forms";
+import {
+  MatDialogConfig,
+  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 {
+  CreatePolicyInstance,
+  PolicyInstance,
+  PolicyTypeSchema,
+} from "../../interfaces/policy.types";
+import { RicSelectorComponent } from "../ric-selector/ric-selector.component";
+import {
+  formatJsonString,
+  NoTypePolicyEditorComponent,
+} from "../no-type-policy-editor/no-type-policy-editor.component";
+import { TypedPolicyEditorComponent } from "../typed-policy-editor/typed-policy-editor.component";
 
 @Component({
-    selector: 'nrcp-policy-instance-dialog',
-    templateUrl: './policy-instance-dialog.component.html',
-    styleUrls: ['./policy-instance-dialog.component.scss']
+  selector: "nrcp-policy-instance-dialog",
+  templateUrl: "./policy-instance-dialog.component.html",
+  styleUrls: ["./policy-instance-dialog.component.scss"],
 })
 export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
-    @ViewChild(TypedPolicyEditorComponent)
-    policyEditor: TypedPolicyEditorComponent;
-    instanceForm: FormGroup;
+  instanceForm: FormGroup;
+  @ViewChild(RicSelectorComponent)
+  ricSelector: RicSelectorComponent;
+  @ViewChild(NoTypePolicyEditorComponent)
+  noTypePolicyEditor: NoTypePolicyEditorComponent;
+  @ViewChild(TypedPolicyEditorComponent)
+  typedPolicyEditor: TypedPolicyEditorComponent;
+  policyInstanceId: string; // null if not yet created
+  policyJson: string;
+  policyTypeName: string;
+  jsonSchemaObject: any;
+  darkMode: boolean;
+  ric: string;
+  allRicIds: string[] = [];
 
+  constructor(
+    private cdr: ChangeDetectorRef,
+    public dialogRef: MatDialogRef<PolicyInstanceDialogComponent>,
+    private policySvc: PolicyService,
+    private errorService: ErrorDialogService,
+    private notificationService: NotificationService,
+    @Inject(MAT_DIALOG_DATA) private data,
+    private ui: UiService
+  ) {
+    this.policyInstanceId = data.instanceId;
+    this.policyTypeName = data.name;
+    this.policyJson = data.instanceJson;
+    this.jsonSchemaObject = data.createSchema;
+    this.ric = data.ric;
+  }
 
-    ric: string;
-    allRics: Ric[];
-    policyInstanceId: string; // null if not yet created
-    policyTypeName: string;
-    jsonSchemaObject: any = {};
-    darkMode: boolean;
+  ngOnInit() {
+    this.ui.darkModeState.subscribe((isDark) => {
+      this.darkMode = isDark;
+    });
+    this.instanceForm = new FormGroup({});
+    this.formatNoTypePolicyJson();
+  }
 
-    private fetchRics() {
-        console.log('fetchRics ' + this.policyTypeName);
-        const self: PolicyInstanceDialogComponent = this;
-        this.dataService.getRics(this.policyTypeName).subscribe(
-            {
-                next(value: Rics) {
-                    self.allRics = value.rics;
-                    console.log(value);
-                }
-            });
-    }
+  // Do not remove! Needed to avoid "Expression has changed after it was checked" warning
+  ngAfterViewInit() {
+    this.cdr.detectChanges();
+  }
 
-    constructor(
-        private cdr: ChangeDetectorRef,
-        private dataService: PolicyService,
-        private errorService: ErrorDialogService,
-        private notificationService: NotificationService,
-        @Inject(MAT_DIALOG_DATA) public data,
-        private dialogRef: MatDialogRef<PolicyInstanceDialogComponent>,
-        private ui: UiService) {
-        this.policyInstanceId = data.instanceId;
-        this.policyTypeName = data.name;
-        this.jsonSchemaObject = data.createSchema;
-        this.ric = data.ric;
+  private formatNoTypePolicyJson() {
+    if (!this.typeHasSchema()) {
+      if (this.policyJson) {
+        this.policyJson = formatJsonString(this.policyJson);
+      } else {
+        this.policyJson = "{}";
+      }
     }
+  }
 
-    ngOnInit() {
-        this.ui.darkModeState.subscribe((isDark) => {
-            this.darkMode = isDark;
-        });
-        this.instanceForm = new FormGroup({
-            'ricSelector': new FormControl(this.ric, [
-                Validators.required
-            ])
-        });
-        if (!this.policyInstanceId) {
-            this.fetchRics();
-        }
+  onSubmit() {
+    if (this.policyInstanceId == null) {
+      this.policyInstanceId = uuid.v4();
     }
-
-    ngAfterViewInit() {
-        this.cdr.detectChanges();
+    const self: PolicyInstanceDialogComponent = this;
+    let policyData: string;
+    if (this.typeHasSchema()) {
+      policyData = this.typedPolicyEditor.prettyLiveFormData;
+    } else {
+      policyData = this.noTypePolicyEditor.policyJsonTextArea.value;
     }
+    let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(
+      policyData
+    );
+    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);
+      },
+      complete() {},
+    });
+  }
 
-    get ricSelector() { return this.instanceForm.get('ricSelector'); }
-
-    onSubmit() {
-        if (this.policyInstanceId == null) {
-            this.policyInstanceId = uuid.v4();
-        }
-        const policyJson: string = this.policyEditor.prettyLiveFormData;
-        const self: PolicyInstanceDialogComponent = this;
-        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);
-                },
-                complete() { }
-            });
-    }
+  typeHasSchema(): boolean {
+    return this.jsonSchemaObject !== "{}";
+  }
 
-    private createPolicyInstance(policyJson: string) {
-        let createPolicyInstance = {} as CreatePolicyInstance;
-        createPolicyInstance.policy_data = JSON.parse(policyJson);
-        createPolicyInstance.policy_id = this.policyInstanceId;
-        createPolicyInstance.policytype_id = this.policyTypeName;
-        createPolicyInstance.ric_id = (!this.ricSelector.value.ric_id) ? this.ric : this.ricSelector.value.ric_id;
-        createPolicyInstance.service_id = 'controlpanel';
-        return createPolicyInstance;
+  isFormValid(): boolean {
+    let isValid: boolean = this.instanceForm.valid;
+    if (this.typeHasSchema()) {
+      isValid =
+        isValid && this.typedPolicyEditor
+          ? this.typedPolicyEditor.formIsValid
+          : false;
     }
+    return isValid;
+  }
 
-    onClose() {
-        this.dialogRef.close();
-    }
-
-    get isJsonFormValid(): boolean {
-        return this.policyEditor ? this.policyEditor.formIsValid : false;
-    }
+  private createPolicyInstance(policyJson: string): CreatePolicyInstance {
+    let createPolicyInstance = {} as CreatePolicyInstance;
+    createPolicyInstance.policy_data = JSON.parse(policyJson);
+    createPolicyInstance.policy_id = this.policyInstanceId;
+    createPolicyInstance.policytype_id = "";
+    createPolicyInstance.ric_id = this.ricSelector
+      ? this.ricSelector.selectedRic
+      : this.ric;
+    createPolicyInstance.service_id = "controlpanel";
+    return createPolicyInstance;
+  }
 }
 
-export function getPolicyDialogProperties(policyTypeSchema: PolicyTypeSchema, instance: PolicyInstance, darkMode: boolean): MatDialogConfig {
-    const createSchema = policyTypeSchema.schemaObject;
-    const instanceId = instance ? instance.policy_id : null;
-    const instanceJson = instance ? instance.policy_data : null;
-    const name = policyTypeSchema.name;
-    const ric = instance ? instance.ric_id : null;
-    return {
-        maxWidth: '1200px',
-        maxHeight: '900px',
-        width: '900px',
-        role: 'dialog',
-        disableClose: false,
-        panelClass: darkMode ? 'dark-theme' : '',
-        data: {
-            createSchema,
-            instanceId,
-            instanceJson,
-            name,
-            ric
-        }
-    };
+export function getPolicyDialogProperties(
+  policyTypeSchema: PolicyTypeSchema,
+  instance: PolicyInstance,
+  darkMode: boolean
+): MatDialogConfig {
+  const createSchema = policyTypeSchema.schemaObject;
+  const instanceId = instance ? instance.policy_id : null;
+  const instanceJson = instance ? instance.policy_data : null;
+  const name = policyTypeSchema.name;
+  const ric = instance ? instance.ric_id : null;
+  return {
+    maxWidth: "1200px",
+    maxHeight: "900px",
+    width: "900px",
+    role: "dialog",
+    disableClose: false,
+    panelClass: darkMode ? "dark-theme" : "",
+    data: {
+      createSchema,
+      instanceId,
+      instanceJson,
+      name,
+      ric,
+    },
+  };
 }
-
index 95313d0..0f34c43 100644 (file)
@@ -28,7 +28,6 @@ import { NotificationService } from '../../services/ui/notification.service';
 import { PolicyService } from '../../services/policy/policy.service';
 import { ConfirmDialogService } from '../../services/ui/confirm-dialog.service';
 import { PolicyInstance } from '../../interfaces/policy.types';
-import { NoTypePolicyInstanceDialogComponent } from '../no-type-policy-instance-dialog/no-type-policy-instance-dialog.component';
 import { PolicyInstanceDialogComponent } from '../policy-instance-dialog/policy-instance-dialog.component';
 import { getPolicyDialogProperties } from '../policy-instance-dialog/policy-instance-dialog.component';
 import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
@@ -84,24 +83,13 @@ export class PolicyInstanceComponent implements OnInit, AfterViewInit {
         this.policySvc.getPolicyInstance(instance.policy_id).subscribe(
             (refreshedJson: any) => {
                 instance = refreshedJson;
-                if (this.isSchemaEmpty()) {
-                    this.dialog.open(
-                        NoTypePolicyInstanceDialogComponent,
-                        getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe(
-                            (_: any) => {
-                                this.instanceDataSource.getPolicyInstances();
-                            }
-                        );
-                } else {
-                    this.dialog.open(
-                        PolicyInstanceDialogComponent,
-                        getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe(
-                            (_: any) => {
-                                this.instanceDataSource.getPolicyInstances();
-                            }
-                        );
-
-                }
+                this.dialog.open(
+                    PolicyInstanceDialogComponent,
+                    getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe(
+                        (_: any) => {
+                            this.instanceDataSource.getPolicyInstances();
+                        }
+                    );
             },
             (httpError: HttpErrorResponse) => {
                 this.notificationService.error('Could not refresh instance. Please try again.' + httpError.message);
index 5f7e4fc..d4a4b1e 100644 (file)
@@ -23,7 +23,6 @@ import { NgModule } from '@angular/core';
 import { MatTableModule } from '@angular/material/table';
 import { PolicyCardComponent } from './policy-card/policy-card.component';
 import { PolicyControlComponent } from './policy-control.component';
-import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog/no-type-policy-instance-dialog.component';
 import { PolicyInstanceDialogComponent } from './policy-instance-dialog/policy-instance-dialog.component';
 import { PolicyInstanceComponent } from './policy-instance/policy-instance.component';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -52,8 +51,8 @@ import { MaterialDesignFrameworkModule } from 'angular6-json-schema-form';
 import { FlexLayoutModule } from '@angular/flex-layout';
 import { Routes, RouterModule } from '@angular/router';
 import { RicSelectorComponent } from './ric-selector/ric-selector.component';
-import { NoTypePolicyEditorComponent } from './no-type-policy-editor/no-type-policy-editor.component';
 import { TypedPolicyEditorComponent } from './typed-policy-editor/typed-policy-editor.component';
+import { NoTypePolicyEditorComponent } from './no-type-policy-editor/no-type-policy-editor.component';
 
 const routes:Routes = [
   {path: 'policy', component: PolicyControlComponent}
@@ -61,7 +60,6 @@ const routes:Routes = [
 
 @NgModule({
   declarations: [
-    NoTypePolicyInstanceDialogComponent,
     PolicyCardComponent,
     PolicyControlComponent,
     PolicyInstanceComponent,
index e0ba87e..996d9a0 100644 (file)
 //   ========================LICENSE_END===================================
 //  /
 
-import { Component, Input, OnInit } from '@angular/core';
-import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
-import { Rics } from 'src/app/interfaces/ric';
-import { PolicyService } from 'src/app/services/policy/policy.service';
+import { Component, Input, OnInit } from "@angular/core";
+import {
+  AbstractControl,
+  ControlContainer,
+  FormBuilder,
+  FormControl,
+  FormGroup,
+  FormGroupDirective,
+  Validators,
+} from "@angular/forms";
+import { Rics } from "src/app/interfaces/ric";
+import { PolicyService } from "src/app/services/policy/policy.service";
 
 @Component({
-  selector: 'nrcp-ric-selector',
-  templateUrl: './ric-selector.component.html',
-  styleUrls: ['./ric-selector.component.scss'],
-  viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
-
+  selector: "nrcp-ric-selector",
+  templateUrl: "./ric-selector.component.html",
+  styleUrls: ["./ric-selector.component.scss"],
+  viewProviders: [
+    { provide: ControlContainer, useExisting: FormGroupDirective },
+  ],
 })
 export class RicSelectorComponent implements OnInit {
-
   @Input() instanceForm: FormGroup;
-  @Input() policyTypeName: string = '';
+  @Input() policyTypeName: string = "";
   ric: string;
   allRics: string[] = [];
 
   constructor(
     private dataService: PolicyService,
-    private formBuilder: FormBuilder) {
-  }
+    private formBuilder: FormBuilder
+  ) {}
 
   ngOnInit(): void {
     this.instanceForm.addControl(
-      'ricSelector', new FormControl(this.ric, [
-        Validators.required
-      ]));
+      "ricSelector",
+      new FormControl(this.ric, [Validators.required])
+    );
 
-    console.log('Ric:', this.ric);
+    console.log("Ric:", this.ric);
     this.fetchRics();
   }
 
-  get selectedRic(): string { return this.ric; }
+  get selectedRic(): string {
+    return this.ric;
+  }
 
   get ricSelector(): AbstractControl {
-    return this.instanceForm.get('ricSelector');
+    return this.instanceForm.get("ricSelector");
   }
 
   private fetchRics() {
-    console.log('fetchRics ', this.policyTypeName);
+    console.log("fetchRics ", this.policyTypeName);
     const self: RicSelectorComponent = this;
-    this.dataService.getRics(this.policyTypeName).subscribe(
-      {
-        next(value: Rics) {
-          value.rics.forEach(ric => {
-            self.allRics.push(ric.ric_id)
-          });
-          console.log(value);
-        }
-      });
+    this.dataService.getRics(this.policyTypeName).subscribe({
+      next(value: Rics) {
+        value.rics.forEach((ric) => {
+          self.allRics.push(ric.ric_id);
+        });
+        console.log(value);
+      },
+    });
   }
 }
index 9e6e1e4..6b8c233 100644 (file)
 
 -->
 
-<h4 class="default-cursor" (click)="toggleVisible('form')">
-    <mat-icon matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
+<h4 id="propertiesHeading" class="default-cursor" (click)="toggleVisible('form')">
+    <mat-icon id="propertiesIcon" matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
     Properties
 </h4>
-<div *ngIf="isVisible.form" class="json-schema-form" [@expandSection]="true">
-    <div *ngIf="!formActive">{{jsonFormStatusMessage}}</div>
-
-    <json-schema-form id="json-schema-form" *ngIf="formActive" [form]="jsonSchemaObject"
+<div id="propertiesDiv" *ngIf="isVisible.form" class="json-schema-form" [@expandSection]="true">
+    <json-schema-form id="json-schema-form" [form]="jsonSchemaObject"
         [(data)]="jsonObject" [options]="jsonFormOptions" [framework]="'material-design'" [language]="'en'"
         (onChanges)="onChanges($event)" (isValid)="isValid($event)"
         (validationErrors)="validationErrors($event)">
     </json-schema-form>
 </div>
-<h4 [class.text-danger]="!formIsValid && !isVisible.json" [class.default-cursor]="formIsValid || isVisible.json"
+<h4 id="jsonHeading" [class.text-danger]="!formIsValid && !isVisible.json" [class.default-cursor]="formIsValid || isVisible.json"
 (click)="toggleVisible('json')">
-<mat-icon matTooltip="Json">{{isVisible.json ? 'expand_less' : 'expand_more'}}</mat-icon>
-Json
+<mat-icon id="jsonIcon" matTooltip="Json">{{isVisible.json ? 'expand_less' : 'expand_more'}}</mat-icon>
+JSON
 </h4>
-<div *ngIf="isVisible.json" fxLayout="column" [@expandSection]="true">
+<div id="jsonDiv" *ngIf="isVisible.json" fxLayout="column" [@expandSection]="true">
 <div>
     <strong *ngIf="formIsValid || prettyValidationErrors" [class.text-muted]="formIsValid"
         [class.text-danger]="!formIsValid">
@@ -53,11 +51,11 @@ Json
 </div>
 </div>
 
-<h4 class="default-cursor" (click)="toggleVisible('schema')">
-<mat-icon matTooltip="Json Schema">{{isVisible.schema ? 'expand_less' : 'expand_more'}}</mat-icon>
-Json Schema
+<h4 id="schemaHeading" class="default-cursor" (click)="toggleVisible('schema')">
+<mat-icon id="schemaIcon" matTooltip="Json Schema">{{isVisible.schema ? 'expand_less' : 'expand_more'}}</mat-icon>
+JSON Schema
 </h4>
-<div *ngIf="isVisible.schema" fxLayout="column" [@expandSection]="true">
+<div id="schemaDiv" *ngIf="isVisible.schema" fxLayout="column" [@expandSection]="true">
 <strong class="text-muted">Schema</strong>
 <pre [class.text__dark]="darkMode">{{schemaAsString}}</pre>
 </div>
index 9f58af2..fc4a166 100644 (file)
-import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/compiler';
-import { ChangeDetectorRef } from '@angular/core';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { MatIconModule } from '@angular/material/icon';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+// -
+//   ========================LICENSE_START=================================
+//   O-RAN-SC
+//   %%
+//   Copyright (C) 2021: 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 { TypedPolicyEditorComponent } from './typed-policy-editor.component';
+import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/compiler";
+import { Component } from "@angular/core";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { MatIconModule } from "@angular/material/icon";
+import { BrowserModule } from "@angular/platform-browser";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 
-describe('TypedPolicyEditorComponent', () => {
-  let component: TypedPolicyEditorComponent;
-  let fixture: ComponentFixture<TypedPolicyEditorComponent>;
+import { TypedPolicyEditorComponent } from "./typed-policy-editor.component";
 
-  beforeEach(async(() => {
+describe("TypedPolicyEditorComponent", () => {
+  let component: TestTypedPolicyEditorComponentHostComponent;
+  let fixture: ComponentFixture<TestTypedPolicyEditorComponentHostComponent>;
+
+  beforeEach(async () => {
     TestBed.configureTestingModule({
-      imports: [
-        BrowserAnimationsModule,
-        MatIconModule
-      ],
+      imports: [BrowserModule, BrowserAnimationsModule, MatIconModule],
       declarations: [
-        TypedPolicyEditorComponent
-      ],
-      schemas: [
-        CUSTOM_ELEMENTS_SCHEMA
+        TypedPolicyEditorComponent,
+        TestTypedPolicyEditorComponentHostComponent,
       ],
-      providers: [
-        ChangeDetectorRef
-      ]
-    })
-    .compileComponents();
-  }));
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
+    }).compileComponents();
+  });
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(TypedPolicyEditorComponent);
+    fixture = TestBed.createComponent(
+      TestTypedPolicyEditorComponentHostComponent
+    );
     component = fixture.componentInstance;
     fixture.detectChanges();
   });
 
-  it('should create', () => {
+  it("should create", () => {
     expect(component).toBeTruthy();
   });
+
+  it("should have JSON form visible and JSON and JSON Schema not visible", () => {
+    let propertiesHeading = fixture.debugElement.nativeElement.querySelector(
+      "#propertiesHeading"
+    );
+    expect(propertiesHeading).toBeTruthy();
+    expect(propertiesHeading.innerText).toContain("Properties");
+    let propertiesIcon = fixture.debugElement.nativeElement.querySelector(
+      "#propertiesIcon"
+    );
+    expect(propertiesIcon).toBeTruthy();
+    expect(propertiesIcon.innerText).toEqual("expand_less");
+    let jsonForm = fixture.debugElement.nativeElement.querySelector(
+      "json-schema-form"
+    );
+    expect(jsonForm).toBeTruthy();
+
+    let jsonHeading = fixture.debugElement.nativeElement.querySelector(
+      "#jsonHeading"
+    );
+    expect(jsonHeading).toBeTruthy();
+    expect(jsonHeading.innerText).toContain("JSON");
+    let jsonIcon = fixture.debugElement.nativeElement.querySelector(
+      "#jsonIcon"
+    );
+    expect(jsonIcon).toBeTruthy();
+    expect(jsonIcon.innerText).toEqual("expand_more");
+    let jsonDiv = fixture.debugElement.nativeElement.querySelector("#jsonDiv");
+    expect(jsonDiv).toBeFalsy();
+
+    let schemaHeading = fixture.debugElement.nativeElement.querySelector(
+      "#schemaHeading"
+    );
+    expect(schemaHeading).toBeTruthy();
+    expect(schemaHeading.innerText).toContain("JSON Schema");
+    let schemaIcon = fixture.debugElement.nativeElement.querySelector(
+      "#schemaIcon"
+    );
+    expect(schemaIcon).toBeTruthy();
+    expect(schemaIcon.innerText).toEqual("expand_more");
+    let schemaDiv = fixture.debugElement.nativeElement.querySelector(
+      "#schemaDiv"
+    );
+    expect(schemaDiv).toBeFalsy();
+  });
+
+  it("should hide JSON form", () => {
+    let propertiesHeading = fixture.debugElement.nativeElement.querySelector(
+      "#propertiesHeading"
+    );
+    expect(propertiesHeading).toBeTruthy();
+    propertiesHeading.click();
+    fixture.detectChanges();
+
+    let propertiesIcon = fixture.debugElement.nativeElement.querySelector(
+      "#propertiesIcon"
+    );
+    expect(propertiesIcon).toBeTruthy();
+    expect(propertiesIcon.innerText).toEqual("expand_more");
+    let propertiesDiv = fixture.debugElement.nativeElement.querySelector(
+      "propertiesDiv"
+    );
+    expect(propertiesDiv).toBeFalsy();
+  });
+
+  it("should show JSON with text for dark mode", () => {
+    let jsonHeading = fixture.debugElement.nativeElement.querySelector(
+      "#jsonHeading"
+    );
+    expect(jsonHeading).toBeTruthy();
+    jsonHeading.click();
+    fixture.detectChanges();
+
+    let jsonIcon = fixture.debugElement.nativeElement.querySelector(
+      "#jsonIcon"
+    );
+    expect(jsonIcon).toBeTruthy();
+    expect(jsonIcon.innerText).toEqual("expand_less");
+    let jsonDiv = fixture.debugElement.nativeElement.querySelector("#jsonDiv");
+    expect(jsonDiv).toBeTruthy();
+    let jsonText = jsonDiv.querySelector("pre");
+    expect(jsonText.classList).toContain("text__dark");
+  });
+
+  it("should show JSON Schema with text for dark mode", () => {
+    let schemaHeading = fixture.debugElement.nativeElement.querySelector(
+      "#schemaHeading"
+    );
+    expect(schemaHeading).toBeTruthy();
+    schemaHeading.click();
+    fixture.detectChanges();
+
+    let schemaIcon = fixture.debugElement.nativeElement.querySelector(
+      "#schemaIcon"
+    );
+    expect(schemaIcon).toBeTruthy();
+    expect(schemaIcon.innerText).toEqual("expand_less");
+    let schemaDiv = fixture.debugElement.nativeElement.querySelector(
+      "#schemaDiv"
+    );
+    expect(schemaDiv).toBeTruthy();
+    let jsonSchemaText = schemaDiv.querySelector("pre");
+    expect(jsonSchemaText.classList).toContain("text__dark");
+  });
+
+  @Component({
+    selector: `typed-policy-editor-host-component`,
+    template: `<nrcp-typed-policy-editor
+      [jsonObject]="policyJson"
+      [jsonSchemaObject]="jsonSchemaObject"
+      [darkMode]="true"
+    ></nrcp-typed-policy-editor>`,
+  })
+  class TestTypedPolicyEditorComponentHostComponent {
+    policyJson: string = '{"A":"A"}';
+    jsonSchemaObject: string =
+      'policy_schema": { "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "A": "string" }, "required": [ "A" ]}';
+  }
 });
index d05c78f..f9651a4 100644 (file)
 //
 
 import { animate, state, style, transition, trigger } from '@angular/animations';
-import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
 import { JsonPointer } from 'angular6-json-schema-form';
 
 @Component({
-    selector: 'nrcp-typed-policy-editor',
-    templateUrl: './typed-policy-editor.component.html',
-    styleUrls: ['./typed-policy-editor.component.scss'],
-    animations: [
-        trigger('expandSection', [
-            state('in', style({ height: '*' })),
-            transition(':enter', [
-                style({ height: 0 }), animate(100),
-            ]),
-            transition(':leave', [
-                style({ height: '*' }),
-                animate(100, style({ height: 0 })),
-            ]),
-        ]),
-    ],
-
+  selector: "nrcp-typed-policy-editor",
+  templateUrl: "./typed-policy-editor.component.html",
+  styleUrls: ["./typed-policy-editor.component.scss"],
+  animations: [
+    trigger("expandSection", [
+      state("in", style({ height: "*" })),
+      transition(":enter", [style({ height: 0 }), animate(100)]),
+      transition(":leave", [
+        style({ height: "*" }),
+        animate(100, style({ height: 0 })),
+      ]),
+    ]),
+  ],
 })
 export class TypedPolicyEditorComponent implements OnInit {
-    jsonFormOptions: any = {
-        addSubmit: false, // Add a submit button if layout does not have one
-        debug: false, // Don't show inline debugging information
-        loadExternalAssets: false, // Load external css and JavaScript for frameworks
-        returnEmptyFields: false, // Don't return values for empty input fields
-        setSchemaDefaults: true, // Always use schema defaults for empty fields
-        defautWidgetOptions: { feedback: true }, // Show inline feedback icons
-    };
-
-    @Input() jsonSchemaObject: any = {};
-    @Input() jsonObject: any = {};
-    @Input() darkMode: boolean;
-
-    isVisible = {
-        form: true,
-        json: false,
-        schema: false
-    };
-    formActive: boolean = false;
-    jsonFormStatusMessage: string = 'Loading form...';
-    liveFormData: any = {};
-    formIsValid: boolean = false;
-    formValidationErrors: any;
-
-    constructor(
-        private cdr: ChangeDetectorRef) {
-        this.formActive = false;
-    }
-
-    ngOnInit(): void {
-         this.formActive = true;
-    }
-
-    ngAfterViewInit() {
-        this.cdr.detectChanges();
-    }
-
-    public onChanges(formData: any) {
-        this.liveFormData = formData;
-    }
-
-    get prettyLiveFormData(): string {
-        return JSON.stringify(this.liveFormData, null, 2);
-    }
-
-    get schemaAsString(): string {
-        return JSON.stringify(this.jsonSchemaObject, null, 2);
+  jsonFormOptions: any = {
+    addSubmit: false, // Add a submit button if layout does not have one
+    debug: false, // Don't show inline debugging information
+    loadExternalAssets: false, // Load external css and JavaScript for frameworks
+    returnEmptyFields: false, // Don't return values for empty input fields
+    setSchemaDefaults: true, // Always use schema defaults for empty fields
+    defautWidgetOptions: { feedback: true }, // Show inline feedback icons
+  };
+
+  @Input() jsonSchemaObject: any = {};
+  @Input() jsonObject: any = {};
+  @Input() darkMode: boolean;
+
+  isVisible = {
+    form: true,
+    json: false,
+    schema: false,
+  };
+  liveFormData: any = {};
+  formIsValid: boolean = false;
+  formValidationErrors: any;
+
+  constructor() {}
+
+  ngOnInit(): void {}
+
+  public onChanges(formData: any) {
+    this.liveFormData = formData;
+  }
+
+  get prettyLiveFormData(): string {
+    return JSON.stringify(this.liveFormData, null, 2);
+  }
+
+  get schemaAsString(): string {
+    return JSON.stringify(this.jsonSchemaObject, null, 2);
+  }
+
+  get jsonAsString(): string {
+    return JSON.stringify(this.jsonObject, null, 2);
+  }
+
+  isValid(isValid: boolean): void {
+    this.formIsValid = isValid;
+  }
+
+  validationErrors(validationErrors: any): void {
+    this.formValidationErrors = validationErrors;
+  }
+
+  get prettyValidationErrors() {
+    if (!this.formValidationErrors) {
+      return null;
     }
-
-    get jsonAsString(): string {
-        return JSON.stringify(this.jsonObject, null, 2);
-    }
-
-    isValid(isValid: boolean): void {
-        this.formIsValid = isValid;
-    }
-
-    validationErrors(validationErrors: any): void {
-        this.formValidationErrors = validationErrors;
-    }
-
-    get prettyValidationErrors() {
-        if (!this.formValidationErrors) { return null; }
-        const errorArray = [];
-        for (const error of this.formValidationErrors) {
-            const message = error.message;
-            const dataPathArray = JsonPointer.parse(error.dataPath);
-            if (dataPathArray.length) {
-                let field = dataPathArray[0];
-                for (let i = 1; i < dataPathArray.length; i++) {
-                    const key = dataPathArray[i];
-                    field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
-                }
-                errorArray.push(`${field}: ${message}`);
-            } else {
-                errorArray.push(message);
-            }
+    const errorArray = [];
+    for (const error of this.formValidationErrors) {
+      const message = error.message;
+      const dataPathArray = JsonPointer.parse(error.dataPath);
+      if (dataPathArray.length) {
+        let field = dataPathArray[0];
+        for (let i = 1; i < dataPathArray.length; i++) {
+          const key = dataPathArray[i];
+          field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
         }
-        return errorArray.join('<br>');
+        errorArray.push(`${field}: ${message}`);
+      } else {
+        errorArray.push(message);
+      }
     }
+    return errorArray.join("<br>");
+  }
 
-    public toggleVisible(item: string) {
-        this.isVisible[item] = !this.isVisible[item];
-    }
+  public toggleVisible(item: string) {
+    this.isVisible[item] = !this.isVisible[item];
+  }
 }