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 { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component";
import { TypedPolicyEditorComponent } from "@policy/typed-policy-editor/typed-policy-editor.component";
import { CreatePolicyInstance } from "@interfaces/policy.types";
import { NotificationService } from "@services/ui/notification.service";
import * as uuid from "uuid";
+import { HttpErrorResponse } from "@angular/common/http";
describe("PolicyInstanceDialogComponent", () => {
const untypedSchema = JSON.parse("{}");
- const typedSchema =
- JSON.parse('{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}');
+ const typedSchema = JSON.parse(
+ '{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}'
+ );
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 () => {
dialogRefSpy = jasmine.createSpyObj("MatDialogRef", ["close"]);
policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]);
- errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [
- "displayError",
- ]);
notificationServiceSpy = jasmine.createSpyObj("NotificationService", [
"success",
]);
ChangeDetectorRef,
{ provide: MatDialogRef, useValue: dialogRefSpy },
{ provide: PolicyService, useValue: policyServiceSpy },
- { provide: ErrorDialogService, useValue: errDialogServiceSpy },
{ provide: NotificationService, useValue: notificationServiceSpy },
{ provide: MAT_DIALOG_DATA, useValue: true },
UiService,
expect(await submitButton.isDisabled()).toBeFalsy();
});
- it("should generate policy ID when submitting new policy", async () => {
+ it("should generate policy ID when submitting new policy and close dialog", async () => {
const ricSelector: RicSelectorComponent = fixture.debugElement.query(
By.directive(RicSelectorComponent)
).componentInstance;
spyOn(uuid, "v4").and.returnValue("1234567890");
ricSelector.selectedRic.emit("ric1");
noTypePolicyEditor.validJson.emit("{}");
+
+ policyServiceSpy.putPolicy.and.returnValue(of("Success"));
+
await submitButton.click();
const policyInstance = {} as CreatePolicyInstance;
policyInstance.ric_id = "ric1";
policyInstance.service_id = "controlpanel";
expect(policyServiceSpy.putPolicy).toHaveBeenCalledWith(policyInstance);
+
+ expect(dialogRefSpy.close).toHaveBeenCalledWith("ok");
+ });
+
+ it("should not close dialog when error from server", async () => {
+ let submitButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#submitButton" })
+ );
+
+ const errorResponse = {
+ status: 400,
+ statusText: "Bad Request",
+ } as HttpErrorResponse;
+ policyServiceSpy.putPolicy.and.returnValue(errorResponse);
+
+ await submitButton.click();
+
+ expect(policyServiceSpy.putPolicy).toHaveBeenCalled();
+
+ expect(dialogRefSpy.close).not.toHaveBeenCalled();
});
});
});
describe("content when editing policy without type", () => {
- const instanceJson = JSON.parse('{"qosObjectives": {"priorityLevel": 3100}}');
+ const instanceJson = JSON.parse(
+ '{"qosObjectives": {"priorityLevel": 3100}}'
+ );
beforeEach(async () => {
const policyData = {
createSchema: untypedSchema,
By.directive(NoTypePolicyEditorComponent)
).componentInstance;
expect(noTypePolicyEditor).toBeTruthy();
- expect(noTypePolicyEditor.policyJson).toEqual(
- instanceJson
- );
+ expect(noTypePolicyEditor.policyJson).toEqual(instanceJson);
});
it("should contain enabled Close and Submit buttons when all inputs are valid", async () => {
return (
typeof policy1.policy_data === "object" &&
typeof policy2.policy_data === "object" &&
- JSON.stringify(policy1.policy_data) === JSON.stringify(policy2.policy_data) &&
+ 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 &&
* ========================LICENSE_END===================================
*/
-import { Component, ViewChild } from "@angular/core";
-import { async, ComponentFixture, TestBed } from "@angular/core/testing";
+import { HarnessLoader } from "@angular/cdk/testing";
+import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
+import { HttpResponse } from "@angular/common/http";
+import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+import { MatButtonHarness } from "@angular/material/button/testing";
import { MatDialog } from "@angular/material/dialog";
+import { MatIconModule } from "@angular/material/icon";
+import { MatInputHarness } from "@angular/material/input/testing";
+import { MatSortModule } from "@angular/material/sort";
+import { MatSortHarness } from "@angular/material/sort/testing";
+import { MatTableModule } from "@angular/material/table";
+import { MatTableHarness } from "@angular/material/table/testing";
+import { By } from "@angular/platform-browser";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import {
PolicyInstance,
PolicyInstances,
} from "@app/interfaces/policy.types";
import { PolicyService } from "@app/services/policy/policy.service";
import { ConfirmDialogService } from "@app/services/ui/confirm-dialog.service";
-import { ErrorDialogService } from "@app/services/ui/error-dialog.service";
import { NotificationService } from "@app/services/ui/notification.service";
import { UiService } from "@app/services/ui/ui.service";
import { ToastrModule } from "ngx-toastr";
-import { of } from "rxjs";
+import { Observable, of } from "rxjs";
+import { PolicyInstanceDialogComponent } from "../policy-instance-dialog/policy-instance-dialog.component";
import { PolicyInstanceComponent } from "./policy-instance.component";
+const lastModifiedTime = "2021-01-26T13:15:11.895297Z";
describe("PolicyInstanceComponent", () => {
let hostComponent: PolicyInstanceComponentHostComponent;
+ let componentUnderTest: PolicyInstanceComponent;
let hostFixture: ComponentFixture<PolicyInstanceComponentHostComponent>;
+ let loader: HarnessLoader;
let policyServiceSpy: jasmine.SpyObj<PolicyService>;
let dialogSpy: jasmine.SpyObj<MatDialog>;
+ let notificationServiceSpy: jasmine.SpyObj<NotificationService>;
+ let confirmServiceSpy: jasmine.SpyObj<ConfirmDialogService>;
+
+ const policyInstances = {
+ policy_ids: ["policy1", "policy2"],
+ } as PolicyInstances;
+ const policyTypeSchema = JSON.parse(
+ '{"title": "1", "description": "Type 1 policy type"}'
+ );
+ const policy1 = {
+ policy_id: "policy1",
+ policy_data: "{}",
+ ric_id: "1",
+ service_id: "service",
+ lastModified: "Now",
+ } as PolicyInstance;
+ const policy2 = {
+ policy_id: "policy2",
+ policy_data: "{}",
+ ric_id: "2",
+ service_id: "service",
+ lastModified: "Now",
+ } as PolicyInstance;
+ const policy1Status = {
+ last_modified: lastModifiedTime,
+ } as PolicyStatus;
+ const policy2Status = {
+ last_modified: lastModifiedTime,
+ } as PolicyStatus;
+
+ const policyIdToInstanceMap = {
+ policy1: policy1,
+ policy2: policy2,
+ };
+ const policyIdToStatusMap = {
+ policy1: policy1Status,
+ policy2: policy2Status,
+ };
@Component({
selector: "policy-instance-compnent-host-component",
"<nrcp-policy-instance [policyTypeSchema]=policyType></nrcp-policy-instance>",
})
class PolicyInstanceComponentHostComponent {
- @ViewChild(PolicyInstanceComponent)
- componentUnderTest: PolicyInstanceComponent;
- policyTypeSchema = JSON.parse(
- '{"title": "1", "description": "Type 1 policy type"}'
- );
policyType = {
id: "type1",
name: "1",
- schemaObject: this.policyTypeSchema,
+ schemaObject: policyTypeSchema,
} as PolicyTypeSchema;
}
- beforeEach(async(() => {
+ beforeEach(async () => {
policyServiceSpy = jasmine.createSpyObj("PolicyService", [
"getPolicyInstancesByType",
"getPolicyInstance",
"getPolicyStatus",
+ "deletePolicy",
]);
- let policyInstances = { policy_ids: ["policy1", "policy2"] } as PolicyInstances;
policyServiceSpy.getPolicyInstancesByType.and.returnValue(
of(policyInstances)
);
- let policy1 = {
- policy_id: "policy1",
- policy_data: "{}",
- ric_id: "1",
- service_id: "service",
- lastModified: "Now",
- } as PolicyInstance;
- let policy2 = {
- policy_id: "policy2",
- policy_data: "{}",
- ric_id: "2",
- service_id: "service",
- lastModified: "Now",
- } as PolicyInstance;
- policyServiceSpy.getPolicyInstance.and.returnValues(
- of(policy1),
- of(policy2)
- );
- let policy1Status = { last_modified: "Just now" } as PolicyStatus;
- let policy2Status = { last_modified: "Before" } as PolicyStatus;
- policyServiceSpy.getPolicyStatus.and.returnValues(
- of(policy1Status),
- of(policy2Status)
- );
+ policyServiceSpy.getPolicyInstance.and.callFake(function (
+ policyId: string
+ ) {
+ return of(policyIdToInstanceMap[policyId]);
+ });
+ policyServiceSpy.getPolicyStatus.and.callFake(function (policyId: string) {
+ return of(policyIdToStatusMap[policyId]);
+ });
dialogSpy = jasmine.createSpyObj("MatDialog", ["open"]);
+ notificationServiceSpy = jasmine.createSpyObj("NotificationService", [
+ "success",
+ "warn",
+ ]);
+ confirmServiceSpy = jasmine.createSpyObj("ConfirmDialogService", [
+ "openConfirmDialog",
+ ]);
- TestBed.configureTestingModule({
- imports: [ToastrModule.forRoot()],
+ await TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ FormsModule,
+ MatIconModule,
+ MatSortModule,
+ MatTableModule,
+ ReactiveFormsModule,
+ ToastrModule.forRoot(),
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [
PolicyInstanceComponent,
PolicyInstanceComponentHostComponent,
providers: [
{ provide: PolicyService, useValue: policyServiceSpy },
{ provide: MatDialog, useValue: dialogSpy },
- ErrorDialogService,
- NotificationService,
- ConfirmDialogService,
+ { provide: NotificationService, useValue: notificationServiceSpy },
+ { provide: ConfirmDialogService, useValue: confirmServiceSpy },
UiService,
],
}).compileComponents();
- }));
- beforeEach(() => {
hostFixture = TestBed.createComponent(PolicyInstanceComponentHostComponent);
hostComponent = hostFixture.componentInstance;
+ componentUnderTest = hostFixture.debugElement.query(
+ By.directive(PolicyInstanceComponent)
+ ).componentInstance;
hostFixture.detectChanges();
+ loader = TestbedHarnessEnvironment.loader(hostFixture);
});
it("should create", () => {
expect(hostComponent).toBeTruthy();
+
+ expect(componentUnderTest).toBeTruthy();
+ });
+
+ it("should set correct dark mode from UIService", () => {
+ const uiService: UiService = TestBed.inject(UiService);
+ expect(componentUnderTest.darkMode).toBeTruthy();
+
+ uiService.darkModeState.next(false);
+ hostFixture.detectChanges();
+ expect(componentUnderTest.darkMode).toBeFalsy();
+ });
+
+ it("should contain number of instances heading and value, create and refresh buttons, and policies table", async () => {
+ const instancesHeading = hostFixture.debugElement.nativeElement.querySelector(
+ "div"
+ );
+ expect(instancesHeading.innerText).toContain("Number of instances: 2");
+
+ const createButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#createButton" })
+ );
+ expect(createButton).toBeTruthy();
+ const createIcon = hostFixture.debugElement.nativeElement.querySelector(
+ "#createIcon"
+ );
+ expect(createIcon.innerText).toContain("add_box");
+
+ const refreshButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#refreshButton" })
+ );
+ expect(refreshButton).toBeTruthy();
+ const refreshIcon = hostFixture.debugElement.nativeElement.querySelector(
+ "#refreshIcon"
+ );
+ expect(refreshIcon.innerText).toContain("refresh");
+
+ const policiesTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ expect(policiesTable).toBeTruthy();
+ });
+
+ it("should open dialog to create policy and refresh policies after successful creation", async () => {
+ const dialogRefSpy = setupDialogRefSpy();
+ dialogSpy.open.and.returnValue(dialogRefSpy);
+
+ spyOn(componentUnderTest, "getPolicyInstances");
+
+ const createButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#createButton" })
+ );
+ await createButton.click();
+
+ expect(dialogSpy.open).toHaveBeenCalledWith(PolicyInstanceDialogComponent, {
+ maxWidth: "1200px",
+ maxHeight: "900px",
+ width: "900px",
+ role: "dialog",
+ disableClose: false,
+ panelClass: "dark-theme",
+ data: {
+ createSchema: policyTypeSchema,
+ instanceId: null,
+ instanceJson: null,
+ name: "1",
+ ric: null,
+ },
+ });
+ expect(componentUnderTest.getPolicyInstances).toHaveBeenCalled();
+ });
+
+ it("should open dialog to edit policy and refresh policies after successful update", async () => {
+ const dialogRefSpy = setupDialogRefSpy();
+ dialogSpy.open.and.returnValue(dialogRefSpy);
+
+ spyOn(componentUnderTest, "getPolicyInstances");
+
+ const editButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#policy1EditButton" })
+ );
+ await editButton.click();
+
+ expect(dialogSpy.open).toHaveBeenCalledWith(PolicyInstanceDialogComponent, {
+ maxWidth: "1200px",
+ maxHeight: "900px",
+ width: "900px",
+ role: "dialog",
+ disableClose: false,
+ panelClass: "dark-theme",
+ data: {
+ createSchema: policyTypeSchema,
+ instanceId: "policy1",
+ instanceJson: "{}",
+ name: "1",
+ ric: "1",
+ },
+ });
+ expect(componentUnderTest.getPolicyInstances).toHaveBeenCalled();
+ });
+
+ it("should open dialog to edit policy and not refresh policies when dialog closed wihtout submit", async () => {
+ const dialogRefSpy = setupDialogRefSpy(false);
+ dialogSpy.open.and.returnValue(dialogRefSpy);
+
+ spyOn(componentUnderTest, "getPolicyInstances");
+
+ const editButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#policy1EditButton" })
+ );
+ await editButton.click();
+
+ expect(componentUnderTest.getPolicyInstances).not.toHaveBeenCalled();
+ });
+
+ it("should open instance dialog when clicking in any policy cell in table", async () => {
+ spyOn(componentUnderTest, "modifyInstance");
+
+ const policiesTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const firstRow = (await policiesTable.getRows())[0];
+ const idCell = (await firstRow.getCells())[0];
+ (await idCell.host()).click();
+ const ownerCell = (await firstRow.getCells())[1];
+ (await ownerCell.host()).click();
+ const serviceCell = (await firstRow.getCells())[2];
+ (await serviceCell.host()).click();
+ const lastModifiedCell = (await firstRow.getCells())[3];
+ (await lastModifiedCell.host()).click();
+
+ // Totally unnecessary call just to make the bloody framework count the number of calls to the spy correctly!
+ await policiesTable.getRows();
+
+ expect(componentUnderTest.modifyInstance).toHaveBeenCalledTimes(4);
+ });
+
+ it("should open dialog asking for delete and delete when ok response and refresh table afterwards", async () => {
+ const dialogRefSpy = setupDialogRefSpy();
+ confirmServiceSpy.openConfirmDialog.and.returnValue(dialogRefSpy);
+ const createResponse = { status: 204 } as HttpResponse<Object>;
+ policyServiceSpy.deletePolicy.and.returnValue(of(createResponse));
+
+ spyOn(componentUnderTest, "getPolicyInstances");
+ const deleteButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#policy1DeleteButton" })
+ );
+ await deleteButton.click();
+
+ expect(confirmServiceSpy.openConfirmDialog).toHaveBeenCalledWith(
+ "Are you sure you want to delete this policy instance?"
+ );
+ expect(policyServiceSpy.deletePolicy).toHaveBeenCalledWith("policy1");
+ expect(notificationServiceSpy.success).toHaveBeenCalledWith(
+ "Delete succeeded!"
+ );
+ expect(componentUnderTest.getPolicyInstances).toHaveBeenCalled();
+ });
+
+ it("should open dialog asking for delete and not delete whith Cancel as response", async () => {
+ const dialogRefSpy = setupDialogRefSpy(false);
+ confirmServiceSpy.openConfirmDialog.and.returnValue(dialogRefSpy);
+
+ const deleteButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#policy1DeleteButton" })
+ );
+ await deleteButton.click();
+
+ expect(policyServiceSpy.deletePolicy).not.toHaveBeenCalled();
+ });
+
+ it("should refresh table", async () => {
+ spyOn(componentUnderTest, "getPolicyInstances");
+
+ const refreshButton: MatButtonHarness = await loader.getHarness(
+ MatButtonHarness.with({ selector: "#refreshButton" })
+ );
+ await refreshButton.click();
+
+ expect(componentUnderTest.getPolicyInstances).toHaveBeenCalled();
+ });
+
+ describe("#policiesTable", () => {
+ const expectedPolicy1Row = {
+ instanceId: "policy1",
+ ric: "1",
+ service: "service",
+ lastModified: toLocalTime(lastModifiedTime),
+ action: "editdelete",
+ };
+
+ it("should contain correct headings", async () => {
+ const policiesTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const headerRow = (await policiesTable.getHeaderRows())[0];
+ const headers = await headerRow.getCellTextByColumnName();
+
+ expect(headers).toEqual({
+ instanceId: "Instance",
+ ric: "Target",
+ service: "Owner",
+ lastModified: "Last modified",
+ action: "Action",
+ });
+ });
+
+ it("should contain data after initialization", async () => {
+ const expectedJobRows = [
+ expectedPolicy1Row,
+ {
+ instanceId: "policy2",
+ ric: "2",
+ service: "service",
+ lastModified: toLocalTime(lastModifiedTime),
+ action: "editdelete",
+ },
+ ];
+ const policiesTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const policyRows = await policiesTable.getRows();
+ expect(policyRows.length).toEqual(2);
+ policyRows.forEach((row) => {
+ row.getCellTextByColumnName().then((values) => {
+ expect(expectedJobRows).toContain(jasmine.objectContaining(values));
+ });
+ });
+ });
+
+ it("should have filtering for all four policy data headings", async () => {
+ const policiesTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+
+ const idFilterInput = await loader.getHarness(
+ MatInputHarness.with({ selector: "#policyInstanceIdFilter" })
+ );
+ await idFilterInput.setValue("1");
+ const policyRows = await policiesTable.getRows();
+ expect(policyRows.length).toEqual(1);
+ expect(await policyRows[0].getCellTextByColumnName()).toEqual(
+ expectedPolicy1Row
+ );
+
+ const targetFilterInput = await loader.getHarness(
+ MatInputHarness.with({ selector: "#policyInstanceTargetFilter" })
+ );
+ expect(targetFilterInput).toBeTruthy();
+
+ const ownerFilterInput = await loader.getHarness(
+ MatInputHarness.with({ selector: "#policyInstanceOwnerFilter" })
+ );
+ expect(ownerFilterInput).toBeTruthy();
+
+ const lastModifiedFilterInput = await loader.getHarness(
+ MatInputHarness.with({ selector: "#policyInstanceLastModifiedFilter" })
+ );
+ expect(lastModifiedFilterInput).toBeTruthy();
+ });
+
+ it("should not sort when click in filter inputs", async () => {
+ spyOn(componentUnderTest, "stopSort").and.callThrough();
+
+ const idFilterInputDiv = hostFixture.debugElement.nativeElement.querySelector(
+ "#idSortStop"
+ );
+ idFilterInputDiv.click();
+
+ const targetFilterInputDiv = hostFixture.debugElement.nativeElement.querySelector(
+ "#targetSortStop"
+ );
+ targetFilterInputDiv.click();
+
+ const ownerFilterInputDiv = hostFixture.debugElement.nativeElement.querySelector(
+ "#ownerSortStop"
+ );
+ ownerFilterInputDiv.click();
+
+ const lastModifiedFilterInputDiv = hostFixture.debugElement.nativeElement.querySelector(
+ "#lastModifiedSortStop"
+ );
+ lastModifiedFilterInputDiv.click();
+
+ expect(componentUnderTest.stopSort).toHaveBeenCalledTimes(4);
+
+ const eventSpy = jasmine.createSpyObj("any", ["stopPropagation"]);
+ componentUnderTest.stopSort(eventSpy);
+ expect(eventSpy.stopPropagation).toHaveBeenCalled();
+ });
+
+ describe("#sorting", () => {
+ it("should verify sort functionality on the table", async () => {
+ const sort = await loader.getHarness(MatSortHarness);
+ const headers = await sort.getSortHeaders({ sortDirection: "" });
+ expect(headers.length).toBe(4);
+
+ await headers[0].click();
+ expect(await headers[0].isActive()).toBe(true);
+ expect(await headers[0].getSortDirection()).toBe("asc");
+
+ await headers[0].click();
+ expect(await headers[0].getSortDirection()).toBe("desc");
+ });
+
+ it("should sort table asc and desc by first header", async () => {
+ const sort = await loader.getHarness(MatSortHarness);
+ const policyTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const firstHeader = (await sort.getSortHeaders())[0];
+ expect(await firstHeader.getSortDirection()).toBe("");
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("asc");
+ let policyRows = await policyTable.getRows();
+ expect(await policyRows[0].getCellTextByColumnName()).toEqual(
+ expectedPolicy1Row
+ );
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("desc");
+ policyRows = await policyTable.getRows();
+ expect(
+ await policyRows[policyRows.length - 1].getCellTextByColumnName()
+ ).toEqual(expectedPolicy1Row);
+ });
+
+ it("should sort table asc and desc by second header", async () => {
+ const sort = await loader.getHarness(MatSortHarness);
+ const jobsTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const firstHeader = (await sort.getSortHeaders())[1];
+ expect(await firstHeader.getSortDirection()).toBe("");
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("asc");
+ let policyRows = await jobsTable.getRows();
+ policyRows = await jobsTable.getRows();
+ expect(await policyRows[0].getCellTextByColumnName()).toEqual(
+ expectedPolicy1Row
+ );
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("desc");
+ policyRows = await jobsTable.getRows();
+ expect(
+ await policyRows[policyRows.length - 1].getCellTextByColumnName()
+ ).toEqual(expectedPolicy1Row);
+ });
+
+ it("should sort table asc and desc by third header", async () => {
+ const sort = await loader.getHarness(MatSortHarness);
+ const jobsTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const firstHeader = (await sort.getSortHeaders())[2];
+ expect(await firstHeader.getSortDirection()).toBe("");
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("asc");
+ let policyRows = await jobsTable.getRows();
+ policyRows = await jobsTable.getRows();
+ expect(await policyRows[0].getCellTextByColumnName()).toEqual(
+ expectedPolicy1Row
+ );
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("desc");
+ policyRows = await jobsTable.getRows();
+ expect(
+ await policyRows[policyRows.length - 1].getCellTextByColumnName()
+ ).toEqual(expectedPolicy1Row);
+ });
+
+ it("should sort table asc and desc by fourth header", async () => {
+ const sort = await loader.getHarness(MatSortHarness);
+ const jobsTable = await loader.getHarness(
+ MatTableHarness.with({ selector: "#policiesTable" })
+ );
+ const firstHeader = (await sort.getSortHeaders())[3];
+ expect(await firstHeader.getSortDirection()).toBe("");
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("asc");
+ let policyRows = await jobsTable.getRows();
+ policyRows = await jobsTable.getRows();
+ expect(await policyRows[0].getCellTextByColumnName()).toEqual(
+ expectedPolicy1Row
+ );
+
+ await firstHeader.click();
+ expect(await firstHeader.getSortDirection()).toBe("desc");
+ policyRows = await jobsTable.getRows();
+ expect(
+ await policyRows[policyRows.length - 1].getCellTextByColumnName()
+ ).toEqual(expectedPolicy1Row);
+ });
+ });
});
});
+
+function setupDialogRefSpy(returnValue: boolean = true) {
+ const afterClosedObservable = new Observable((observer) => {
+ observer.next(returnValue);
+ });
+
+ const dialogRefSpy = jasmine.createSpyObj("MatDialogRef", ["afterClosed"]);
+ dialogRefSpy.afterClosed.and.returnValue(afterClosedObservable);
+ return dialogRefSpy;
+}
+
+function toLocalTime(utcTime: string): string {
+ const date = new Date(utcTime);
+ const toutc = date.toUTCString();
+ return new Date(toutc + " UTC").toLocaleString();
+}
import { Component, OnInit, Input } from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
import { PolicyTypeSchema } from "@interfaces/policy.types";
-import { ErrorDialogService } from "@services/ui/error-dialog.service";
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 { 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";
-import { BehaviorSubject } from "rxjs";
+import { HttpResponse } from "@angular/common/http";
+import { BehaviorSubject, forkJoin } from "rxjs";
import { UiService } from "@services/ui/ui.service";
import { FormControl, FormGroup } from "@angular/forms";
import { MatTableDataSource } from "@angular/material/table";
-
-class PolicyTypeInfo {
- constructor(public type: PolicyTypeSchema) {}
-
- isExpanded: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
-}
+import { mergeMap } from "rxjs/operators";
@Component({
selector: "nrcp-policy-instance",
})
export class PolicyInstanceComponent implements OnInit {
@Input() policyTypeSchema: PolicyTypeSchema;
- policyInstances: PolicyInstance[] = [];
- private policyInstanceSubject = new BehaviorSubject<PolicyInstance[]>([]);
- policyTypeInfo = new Map<string, PolicyTypeInfo>();
- instanceDataSource: MatTableDataSource<PolicyInstance> = new MatTableDataSource<PolicyInstance>();
- policyInstanceForm: FormGroup;
darkMode: boolean;
+ instanceDataSource: MatTableDataSource<PolicyInstance>;
+ policyInstanceForm: FormGroup;
+ private policyInstanceSubject = new BehaviorSubject<PolicyInstance[]>([]);
+ policyInstances: PolicyInstance[] = [];
constructor(
private policySvc: PolicyService,
private dialog: MatDialog,
- private errorDialogService: ErrorDialogService,
private notificationService: NotificationService,
private confirmDialogService: ConfirmDialogService,
private ui: UiService
ngOnInit() {
this.getPolicyInstances();
this.policyInstanceSubject.subscribe((data) => {
- this.instanceDataSource.data = data;
+ this.instanceDataSource = new MatTableDataSource<PolicyInstance>(data);
+
+ this.instanceDataSource.filterPredicate = ((
+ data: PolicyInstance,
+ filter
+ ) => {
+ return (
+ this.isDataIncluding(data.policy_id, filter.id) &&
+ this.isDataIncluding(data.ric_id, filter.target) &&
+ this.isDataIncluding(data.service_id, filter.owner) &&
+ this.isDataIncluding(data.lastModified, filter.lastModified)
+ );
+ }) as (data: PolicyInstance, filter: any) => boolean;
});
this.policyInstanceForm.valueChanges.subscribe((value) => {
- const filter = { ...value, id: value.id.trim().toLowerCase() } as string;
+ const filter = {
+ ...value,
+ id: value.id.trim().toLowerCase(),
+ } as string;
this.instanceDataSource.filter = filter;
});
- this.instanceDataSource.filterPredicate = ((
- data: PolicyInstance,
- filter
- ) => {
- return (
- this.isDataIncluding(data.policy_id, filter.id) &&
- this.isDataIncluding(data.ric_id, filter.target) &&
- this.isDataIncluding(data.service_id, filter.owner) &&
- this.isDataIncluding(data.lastModified, filter.lastModified)
- );
- }) as (data: PolicyInstance, filter: any) => boolean;
-
this.ui.darkModeState.subscribe((isDark) => {
this.darkMode = isDark;
});
getPolicyInstances() {
this.policyInstances = [] as PolicyInstance[];
this.policySvc
- .getPolicyInstancesByType(this.policyTypeSchema.id)
- .subscribe((policies) => {
- if (policies.policy_ids.length != 0) {
- policies.policy_ids.forEach((policyId) => {
- this.policySvc
- .getPolicyInstance(policyId)
- .subscribe((policyInstance) => {
- this.policySvc
- .getPolicyStatus(policyId)
- .subscribe((policyStatus) => {
- policyInstance.lastModified = policyStatus.last_modified;
- });
- this.policyInstances.push(policyInstance);
- });
- this.policyInstanceSubject.next(this.policyInstances);
- });
- }
+ .getPolicyInstancesByType(this.policyTypeSchema.id)
+ .pipe(
+ mergeMap((policyIds) =>
+ forkJoin(
+ policyIds.policy_ids.map((id) => {
+ return forkJoin([
+ this.policySvc.getPolicyInstance(id),
+ this.policySvc.getPolicyStatus(id),
+ ]);
+ })
+ )
+ )
+ )
+ .subscribe((res) => {
+ this.policyInstances = res.map((policy) => {
+ let policyInstance = <PolicyInstance>{};
+ policyInstance = policy[0];
+ policyInstance.lastModified = policy[1].last_modified;
+ return policyInstance;
+ });
+ this.policyInstanceSubject.next(this.policyInstances);
});
}
getSortedData(sort: Sort) {
const data = this.instanceDataSource.data;
- data.sort((a, b) => {
+ data.sort((a: PolicyInstance, b: PolicyInstance) => {
const isAsc = sort.direction === "asc";
switch (sort.active) {
case "instanceId":
return !filter || data.toLowerCase().includes(filter);
}
- private onExpand(isExpanded: boolean) {
- if (isExpanded) {
- this.getPolicyInstances();
- }
+ createPolicyInstance(policyTypeSchema: PolicyTypeSchema): void {
+ this.openInstanceDialog(null);
}
- private isSchemaEmpty(): boolean {
- return this.policyTypeSchema.schemaObject === "{}";
+ modifyInstance(instance: PolicyInstance): void {
+ let refreshedInstance: PolicyInstance;
+ this.policySvc
+ .getPolicyInstance(instance.policy_id)
+ .subscribe((refreshedJson: any) => {
+ refreshedInstance = refreshedJson;
+ });
+
+ this.openInstanceDialog(refreshedInstance);
}
- modifyInstance(instance: PolicyInstance): void {
- this.policySvc.getPolicyInstance(instance.policy_id).subscribe(
- (refreshedJson: any) => {
- instance = refreshedJson;
- this.dialog
- .open(
- PolicyInstanceDialogComponent,
- getPolicyDialogProperties(
- this.policyTypeSchema,
- instance,
- this.darkMode
- )
- )
- .afterClosed()
- .subscribe((_: any) => {
- this.getPolicyInstances();
- });
- },
- (httpError: HttpErrorResponse) => {
- this.notificationService.error(
- "Could not refresh instance. Please try again." + httpError.message
- );
- }
+ private openInstanceDialog(policy: PolicyInstance) {
+ const dialogData = getPolicyDialogProperties(
+ this.policyTypeSchema,
+ policy,
+ this.darkMode
+ );
+ const dialogRef = this.dialog.open(
+ PolicyInstanceDialogComponent,
+ dialogData
);
+ dialogRef.afterClosed().subscribe((ok: any) => {
+ if (ok) this.getPolicyInstances();
+ });
}
- nbInstances(): number {
+ noInstances(): number {
return this.policyInstances.length;
}
return new Date(toutc + " UTC").toLocaleString();
}
- createPolicyInstance(policyTypeSchema: PolicyTypeSchema): void {
- let dialogRef = this.dialog.open(
- PolicyInstanceDialogComponent,
- getPolicyDialogProperties(policyTypeSchema, null, this.darkMode)
- );
- const info: PolicyTypeInfo = this.getPolicyTypeInfo(policyTypeSchema);
- dialogRef.afterClosed().subscribe((_) => {
- info.isExpanded.next(info.isExpanded.getValue());
- });
- }
-
deleteInstance(instance: PolicyInstance): void {
this.confirmDialogService
.openConfirmDialog(
.afterClosed()
.subscribe((res: any) => {
if (res) {
- this.policySvc.deletePolicy(instance.policy_id).subscribe(
- (response: HttpResponse<Object>) => {
- switch (response.status) {
- case 204:
- this.notificationService.success("Delete succeeded!");
- this.getPolicyInstances();
- break;
- default:
- this.notificationService.warn(
- "Delete failed " + response.status + " " + response.body
- );
+ this.policySvc
+ .deletePolicy(instance.policy_id)
+ .subscribe((response: HttpResponse<Object>) => {
+ if (response.status === 204) {
+ this.notificationService.success("Delete succeeded!");
+ this.getPolicyInstances();
}
- },
- (error: HttpErrorResponse) => {
- this.errorDialogService.displayError(
- error.statusText + ", " + error.error
- );
- }
- );
+ });
}
});
}
- getPolicyTypeInfo(policyTypeSchema: PolicyTypeSchema): PolicyTypeInfo {
- let info: PolicyTypeInfo = this.policyTypeInfo.get(policyTypeSchema.name);
- if (!info) {
- info = new PolicyTypeInfo(policyTypeSchema);
- this.policyTypeInfo.set(policyTypeSchema.name, info);
- }
- return info;
- }
-
refreshTable() {
this.getPolicyInstances();
}