You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2023/12/18 02:16:04 UTC

(trafficcontrol) branch master updated: Update Service Categories table to use AG-Grid (#7880)

This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 0c5607e2d6 Update Service Categories table to use AG-Grid (#7880)
0c5607e2d6 is described below

commit 0c5607e2d6fa010bc02921b09e971539d92b2c4b
Author: The Anh Nguyen <nt...@gmail.com>
AuthorDate: Mon Dec 18 09:15:57 2023 +0700

    Update Service Categories table to use AG-Grid (#7880)
    
    * update UI service categories to AG-Grid
    
    * update integration tests Service Categories
---
 .../TableServiceCategoriesController.js            |  81 ++++++++++----
 .../table.serviceCategories.tpl.html               |  35 ++----
 .../PageObjects/ServiceCategories.po.ts            | 122 ++++++++++-----------
 .../integration/specs/ServiceCategories.spec.ts    |  61 +++++++----
 4 files changed, 168 insertions(+), 131 deletions(-)

diff --git a/traffic_portal/app/src/common/modules/table/serviceCategories/TableServiceCategoriesController.js b/traffic_portal/app/src/common/modules/table/serviceCategories/TableServiceCategoriesController.js
index 363ba6fe63..48c1958d2f 100644
--- a/traffic_portal/app/src/common/modules/table/serviceCategories/TableServiceCategoriesController.js
+++ b/traffic_portal/app/src/common/modules/table/serviceCategories/TableServiceCategoriesController.js
@@ -18,36 +18,79 @@
  */
 
 /**
- * @param {*} serviceCategories
+ *@typedef ServiceCategory
+ * @property {string} name
+ * @property {string} lastUpdated
+ */
+
+/**
+ * @param {ServiceCategory} serviceCategories
  * @param {*} $scope
  * @param {*} $state
  * @param {import("../../../service/utils/LocationUtils")} locationUtils
  */
-var TableServiceCategoriesController = function(serviceCategories, $scope, $state, locationUtils) {
+var TableServiceCategoriesController = function (
+    serviceCategories,
+    $scope,
+    $state,
+    locationUtils
+) {
+    /**** Constants, scope data, etc. ****/
 
-    $scope.serviceCategories = serviceCategories;
+    /** The columns of the ag-grid table */
+    $scope.columns = [
+        {
+            headerName: "Name",
+            field: "name",
+            hide: false,
+        },
+        {
+            headerName: "Last Updated",
+            field: "lastUpdated",
+            hide: true,
+            filter: "agDateColumnFilter",
+        },
+    ];
 
-    $scope.editServiceCategory = function(name) {
-        locationUtils.navigateToPath('/service-categories/edit?name=' + encodeURIComponent(name));
-    };
+    /** @type {import("../agGrid/CommonGridController").CGC.DropDownOption[]} */
+    $scope.dropDownOptions = [
+        {
+            name: "createServiceCategoryMenuItem",
+            href: "#!/service-categories/new",
+            text: "Create New Service Category",
+            type: 2,
+        },
+    ];
 
-    $scope.createServiceCategory = function() {
-        locationUtils.navigateToPath('/service-categories/new');
+    /** Reloads all resolved data for the view. */
+    $scope.refresh = () => {
+        $state.reload();
     };
 
-    $scope.refresh = function() {
-        $state.reload(); // reloads all the resolves for the view
+    /** Options, configuration, data and callbacks for the ag-grid table. */
+    /** @type {import("../agGrid/CommonGridController").CGC.GridSettings} */
+    $scope.gridOptions = {
+        onRowClick: (row) => {
+            locationUtils.navigateToPath(
+                `/service-categories/edit?name=${encodeURIComponent(
+                    row.data.name
+                )}`
+            );
+        },
     };
 
-    angular.element(document).ready(function () {
-        $('#serviceCategoriesTable').dataTable({
-            "aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
-            "iDisplayLength": 25,
-            "aaSorting": []
-        });
-    });
-
+    $scope.serviceCategories = serviceCategories.map((serviceCategory) => ({
+        ...serviceCategory,
+        lastUpdated: new Date(
+            serviceCategory.lastUpdated.replace(" ", "T").replace("+00", "Z")
+        ),
+    }));
 };
 
-TableServiceCategoriesController.$inject = ['serviceCategories', '$scope', '$state', 'locationUtils'];
+TableServiceCategoriesController.$inject = [
+    "serviceCategories",
+    "$scope",
+    "$state",
+    "locationUtils",
+];
 module.exports = TableServiceCategoriesController;
diff --git a/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html b/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
index 9b95deadf4..ef22b60b97 100644
--- a/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
@@ -18,30 +18,13 @@ under the License.
 -->
 
 <div class="x_panel">
-    <div class="x_title">
-        <ol class="breadcrumb pull-left">
-            <li class="active">Service Categories</li>
-        </ol>
-        <div class="pull-right">
-            <button name="createServiceCategoryButton" class="btn btn-primary" title="Create Service Category" ng-click="createServiceCategory()"><i class="fa fa-plus"></i></button>
-            <button type=button class="btn btn-default" title="Refresh" ng-click="refresh()"><i class="fa fa-refresh"></i></button>
-        </div>
-        <div class="clearfix"></div>
-    </div>
-    <div class="x_content">
-        <br>
-        <table id="serviceCategoriesTable" class="table responsive-utilities jambo_table">
-            <thead>
-            <tr class="headings">
-                <th>Name</th>
-            </tr>
-            </thead>
-            <tbody>
-            <tr ng-click="editServiceCategory(sc.name)" ng-repeat="sc in ::serviceCategories">
-                <td name="name" data-search="^{{::sc.name}}$">{{::sc.name}}</td>
-            </tr>
-            </tbody>
-        </table>
-    </div>
+    <common-grid-controller
+        table-title="Service Categories"
+        table-name="serviceCategories"
+        options="gridOptions"
+        data="serviceCategories"
+        columns="columns"
+        drop-down-options="dropDownOptions"
+    >
+    </common-grid-controller>
 </div>
-
diff --git a/traffic_portal/test/integration/PageObjects/ServiceCategories.po.ts b/traffic_portal/test/integration/PageObjects/ServiceCategories.po.ts
index bac32353e7..f5247c1926 100644
--- a/traffic_portal/test/integration/PageObjects/ServiceCategories.po.ts
+++ b/traffic_portal/test/integration/PageObjects/ServiceCategories.po.ts
@@ -16,104 +16,96 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { browser, by, element } from 'protractor';
+import { by, element } from "protractor";
 
-import { randomize } from '../config';
-import { BasePage } from './BasePage.po';
-import { SideNavigationPage } from './SideNavigationPage.po';
+import { randomize } from "../config";
+import { SideNavigationPage } from "./SideNavigationPage.po";
 
 interface CreateServiceCategory {
     Name: string;
-    validationMessage?: string;
 }
 
 interface UpdateServiceCategory {
     description: string;
     NewName: string;
-    validationMessage?: string;
 }
 
 interface DeleteServiceCategory {
     Name: string;
-    validationMessage?: string;
 }
 
-export class ServiceCategoriesPage extends BasePage {
-
-    private btnCreateServiceCategories = element(by.name("createServiceCategoryButton"));
-    private txtSearch = element(by.id('serviceCategoriesTable_filter')).element(by.css('label input'));
-    private txtName = element(by.id('name'));
-
-    private btnDelete = element(by.buttonText('Delete'));
-    private txtConfirmName = element(by.name('confirmWithNameInput'));
-    private randomize = randomize;
-
+export class ServiceCategoriesPage extends SideNavigationPage {
+    private txtName = element(by.name("name"));
     async OpenServicesMenu() {
-        let snp = new SideNavigationPage();
-        await snp.ClickServicesMenu();
+        await this.ClickServicesMenu();
     }
 
+    /**
+     * Navigates the browser to the Service Categories table page.
+     */
     async OpenServiceCategoriesPage() {
-        let snp = new SideNavigationPage();
-        await snp.NavigateToServiceCategoriesPage();
+        await this.NavigateToServiceCategoriesPage();
     }
 
-    public async CreateServiceCategories(serviceCategories: CreateServiceCategory): Promise<boolean> {
-        let result = false;
-        let basePage = new BasePage();
-        await this.btnCreateServiceCategories.click();
-        await this.txtName.sendKeys(serviceCategories.Name + this.randomize);
-        await basePage.ClickCreate();
-        result = await basePage.GetOutputMessage().then(function (value) {
-            if (value.indexOf(serviceCategories.validationMessage ?? "") > -1) {
-                return true;
-            } else {
-                return false;
-            }
-        })
-        return result;
+    public async CreateServiceCategories(
+        serviceCategories: CreateServiceCategory,
+        outputMessage: string
+    ): Promise<boolean> {
+        await this.OpenServiceCategoriesPage();
+        await element(by.buttonText("More")).click();
+        await element(by.linkText("Create New Service Category")).click();
+        this.txtName.sendKeys(serviceCategories.Name + randomize);
+        await this.ClickCreate();
+        return this.GetOutputMessage().then(
+            (v) => v.indexOf(outputMessage ?? "") > -1
+        );
     }
 
-    public async SearchServiceCategories(nameServiceCategories: string): Promise<boolean> {
-        let name = nameServiceCategories + this.randomize;
-        await this.txtSearch.clear();
-        await this.txtSearch.sendKeys(name);
-        if (await browser.isElementPresent(element(by.xpath("//td[@data-search='^" + name + "$']"))) == true) {
-            await element(by.xpath("//td[@data-search='^" + name + "$']")).click();
-            return true;
-        }
-        return false;
+    public async SearchServiceCategories(
+        nameServiceCategories: string
+    ): Promise<void> {
+        nameServiceCategories += randomize;
+        await this.OpenServiceCategoriesPage();
+        const searchInput = element(by.id("quickSearch"));
+        await searchInput.clear();
+        await searchInput.sendKeys(nameServiceCategories);
+        await element(
+            by.cssContainingText("span", nameServiceCategories)
+        ).click();
     }
 
-    public async UpdateServiceCategories(serviceCategories: UpdateServiceCategory): Promise<boolean | undefined> {
-        let basePage = new BasePage();
+    public async UpdateServiceCategories(
+        serviceCategories: UpdateServiceCategory,
+        outputMessage: string
+    ): Promise<boolean | undefined> {
         switch (serviceCategories.description) {
             case "update service categories name":
                 await this.txtName.clear();
-                await this.txtName.sendKeys(serviceCategories.NewName + this.randomize);
-                await basePage.ClickUpdate();
+                await this.txtName.sendKeys(
+                    serviceCategories.NewName + randomize
+                );
+                await this.ClickUpdate();
                 break;
             default:
                 return undefined;
         }
-        return await basePage.GetOutputMessage().then(value => serviceCategories.validationMessage === value || (serviceCategories.validationMessage !== undefined && value.includes(serviceCategories.validationMessage)));
+        return await this.GetOutputMessage().then(
+            (v) =>
+                outputMessage === v ||
+                (outputMessage !== undefined && v.includes(outputMessage))
+        );
     }
 
-    public async DeleteServiceCategories(serviceCategories: DeleteServiceCategory): Promise<boolean> {
-        let name = serviceCategories.Name + this.randomize;
-        let result = false;
-        let basePage = new BasePage();
-        await this.btnDelete.click();
-        await this.txtConfirmName.sendKeys(name);
-        await basePage.ClickDeletePermanently();
-        result = await basePage.GetOutputMessage().then(function (value) {
-            if (value.indexOf(serviceCategories.validationMessage ?? "") > -1) {
-                return true;
-            } else {
-                return false;
-            }
-        })
-        return result;
-
+    public async DeleteServiceCategories(
+        serviceCategories: DeleteServiceCategory,
+        outputMessage: string
+    ): Promise<boolean> {
+        const name = serviceCategories.Name + randomize;
+        await element(by.buttonText("Delete")).click();
+        await element(by.name("confirmWithNameInput")).sendKeys(name);
+        await this.ClickDeletePermanently();
+        return this.GetOutputMessage().then(
+            (v) => v.indexOf(outputMessage ?? "") > -1
+        );
     }
 }
diff --git a/traffic_portal/test/integration/specs/ServiceCategories.spec.ts b/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
index fdbcd6b3fb..3da4587147 100644
--- a/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
+++ b/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
@@ -16,65 +16,84 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { browser } from 'protractor';
+import { browser } from "protractor";
 
-import { LoginPage } from '../PageObjects/LoginPage.po';
-import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
+import { LoginPage } from "../PageObjects/LoginPage.po";
+import { TopNavigationPage } from "../PageObjects/TopNavigationPage.po";
 import { api } from "../config";
-import { ServiceCategoriesPage } from '../PageObjects/ServiceCategories.po';
+import { ServiceCategoriesPage } from "../PageObjects/ServiceCategories.po";
 import { serviceCategories } from "../Data";
 
 const loginPage = new LoginPage();
 const topNavigation = new TopNavigationPage();
 const serviceCategoriesPage = new ServiceCategoriesPage();
 
-describe('Setup API for Service Categories Test', () => {
-    it('Setup', async () => {
+describe("Setup API for Service Categories Test", () => {
+    it("Setup", async () => {
         await api.UseAPI(serviceCategories.setup);
     });
 });
-serviceCategories.tests.forEach(async serviceCategoriesData => {
-    serviceCategoriesData.logins.forEach(login => {
+serviceCategories.tests.forEach(async (serviceCategoriesData) => {
+    serviceCategoriesData.logins.forEach((login) => {
         describe(`Traffic Portal - ServiceCategories - ${login.description}`, () => {
-            it('can login', async () => {
+            it("can login", async () => {
                 browser.get(browser.params.baseUrl);
                 await loginPage.Login(login);
                 expect(await loginPage.CheckUserName(login)).toBeTruthy();
             });
-            it('can open service categories page', async () => {
+            it("can open service categories page", async () => {
                 await serviceCategoriesPage.OpenServicesMenu();
                 await serviceCategoriesPage.OpenServiceCategoriesPage();
             });
 
-            serviceCategoriesData.add.forEach(add => {
+            serviceCategoriesData.add.forEach((add) => {
                 it(add.description, async () => {
-                    expect(await serviceCategoriesPage.CreateServiceCategories(add)).toBeTruthy();
+                    expect(
+                        await serviceCategoriesPage.CreateServiceCategories(
+                            add,
+                            add.validationMessage
+                        )
+                    ).toBeTruthy();
                     await serviceCategoriesPage.OpenServiceCategoriesPage();
                 });
             });
-            serviceCategoriesData.update.forEach(update => {
+            serviceCategoriesData.update.forEach((update) => {
                 it(update.description, async () => {
-                    await serviceCategoriesPage.SearchServiceCategories(update.Name);
-                    expect(await serviceCategoriesPage.UpdateServiceCategories(update)).toBeTruthy();
+                    await serviceCategoriesPage.SearchServiceCategories(
+                        update.Name
+                    );
+                    expect(
+                        await serviceCategoriesPage.UpdateServiceCategories(
+                            update,
+                            update.validationMessage
+                        )
+                    ).toBeTruthy();
                     await serviceCategoriesPage.OpenServiceCategoriesPage();
                 });
             });
-            serviceCategoriesData.remove.forEach(remove => {
+            serviceCategoriesData.remove.forEach((remove) => {
                 it(remove.description, async () => {
-                    await serviceCategoriesPage.SearchServiceCategories(remove.Name);
-                    expect(await serviceCategoriesPage.DeleteServiceCategories(remove)).toBeTruthy();
+                    await serviceCategoriesPage.SearchServiceCategories(
+                        remove.Name
+                    );
+                    expect(
+                        await serviceCategoriesPage.DeleteServiceCategories(
+                            remove,
+                            remove.validationMessage
+                        )
+                    ).toBeTruthy();
                     await serviceCategoriesPage.OpenServiceCategoriesPage();
                 });
             });
-            it('can logout', async () => {
+            it("can logout", async () => {
                 expect(await topNavigation.Logout()).toBeTruthy();
             });
         });
     });
 });
 
-describe('Clean Up API for Service Categories Test', () => {
-    it('Cleanup', async () => {
+describe("Clean Up API for Service Categories Test", () => {
+    it("Cleanup", async () => {
         await api.UseAPI(serviceCategories.cleanup);
     });
 });