You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by rs...@apache.org on 2023/05/11 05:57:14 UTC
[trafficcontrol] 02/03: Corrections wrt tests
This is an automated email from the ASF dual-hosted git repository.
rshah pushed a commit to branch feature/tpv2-role-details
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 3dd1ff9ea550cca4a57723c288b7e0003856e934
Author: Rima Shah <ri...@comcast.com>
AuthorDate: Wed May 10 23:31:57 2023 -0600
Corrections wrt tests
---
.../users/{roleDetails.ts => roleDetail.ts} | 3 --
.../nightwatch/tests/users/role/detail.spec.ts | 32 ++++++++-------
.../src/app/api/testing/user.service.ts | 30 +++++++++------
.../traffic-portal/src/app/api/user.service.ts | 9 +++--
.../traffic-portal/src/app/core/core.module.ts | 1 +
.../users/roles/detail/role-detail.component.html | 19 ++++-----
.../roles/detail/role-detail.component.spec.ts | 19 ++++-----
.../users/roles/detail/role-detail.component.ts | 20 +++++-----
.../roles/table/roles-table.component.spec.ts | 45 ++++++++++++++++------
.../users/roles/table/roles-table.component.ts | 32 ++++++++++++---
10 files changed, 124 insertions(+), 86 deletions(-)
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/roleDetails.ts b/experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts
similarity index 94%
rename from experimental/traffic-portal/nightwatch/page_objects/users/roleDetails.ts
rename to experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts
index de01fda53d..c4b0521b25 100644
--- a/experimental/traffic-portal/nightwatch/page_objects/users/roleDetails.ts
+++ b/experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts
@@ -24,9 +24,6 @@ const roleDetailPageObject = {
description: {
selector: "input[name='description']"
},
- lastUpdated: {
- selector: "input[name='lastUpdated']"
- },
name: {
selector: "input[name='name']"
},
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/users/role/detail.spec.ts
index 8856106316..95d9d2a9ad 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/detail.spec.ts
+++ b/experimental/traffic-portal/nightwatch/tests/users/role/detail.spec.ts
@@ -14,31 +14,29 @@
describe("Role Detail Spec", () => {
it("Test Role", () => {
- const page = browser.page.users.roles();
+ const page = browser.page.users.roleDetail();
browser.url(`${page.api.launchUrl}/core/roles/${browser.globals.testData.role.name}`, res => {
browser.assert.ok(res.status === 0);
page.waitForElementVisible("mat-card")
- .assert.enabled("@role")
.assert.enabled("@description")
+ .assert.enabled("@name")
.assert.enabled("@permissions")
.assert.enabled("@saveBtn")
- .assert.not.enabled("@lastUpdated")
.assert.valueEquals("@name", String(browser.globals.testData.role.name))
- .assert.valueEquals("@permission", String(browser.globals.testData.role.permissions));
+ .assert.valueEquals("@permissions", String(browser.globals.testData.role.permissions));
});
});
- // it("New asn", () => {
- // const page = browser.page.cacheGroups.asnDetail();
- // browser.url(`${page.api.launchUrl}/core/asns/new`, res => {
- // browser.assert.ok(res.status === 0);
- // page.waitForElementVisible("mat-card")
- // .assert.enabled("@asn")
- // .assert.enabled("@cachegroup")
- // .assert.enabled("@saveBtn")
- // .assert.not.elementPresent("@id")
- // .assert.not.elementPresent("@lastUpdated")
- // .assert.valueEquals("@asn", "1");
- // });
- // });
+ it("New asn", () => {
+ const page = browser.page.users.roleDetail();
+ browser.url(`${page.api.launchUrl}/core/roles/new`, res => {
+ browser.assert.ok(res.status === 0);
+ page.waitForElementVisible("mat-card")
+ .assert.enabled("@description")
+ .assert.enabled("@name")
+ .assert.enabled("@permissions")
+ .assert.enabled("@saveBtn")
+ .assert.valueEquals("@name", "test");
+ });
+ });
});
diff --git a/experimental/traffic-portal/src/app/api/testing/user.service.ts b/experimental/traffic-portal/src/app/api/testing/user.service.ts
index bcaf7c6dc2..e176d4ce9f 100644
--- a/experimental/traffic-portal/src/app/api/testing/user.service.ts
+++ b/experimental/traffic-portal/src/app/api/testing/user.service.ts
@@ -86,6 +86,15 @@ export class UserService {
privLevel: 30
}
];
+ private readonly roleDetail: Array<ResponseRole> = [{
+ description: "Has access to everything - cannot be modified or deleted",
+ lastUpdated: new Date(),
+ name: "admin",
+ permissions: [
+ "ALL"
+ ],
+ }
+ ];
private readonly tenants: Array<ResponseTenant> = [
{
@@ -438,10 +447,8 @@ export class UserService {
public async createRole(role: RequestRole): Promise<ResponseRole> {
const resp = {
...role,
- name: role.name,
- lastUpdated: new Date(),
};
- this.roles.push(resp);
+ this.roleDetail.push(resp);
return resp;
}
@@ -452,11 +459,11 @@ export class UserService {
* @returns The updated role.
*/
public async updateRole(role: ResponseRole): Promise<ResponseRole> {
- const name = this.tenants.findIndex(r => r.name === role.name);
- if (name === null) {
+ const roleName = this.roleDetail.findIndex(r => r.name === role.name);
+ if (roleName < 0 ) {
throw new Error(`no such Role: ${role.name}`);
}
- this.roles[name] = role;
+ this.roleDetail[roleName] = role;
return role;
}
@@ -466,12 +473,13 @@ export class UserService {
* @param tenant The role to be deleted.
* @returns The deleted role.
*/
- public async deleteRole(role: ResponseRole): Promise<ResponseRole> {
- const index = this.tenants.findIndex(r => r.name === role.name);
- if (index < 0) {
- throw new Error(`no such role: ${role.name}`);
+ public async deleteRole(role: string | ResponseRole): Promise<ResponseRole> {
+ const roleName = typeof(role) === "string" ? role : role.name;
+ const index = this.roleDetail.findIndex(r => r.name === roleName);
+ if (index === -1) {
+ throw new Error(`no such role: ${role}`);
}
- return this.roles.splice(index, 1)[0];
+ return this.roleDetail.splice(index, 1)[0];
}
/**
diff --git a/experimental/traffic-portal/src/app/api/user.service.ts b/experimental/traffic-portal/src/app/api/user.service.ts
index 3b1bb51b1c..4a1ad356e9 100644
--- a/experimental/traffic-portal/src/app/api/user.service.ts
+++ b/experimental/traffic-portal/src/app/api/user.service.ts
@@ -17,6 +17,7 @@ import { Injectable } from "@angular/core";
import {
type ResponseUser,
type PostRequestUser,
+ type RequestRole,
type RequestTenant,
type ResponseCurrentUser,
type ResponseRole,
@@ -27,7 +28,6 @@ import {
} from "trafficops-types";
import { APIService } from "./base-api.service";
-import {RequestRole} from "trafficops-types";
/**
* UserService exposes API functionality related to Users, Roles and Tenants.
@@ -285,7 +285,7 @@ export class UserService extends APIService {
* @returns The updated role.
*/
public async updateRole(role: ResponseRole): Promise<ResponseRole> {
- return this.put<ResponseRole>(`roles/${role.name}`, role).toPromise();
+ return this.put<ResponseRole>(`roles?name=${role.name}`, role).toPromise();
}
/**
@@ -294,8 +294,9 @@ export class UserService extends APIService {
* @param tenant The role to be deleted.
* @returns The deleted role.
*/
- public async deleteRole(role: ResponseRole): Promise<ResponseRole> {
- return this.delete<ResponseRole>(`roles/${role.name}`).toPromise();
+ public async deleteRole(role: string | ResponseRole): Promise<void> {
+ const roleName = typeof(role) === "string" ? role : role.name;
+ return this.delete(`roles?name=${roleName}`).toPromise();
}
/**
diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts
index f90d850688..e9b68f3cd6 100644
--- a/experimental/traffic-portal/src/app/core/core.module.ts
+++ b/experimental/traffic-portal/src/app/core/core.module.ts
@@ -131,6 +131,7 @@ export const ROUTES: Routes = [
TenantsComponent,
UserRegistrationDialogComponent,
RolesTableComponent,
+ RoleDetailComponent,
TenantDetailsComponent,
ChangeLogsComponent,
LastDaysComponent,
diff --git a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.html b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.html
index 512420ec62..9a46d242fb 100644
--- a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.html
+++ b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.html
@@ -16,23 +16,18 @@ limitations under the License.
<tp-loading *ngIf="!role"></tp-loading>
<form ngNativeValidate (ngSubmit)="submit($event)" *ngIf="role">
<mat-card-content>
- <mat-form-field *ngIf="!new">
- <mat-label>ID</mat-label>
- <input matInput type="text" name="name" disabled readonly [defaultValue]="role.name" />
+ <mat-form-field>
+ <mat-label>Name</mat-label>
+ <input matInput type="text" name="name" required [(ngModel)]="role.name" />
</mat-form-field>
<mat-form-field>
- <mat-label>ASN</mat-label>
- <input matInput type="text" name="description" required [(ngModel)]="role.description" />
+ <mat-label>Description</mat-label>
+ <input matInput type="text" name="description" [(ngModel)]="role.description" />
</mat-form-field>
<mat-form-field>
- <mat-label>Cache Group</mat-label>
- <mat-select name="permissions" [(ngModel)]="role.permissions" required>
- <mat-option *ngFor="" [value]="">{{}}</mat-option>
- </mat-select>
+ <mat-label>Permissions</mat-label>
+ <textarea name="permissions" [(ngModel)]="role.permissions" matInput rows="10" wrap="hard" cdkTextareaAutosize></textarea>
</mat-form-field>
- <mat-form-field *ngIf="!new">
- <mat-label>Last Updated</mat-label>
- <input matInput type="text" name="lastUpdated" disabled readonly [defaultValue]="role.lastUpdated" /> </mat-form-field>
</mat-card-content>
<mat-card-actions align="end">
<button mat-raised-button type="button" *ngIf="!new" color="warn" (click)="deleteRole()">Delete</button>
diff --git a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.spec.ts b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.spec.ts
index 7bbe9d896f..6e1d180395 100644
--- a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.spec.ts
@@ -12,11 +12,12 @@
* limitations under the License.
*/
import { ComponentFixture, TestBed } from "@angular/core/testing";
-import { MatDialogModule } from "@angular/material/dialog";
+import { MatDialog, MatDialogModule, type MatDialogRef } from "@angular/material/dialog";
import { ActivatedRoute } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
-import { ReplaySubject } from "rxjs";
+import { of, ReplaySubject } from "rxjs";
+import { UserService } from "src/app/api";
import { APITestingModule } from "src/app/api/testing";
import { RoleDetailComponent } from "src/app/core/users/roles/detail/role-detail.component";
import { NavigationService } from "src/app/shared/navigation/navigation.service";
@@ -57,21 +58,15 @@ describe("RoleDetailComponent", () => {
fixture.detectChanges();
await fixture.whenStable();
expect(paramMap).toHaveBeenCalled();
- expect(component.roles).not.toBeNull();
- expect(component.roles.name).toBe(1);
+ expect(component.role).not.toBeNull();
+ expect(component.role.name).toBe("");
expect(component.new).toBeTrue();
});
it("existing role", async () => {
- paramMap.and.returnValue("1");
-
- fixture = TestBed.createComponent(RoleDetailComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- await fixture.whenStable();
expect(paramMap).toHaveBeenCalled();
- expect(component.roles).not.toBeNull();
- expect(component.roles.name).toBe(0);
+ expect(component.role).not.toBeNull();
+ expect(component.role.name).toBe("");
expect(component.new).toBeFalse();
});
});
diff --git a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.ts b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.ts
index 9e7897eee3..ba81b5170f 100644
--- a/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.ts
+++ b/experimental/traffic-portal/src/app/core/users/roles/detail/role-detail.component.ts
@@ -31,9 +31,10 @@ import { NavigationService } from "src/app/shared/navigation/navigation.service"
})
export class RoleDetailComponent implements OnInit {
public new = false;
- public asn!: ResponseRole;
- constructor(private readonly route: ActivatedRoute, private readonly location: Location,
- private readonly dialog: MatDialog, private readonly header: NavigationService) {
+ public role!: ResponseRole;
+ constructor(private readonly route: ActivatedRoute, private readonly userService: UserService,
+ private readonly location: Location, private readonly dialog: MatDialog,
+ private readonly header: NavigationService) {
}
/**
@@ -50,15 +51,14 @@ export class RoleDetailComponent implements OnInit {
this.header.headerTitle.next("New Role");
this.new = true;
this.role = {
- description: "Read Only",
- lastUpdated: new Date(),
- name: "test",
+ description: "",
+ name: "",
permissions: []
};
return;
}
- this.role = await this.UserService.getRoles(role);
+ this.role = await this.userService.getRoles(role);
this.header.headerTitle.next(`Role: ${this.role.name}`);
}
@@ -76,7 +76,7 @@ export class RoleDetailComponent implements OnInit {
});
ref.afterClosed().subscribe(result => {
if(result) {
- this.UserService.deleteRole(this.role.name);
+ this.userService.deleteRole(this.role);
this.location.back();
}
});
@@ -91,10 +91,10 @@ export class RoleDetailComponent implements OnInit {
e.preventDefault();
e.stopPropagation();
if(this.new) {
- this.asn = await this.UserService.createRole(this.role);
+ this.role = await this.userService.createRole(this.role);
this.new = false;
} else {
- this.asn = await this.UserService.updateRoleN(this.Role);
+ this.role = await this.userService.updateRole(this.role);
}
}
diff --git a/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.spec.ts b/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.spec.ts
index 55d1ad2088..50455309ff 100644
--- a/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.spec.ts
@@ -13,10 +13,12 @@
*/
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
-import { MatDialogModule } from "@angular/material/dialog";
+import { MatDialog, MatDialogModule, type MatDialogRef } from "@angular/material/dialog";
import { RouterTestingModule } from "@angular/router/testing";
import { ResponseRole } from "trafficops-types";
+import { of } from "rxjs";
+import { UserService } from "src/app/api";
import { APITestingModule } from "src/app/api/testing";
import { RolesTableComponent } from "src/app/core/users/roles/table/roles-table.component";
import { isAction } from "src/app/shared/generic-table/generic-table.component";
@@ -35,8 +37,7 @@ describe("RolesTableComponent", () => {
await TestBed.configureTestingModule({
declarations: [ RolesTableComponent ],
imports: [ APITestingModule, RouterTestingModule, MatDialogModule ]
- })
- .compileComponents();
+ }).compileComponents();
fixture = TestBed.createComponent(RolesTableComponent);
component = fixture.componentInstance;
@@ -93,18 +94,38 @@ describe("RolesTableComponent", () => {
expect(item.href(role)).toBe(role.name);
});
- it("has context menu items that aren't implemented yet", () => {
- const item = component.contextMenuItems.find(i => i.name === "Edit");
+ it("deletes Roles", fakeAsync(async () => {
+ const item = component.contextMenuItems.find(i => i.name === "Delete");
if (!item) {
- return fail("missing 'Edit' context menu item");
+ return fail("missing 'Delete' context menu item");
}
- if (isAction(item)) {
- return fail("incorrect type for 'Edit' menu item. Expected an action, not a link");
- }
- if (typeof(item.disabled) !== "function") {
- return fail("'Edit' context menu item should be disabled, but no disabled function is defined");
+ if (!isAction(item)) {
+ return fail("incorrect type for 'Delete' menu item. Expected an action, not a link");
}
- });
+ expect(item.multiRow).toBeFalsy();
+ expect(item.disabled).toBeUndefined();
+
+ const api = TestBed.inject(UserService);
+ const spy = spyOn(api, "deleteRole").and.callThrough();
+ expect(spy).not.toHaveBeenCalled();
+
+ const dialogService = TestBed.inject(MatDialog);
+ const openSpy = spyOn(dialogService, "open").and.returnValue({
+ afterClosed: () => of(true)
+ } as MatDialogRef<unknown>);
+
+ const testRole = await api.createRole(role);
+ expect(openSpy).not.toHaveBeenCalled();
+ const asyncExpectation = expectAsync(component.handleContextMenu({action: "delete", data: testRole})).toBeResolvedTo(undefined);
+ tick();
+
+ expect(openSpy).toHaveBeenCalled();
+ tick();
+
+ expect(spy).toHaveBeenCalled();
+
+ await asyncExpectation;
+ }));
it("generate 'View Users' context menu item href", () => {
const item = component.contextMenuItems.find(i => i.name === "View Users");
diff --git a/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.ts b/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.ts
index 6e65b7ed2e..5e00253cce 100644
--- a/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.ts
+++ b/experimental/traffic-portal/src/app/core/users/roles/table/roles-table.component.ts
@@ -20,8 +20,10 @@ import type { ResponseRole } from "trafficops-types";
import { UserService } from "src/app/api";
import { CurrentUserService } from "src/app/shared/current-user/current-user.service";
+import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component";
import type { ContextMenuActionEvent, ContextMenuItem } from "src/app/shared/generic-table/generic-table.component";
import { NavigationService } from "src/app/shared/navigation/navigation.service";
+import {MatDialog} from "@angular/material/dialog";
/**
* RolesTableComponent is the controller for the "Roles" table.
*/
@@ -34,7 +36,7 @@ export class RolesTableComponent implements OnInit {
/** List of roles */
public roles: Promise<Array<ResponseRole>>;
constructor(private readonly route: ActivatedRoute, private readonly headerSvc: NavigationService,
- private readonly api: UserService, public readonly auth: CurrentUserService) {
+ private readonly api: UserService, private readonly dialog: MatDialog, public readonly auth: CurrentUserService) {
this.fuzzySubject = new BehaviorSubject<string>("");
this.roles = this.api.getRoles();
this.headerSvc.headerTitle.next("Roles");
@@ -77,7 +79,6 @@ export class RolesTableComponent implements OnInit {
/** Definitions for the context menu items (which act on augmented roles data). */
public contextMenuItems: Array<ContextMenuItem<ResponseRole>> = [
{
- disabled: (): true => true,
href: (selectedRow: ResponseRole): string => `${selectedRow.name}`,
name: "Edit"
},
@@ -86,6 +87,11 @@ export class RolesTableComponent implements OnInit {
name: "Open in New Tab",
newTab: true
},
+ {
+ action: "delete",
+ multiRow: false,
+ name: "Delete"
+ },
{
href: "/core/users",
name: "View Users",
@@ -107,9 +113,25 @@ export class RolesTableComponent implements OnInit {
/**
* Handles a context menu event.
*
- * @param a The action selected from the context menu.
+ * @param evt The action selected from the context menu.
*/
- public handleContextMenu(a: ContextMenuActionEvent<Readonly<ResponseRole>>): void {
- console.log("action:", a);
+ public async handleContextMenu(evt: ContextMenuActionEvent<ResponseRole>): Promise<void> {
+ if (Array.isArray(evt.data)) {
+ console.error("cannot delete multiple roles at once:", evt.data);
+ return;
+ }
+ const data = evt.data;
+ switch(evt.action) {
+ case "delete":
+ const ref = this.dialog.open(DecisionDialogComponent, {
+ data: {message: `Are you sure you want to delete '${data.name}' role?`, title: "Confirm Delete"}
+ });
+ ref.afterClosed().subscribe(result => {
+ if(result) {
+ this.api.deleteRole(data.name).then(async () => this.roles = this.api.getRoles());
+ }
+ });
+ break;
+ }
}
}