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 5f6b1c63b0 Update Topology Cache Group Servers table to use AG-Grid 
(#7849)
5f6b1c63b0 is described below

commit 5f6b1c63b0cd97aab655c126452a6f428d106cff
Author: The Anh Nguyen <[email protected]>
AuthorDate: Sun Nov 12 08:41:31 2023 +0100

    Update Topology Cache Group Servers table to use AG-Grid (#7849)
    
    * Update Topology Cache Group Servers table to use AG-Grid
    
    * reformat code + add case undefined update cache group
    
    * remove toggle test
    
    * add open topology menu
    
    * fix return wrong value when update cache groups
    
    * update delete cache group success message for testable
    
    * fix delete cg validation message
---
 CHANGELOG.md                                       |   1 +
 .../traffic_ops_golang/cachegroup/cachegroups.go   |   3 +-
 .../cacheGroups/TableCacheGroupsController.js      | 361 ++++++++++++---------
 .../table/cacheGroups/table.cacheGroups.tpl.html   |  60 +---
 traffic_portal/test/integration/Data/cachegroup.ts |  60 +---
 .../test/integration/PageObjects/CacheGroup.po.ts  | 234 ++++++-------
 .../test/integration/specs/CacheGroup.spec.ts      | 117 ++++---
 7 files changed, 399 insertions(+), 437 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef959871ba..1098d6d003 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 ### Changed
 - [#7614](https://github.com/apache/trafficcontrol/pull/7614) *Traffic Ops* 
The database upgrade process no longer overwrites changes users may have made 
to the initially seeded data.
 - [#7832](https://github.com/apache/trafficcontrol/pull/7832) *t3c* Removed 
perl dependency
+- Updated the CacheGroups Traffic Portal page to use a more performant 
AG-Grid-based table.
 
 ## [8.0.0] - 2023-09-20
 ### Added
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go 
b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
index 2a729bbbc0..f96ba19ff8 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
@@ -1405,8 +1405,7 @@ func DeleteCacheGroup(w http.ResponseWriter, r 
*http.Request) {
                return
        }
 
-       alertMessage := fmt.Sprintf("%s was deleted.", ID)
-       alerts := tc.CreateAlerts(tc.SuccessLevel, alertMessage)
+       alerts := tc.CreateAlerts(tc.SuccessLevel, "cache group was deleted.")
        api.WriteAlerts(w, r, http.StatusOK, alerts)
        return
 }
diff --git 
a/traffic_portal/app/src/common/modules/table/cacheGroups/TableCacheGroupsController.js
 
b/traffic_portal/app/src/common/modules/table/cacheGroups/TableCacheGroupsController.js
index 10ccff279f..473605a093 100644
--- 
a/traffic_portal/app/src/common/modules/table/cacheGroups/TableCacheGroupsController.js
+++ 
b/traffic_portal/app/src/common/modules/table/cacheGroups/TableCacheGroupsController.js
@@ -18,206 +18,249 @@
  */
 
 /**
- * @param {*} cacheGroups
+ * @typedef CacheGroup
+ * @property {number} id
+ * @property {string} name
+ * @property {number} shortName
+ * @property {number} latitude
+ * @property {number} longitude
+ * @property {string} parentCachegroupName
+ * @property {string} secondaryParentCachegroupName
+ * @property {string} typeName
+ * @property {string} lastUpdated
+ */
+
+/**
+ * @param {CacheGroup} cacheGroup
+ * @returns  {string}
+ */
+const getHref = (cacheGroup) => `#!/cache-groups/${cacheGroup.id}`;
+
+/**
+ * @param {CacheGroup[]} cacheGroups
  * @param {*} $scope
  * @param {*} $state
  * @param 
{import("../../../service/utils/angular.ui.bootstrap").IModalService} $uibModal
- * @param {import("angular").IWindowService} $window
  * @param {import("../../../service/utils/LocationUtils")} locationUtils
  * @param {import("../../../api/CacheGroupService")} cacheGroupService
  * @param {import("../../../models/MessageModel")} messageModel
  */
-var TableCacheGroupsController = function(cacheGroups, $scope, $state, 
$uibModal, $window, locationUtils, cacheGroupService, messageModel) {
-
-    let cacheGroupsTable;
+var TableCacheGroupsController = function (
+    cacheGroups,
+    $scope,
+    $state,
+    $uibModal,
+    locationUtils,
+    cacheGroupService,
+    messageModel
+) {
+    /**** Constants, scope data, etc. ****/
 
-    var queueServerUpdates = function(cacheGroup, cdnId) {
-        cacheGroupService.queueServerUpdates(cacheGroup.id, cdnId);
-    };
+    /** The columns of the ag-grid table */
+    $scope.columns = [
+        {
+            headerName: "Name",
+            field: "name",
+            hide: false,
+        },
+        {
+            headerName: "Short Name",
+            field: "shortName",
+            hide: false,
+        },
+        {
+            headerName: "Type",
+            field: "typeName",
+            hide: false,
+        },
+        {
+            headerName: "1st Parent",
+            field: "parentCachegroupName",
+            hide: false,
+        },
+        {
+            headerName: "2nd Parent",
+            field: "secondaryParentCachegroupName",
+            hide: false,
+        },
+        {
+            headerName: "Latitude",
+            field: "latitude",
+            hide: false,
+        },
+        {
+            headerName: "Longitude",
+            field: "longitude",
+            hide: false,
+        },
+        {
+            headerName: "ID",
+            field: "id",
+            filter: "agNumberColumnFilter",
+            hide: true,
+        },
+        {
+            headerName: "Last Updated",
+            field: "lastUpdated",
+            hide: true,
+            filter: "agDateColumnFilter",
+        },
+    ];
 
-    var clearServerUpdates = function(cacheGroup, cdnId) {
-        cacheGroupService.clearServerUpdates(cacheGroup.id, cdnId);
-    };
+    /** @type {import("../agGrid/CommonGridController").CGC.DropDownOption[]} 
*/
+    $scope.dropDownOptions = [
+        {
+            name: "createCacheGroupMenuItem",
+            href: "#!/cache-groups/new",
+            text: "Create New Cache Group",
+            type: 2,
+        },
+    ];
 
-    var deleteCacheGroup = function(cacheGroup) {
-        cacheGroupService.deleteCacheGroup(cacheGroup.id)
-            .then(function(result) {
-                messageModel.setMessages(result.alerts, false);
-                $scope.refresh();
-            });
+    /** Reloads all resolved data for the view. */
+    $scope.refresh = () => {
+        $state.reload();
     };
 
-    var confirmQueueServerUpdates = function(cacheGroup) {
-        var params = {
-            title: 'Queue Server Updates: ' + cacheGroup.name,
-            message: "Please select a CDN"
+    /**
+     * Deletes a Cache Group if confirmation is given.
+     * @param {CacheGroup} cacheGroup
+     */
+    function confirmDelete(cacheGroup) {
+        const params = {
+            title: `Delete Cache Group: ${cacheGroup.name}`,
+            key: cacheGroup.name,
         };
-        var modalInstance = $uibModal.open({
-            templateUrl: 'common/modules/dialog/select/dialog.select.tpl.html',
-            controller: 'DialogSelectController',
-            size: 'md',
-            resolve: {
-                params: function () {
-                    return params;
-                },
-                collection: function(cdnService) {
-                    return cdnService.getCDNs();
-                }
-            }
-        });
-        modalInstance.result.then(function(cdn) {
-            queueServerUpdates(cacheGroup, cdn.id);
-        }, function () {
-            // do nothing
+        const modalInstance = $uibModal.open({
+            templateUrl: "common/modules/dialog/delete/dialog.delete.tpl.html",
+            controller: "DialogDeleteController",
+            size: "md",
+            resolve: { params },
         });
-    };
+        modalInstance.result
+            .then(() => {
+                cacheGroupService
+                    .deleteCacheGroup(cacheGroup.id)
+                    .then((result) => {
+                        messageModel.setMessages(result.alerts, false);
+                        $scope.refresh();
+                    });
+            })
+            .catch((e) => console.error("failed to delete Cache Group:", e));
+    }
 
-    var confirmClearServerUpdates = function(cacheGroup) {
-        var params = {
-            title: 'Clear Server Updates: ' + cacheGroup.name,
-            message: "Please select a CDN"
+    /**
+     * Queues servers updates on a Cache Group if CDN is selected
+     * @param {CacheGroup} cacheGroup
+     */
+    function confirmQueueServerUpdates(cacheGroup) {
+        const params = {
+            title: `Queue Server Updates: ${cacheGroup.name}`,
+            message: "Please select a CDN",
         };
-        var modalInstance = $uibModal.open({
-            templateUrl: 'common/modules/dialog/select/dialog.select.tpl.html',
-            controller: 'DialogSelectController',
-            size: 'md',
+        const modalInstance = $uibModal.open({
+            templateUrl: "common/modules/dialog/select/dialog.select.tpl.html",
+            controller: "DialogSelectController",
+            size: "md",
             resolve: {
-                params: function () {
-                    return params;
-                },
-                collection: function(cdnService) {
-                    return cdnService.getCDNs();
-                }
-            }
-        });
-        modalInstance.result.then(function(cdn) {
-            clearServerUpdates(cacheGroup, cdn.id);
-        }, function () {
-            // do nothing
+                params,
+                collection: (cdnService) => cdnService.getCDNs(),
+            },
         });
-    };
+        modalInstance.result.then((cdn) =>
+            cacheGroupService.queueServerUpdates(cacheGroup.id, cdn.id)
+        );
+    }
 
-    var confirmDelete = function(cacheGroup) {
-        var params = {
-            title: 'Delete Cache Group: ' + cacheGroup.name,
-            key: cacheGroup.name
+    /**
+     * Clears servers updates on a Cache Group if confirmation is given.
+     * @param {CacheGroup} cacheGroup
+     */
+    function confirmClearServerUpdates(cacheGroup) {
+        const params = {
+            title: `Clear Server Updates: ${cacheGroup.name}`,
+            message: "Please select a CDN",
         };
-        var modalInstance = $uibModal.open({
-            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
-            controller: 'DialogDeleteController',
-            size: 'md',
+        const modalInstance = $uibModal.open({
+            templateUrl: "common/modules/dialog/select/dialog.select.tpl.html",
+            controller: "DialogSelectController",
+            size: "md",
             resolve: {
-                params: function () {
-                    return params;
-                }
-            }
+                params,
+                collection: (cdnService) => cdnService.getCDNs(),
+            },
         });
-        modalInstance.result.then(function() {
-            deleteCacheGroup(cacheGroup);
-        }, function () {
-            // do nothing
+        modalInstance.result.then((cdn) => {
+            cacheGroupService.clearServerUpdates(cacheGroup.id, cdn.id);
         });
-    };
-
-
-    $scope.cacheGroups = cacheGroups;
-
-    $scope.navigateToPath = (path, unsavedChanges) => 
locationUtils.navigateToPath(path, unsavedChanges);
+    }
 
-    $scope.columns = [
-        { "name": "Name", "visible": true, "searchable": true },
-        { "name": "Short Name", "visible": true, "searchable": true },
-        { "name": "Type", "visible": true, "searchable": true },
-        { "name": "1st Parent", "visible": true, "searchable": true },
-        { "name": "2nd Parent", "visible": true, "searchable": true },
-        { "name": "Latitude", "visible": true, "searchable": true },
-        { "name": "Longitude", "visible": true, "searchable": true }
-    ];
-
-    $scope.contextMenuItems = [
+    /** @type 
{import("../agGrid/CommonGridController").CGC.ContextMenuOption[]} */
+    $scope.contextMenuOptions = [
         {
-            text: 'Open in New Tab',
-            click: function ($itemScope) {
-                $window.open('/#!/cache-groups/' + $itemScope.cg.id, '_blank');
-            }
+            getHref,
+            getText: (cacheGroup) => `Open ${cacheGroup.name} in a new tab`,
+            newTab: true,
+            type: 2,
         },
-        null, // Dividier
+        { type: 0 },
         {
-            text: 'Edit',
-            click: function ($itemScope) {
-                $scope.editCacheGroup($itemScope.cg.id);
-            }
+            getHref,
+            text: "Edit",
+            type: 2,
         },
         {
-            text: 'Delete',
-            click: function ($itemScope) {
-                confirmDelete($itemScope.cg);
-            }
+            onClick: (cacheGroup) => confirmDelete(cacheGroup),
+            text: "Delete",
+            type: 1,
         },
-        null, // Dividier
+        { type: 0 },
         {
-            text: 'Queue Server Updates',
-            click: function ($itemScope) {
-                confirmQueueServerUpdates($itemScope.cg);
-            }
+            onClick: (cacheGroup) => confirmQueueServerUpdates(cacheGroup),
+            text: "Queue Server Updates",
+            type: 1,
         },
         {
-            text: 'Clear Server Updates',
-            click: function ($itemScope) {
-                confirmClearServerUpdates($itemScope.cg);
-            }
+            onClick: (cacheGroup) => confirmClearServerUpdates(cacheGroup),
+            text: "Clear Server Updates",
+            type: 1,
         },
-        null, // Dividier
+        { type: 0 },
         {
-            text: 'Manage ASNs',
-            click: function ($itemScope) {
-                locationUtils.navigateToPath('/cache-groups/' + 
$itemScope.cg.id + '/asns');
-            }
+            getHref: (cacheGroup) => `#!/cache-groups/${cacheGroup.id}/asns`,
+            text: "Manage ASNs",
+            type: 2,
         },
         {
-            text: 'Manage Servers',
-            click: function ($itemScope) {
-                locationUtils.navigateToPath('/cache-groups/' + 
$itemScope.cg.id + '/servers');
-            }
-        }
+            getHref: (cacheGroup) => 
`#!/cache-groups/${cacheGroup.id}/servers`,
+            text: "Manage Servers",
+            type: 2,
+        },
     ];
 
-    $scope.editCacheGroup = function(id) {
-        locationUtils.navigateToPath('/cache-groups/' + id);
-    };
-
-    $scope.createCacheGroup = function() {
-        locationUtils.navigateToPath('/cache-groups/new');
-    };
-
-    $scope.refresh = function() {
-        $state.reload(); // reloads all the resolves for the view
-    };
-
-    $scope.toggleVisibility = function(colName) {
-        const col = cacheGroupsTable.column(colName + ':name');
-        col.visible(!col.visible());
-        cacheGroupsTable.rows().invalidate().draw();
+    /** Options, configuration, data and callbacks for the ag-grid table. */
+    /** @type {import("../agGrid/CommonGridController").CGC.GridSettings} */
+    $scope.gridOptions = {
+        onRowClick: function (row) {
+            locationUtils.navigateToPath(`/cache-groups/${row.data.id}`);
+        },
     };
 
-    angular.element(document).ready(function () {
-        cacheGroupsTable = $('#cacheGroupsTable').DataTable({
-            "aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
-            "iDisplayLength": 25,
-            "aaSorting": [],
-            "columns": $scope.columns,
-            "initComplete": function(settings, json) {
-                try {
-                    // need to create the show/hide column checkboxes and bind 
to the current visibility
-                    $scope.columns = 
JSON.parse(localStorage.getItem('DataTables_cacheGroupsTable_/')).columns;
-                } catch (e) {
-                    console.error("Failure to retrieve required column info 
from localStorage (key=DataTables_cacheGroupsTable_/):", e);
-                }
-            }
-        });
-    });
-
+    $scope.cacheGroups = cacheGroups.map((cacheGroup) => ({
+        ...cacheGroup,
+        lastUpdated: new Date(
+            cacheGroup.lastUpdated.replace(" ", "T").replace("+00", "Z")
+        ),
+    }));
 };
 
-TableCacheGroupsController.$inject = ['cacheGroups', '$scope', '$state', 
'$uibModal', '$window', 'locationUtils', 'cacheGroupService', 'messageModel'];
+TableCacheGroupsController.$inject = [
+    "cacheGroups",
+    "$scope",
+    "$state",
+    "$uibModal",
+    "locationUtils",
+    "cacheGroupService",
+    "messageModel",
+];
 module.exports = TableCacheGroupsController;
diff --git 
a/traffic_portal/app/src/common/modules/table/cacheGroups/table.cacheGroups.tpl.html
 
b/traffic_portal/app/src/common/modules/table/cacheGroups/table.cacheGroups.tpl.html
index a2d5726824..44ef006cbb 100644
--- 
a/traffic_portal/app/src/common/modules/table/cacheGroups/table.cacheGroups.tpl.html
+++ 
b/traffic_portal/app/src/common/modules/table/cacheGroups/table.cacheGroups.tpl.html
@@ -18,54 +18,14 @@ under the License.
 -->
 
 <div class="x_panel">
-    <div class="x_title">
-        <ol class="breadcrumb pull-left">
-            <li class="active">Cache Groups</li>
-        </ol>
-        <div class="pull-right" role="group" ng-if="!settings.isNew">
-            <button name="createCacheGroupButton" class="btn btn-primary" 
title="Create Cache Group" ng-click="createCacheGroup()"><i class="fa 
fa-plus"></i></button>
-            <button class="btn btn-default" title="Refresh" 
ng-click="refresh()"><i class="fa fa-refresh"></i></button>
-            <div id="toggleColumns" class="btn-group" role="group" 
title="Select Table Columns" uib-dropdown is-open="columnSettings.isopen">
-                <button type="button" class="btn btn-default dropdown-toggle" 
uib-dropdown-toggle aria-haspopup="true" aria-expanded="false">
-                    <i class="fa fa-columns"></i>&nbsp;
-                    <span class="caret"></span>
-                </button>
-                <menu ng-click="$event.stopPropagation()" 
class="column-settings dropdown-menu-right dropdown-menu" uib-dropdown-menu>
-                    <li role="menuitem" ng-repeat="c in columns | 
orderBy:'name'">
-                        <div class="checkbox">
-                            <label><input type="checkbox" ng-model="c.visible" 
ng-click="toggleVisibility(c.name)"> {{::c.name}}</label>
-                        </div>
-                    </li>
-                </menu>
-            </div>
-        </div>
-        <div class="clearfix"></div>
-    </div>
-    <div class="x_content">
-        <br>
-        <table id="cacheGroupsTable" class="table responsive-utilities 
jambo_table">
-            <thead>
-            <tr class="headings">
-                <th>Name</th>
-                <th>Short Name</th>
-                <th>Type</th>
-                <th>1st Parent</th>
-                <th>2nd Parent</th>
-                <th>Latitude</th>
-                <th>Longitude</th>
-            </tr>
-            </thead>
-            <tbody>
-            <tr ng-click="editCacheGroup(cg.id)" ng-repeat="cg in 
::cacheGroups" context-menu="contextMenuItems">
-                <td data-search="^{{::cg.name}}$">{{::cg.name}}</td>
-                <td data-search="^{{::cg.shortName}}$">{{::cg.shortName}}</td>
-                <td data-search="^{{::cg.typeName}}$">{{::cg.typeName}}</td>
-                <td 
data-search="^{{::cg.parentCachegroupName}}$">{{::cg.parentCachegroupName}}</td>
-                <td 
data-search="^{{::cg.secondaryParentCachegroupName}}$">{{::cg.secondaryParentCachegroupName}}</td>
-                <td data-search="^{{::cg.latitude}}$">{{::cg.latitude}}</td>
-                <td data-search="^{{::cg.longitude}}$">{{::cg.longitude}}</td>
-            </tr>
-            </tbody>
-        </table>
-    </div>
+    <common-grid-controller
+        title="Cache Groups"
+        table-name="cacheGroups"
+        options="gridOptions"
+        data="cacheGroups"
+        columns="columns"
+        drop-down-options="dropDownOptions"
+        context-menu-options="contextMenuOptions"
+    >
+    </common-grid-controller>
 </div>
diff --git a/traffic_portal/test/integration/Data/cachegroup.ts 
b/traffic_portal/test/integration/Data/cachegroup.ts
index 92500b97fc..4a72d7c26d 100644
--- a/traffic_portal/test/integration/Data/cachegroup.ts
+++ b/traffic_portal/test/integration/Data/cachegroup.ts
@@ -27,22 +27,6 @@ export const cachegroups = {
                                        "password": "pa$$word"
                                }
                        ],
-                       toggle:[
-                               {
-                                       description: "hide first table column",
-                                       Name: "1st Parent"
-                               },
-                               {
-                                       description: "redisplay first table 
column",
-                                       Name: "1st Parent"
-                               }
-                       ],
-                       check: [
-                               {
-                                       description: "check CSV link from 
CacheGroup page",
-                                       Name: "Export as CSV"
-                               }
-                       ],
                        create: [
                                {
                                        Description: "create a EDGE_LOC 
cachegroup with FailOver CacheGroup Field",
@@ -111,12 +95,12 @@ export const cachegroups = {
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache1",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                },
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache3",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                }
                        ]
                },
@@ -128,22 +112,6 @@ export const cachegroups = {
                                        "password": "pa$$word"
                                }
                        ],
-                       toggle:[
-                               {
-                                       description: "hide first table column",
-                                       Name: "1st Parent"
-                               },
-                               {
-                                       description: "display first table 
column",
-                                       Name: "1st Parent"
-                               }
-                       ],
-                       check: [
-                               {
-                                       description: "check CSV link from 
CacheGroup page",
-                                       Name: "Export as CSV"
-                               }
-                       ],
                        create: [
                                {
                                        Description: "create a CacheGroup",
@@ -182,22 +150,6 @@ export const cachegroups = {
                                        "password": "pa$$word"
                                }
                        ],
-                       toggle:[
-                               {
-                                       description: "hide first table column",
-                                       Name: "1st Parent"
-                               },
-                               {
-                                       description: "display first table 
column",
-                                       Name: "1st Parent"
-                               }
-                       ],
-                       check: [
-                               {
-                                       description: "check CSV link from 
CacheGroup page",
-                                       Name: "Export as CSV"
-                               }
-                       ],
                        create: [
                                {
                                        Description: "create a EDGE_LOC 
cachegroup with FailOver CacheGroup Field",
@@ -266,22 +218,22 @@ export const cachegroups = {
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache2",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                },
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache4",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                },
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache5",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                },
                                {
                                        Description: "delete a cachegroup",
                                        Name: "TP_Cache6",
-                                       validationMessage: "was deleted"
+                                       validationMessage: "cache group was 
deleted."
                                }
                        ]
                }
diff --git a/traffic_portal/test/integration/PageObjects/CacheGroup.po.ts 
b/traffic_portal/test/integration/PageObjects/CacheGroup.po.ts
index d4ebeb0e06..578e18503a 100644
--- a/traffic_portal/test/integration/PageObjects/CacheGroup.po.ts
+++ b/traffic_portal/test/integration/PageObjects/CacheGroup.po.ts
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { browser, by, element } from 'protractor';
+import { browser, by, element } from "protractor";
 
+import { SideNavigationPage } from "./SideNavigationPage.po";
 import { randomize } from "../config";
-import { BasePage } from './BasePage.po';
-import { SideNavigationPage } from '../PageObjects/SideNavigationPage.po';
 
 interface CreateCacheGroup {
     Type: string;
@@ -37,133 +36,144 @@ interface UpdateCacheGroup {
     Type: string;
     FailoverCG?: string;
 }
-export class CacheGroupPage extends BasePage {
-    private btnCreateCacheGroups = element(by.name('createCacheGroupButton'));
-    private txtName = element(by.name("name"))
-    private txtShortName = element(by.name("shortName"));
-    private txtType = element(by.name("type"));
-    private txtLatitude = element(by.name("latitude"));
-    private txtLongtitude = element(by.name("longitude"));
-    private txtParentCacheGroup = element(by.name("parentCacheGroup"));
-    private txtSecondaryParentCG = 
element(by.name("secondaryParentCacheGroup"));
-    private txtFailoverCG = element(by.name("fallbackOptions"));
-    private txtSearch = 
element(by.id('cacheGroupsTable_filter')).element(by.css('label input'));
-    private txtConfirmCacheGroupName = 
element(by.name("confirmWithNameInput"));
-    private btnDelete = element(by.buttonText('Delete'));
-    private randomize = randomize;
-    private btnTableColumn = element(by.className("caret"))
+
+export class CacheGroupPage extends SideNavigationPage {
+    private txtName = element(by.name("name"));
+
     async OpenTopologyMenu() {
-        let snp = new SideNavigationPage();
-        await snp.ClickTopologyMenu();
+        await this.ClickTopologyMenu();
     }
+
+    /**
+     * Navigates the browser to the Cache Groups table page.
+     */
     async OpenCacheGroupsPage() {
-        let snp = new SideNavigationPage();
-        await snp.NavigateToCacheGroupsPage();
+        await this.NavigateToCacheGroupsPage();
     }
 
-    public async CreateCacheGroups(cachegroup: CreateCacheGroup, 
outputMessage: string): Promise<boolean> {
-        let result = false
-        let basePage = new BasePage();
-        if (cachegroup.Type == "EDGE_LOC") {
-            if (cachegroup.FailoverCG === undefined) {
-                throw new Error(`cachegroups with Type 'EDGE_LOC' must have 
FailoverCG`);
-            }
-            await this.btnCreateCacheGroups.click();
-            await this.txtName.sendKeys(cachegroup.Name + this.randomize);
-            await this.txtShortName.sendKeys(cachegroup.ShortName + 
this.randomize);
-            await this.txtType.sendKeys(cachegroup.Type);
-            await this.txtLatitude.sendKeys(cachegroup.Latitude);
-            await this.txtLongtitude.sendKeys(cachegroup.Longitude);
-            await 
this.txtParentCacheGroup.sendKeys(cachegroup.ParentCacheGroup);
-            await 
this.txtSecondaryParentCG.sendKeys(cachegroup.SecondaryParentCG);
-            await this.txtFailoverCG.sendKeys(cachegroup.FailoverCG);
-        } else {
-            await this.btnCreateCacheGroups.click();
-            await this.txtName.sendKeys(cachegroup.Name + this.randomize);
-            await this.txtShortName.sendKeys(cachegroup.ShortName + 
this.randomize);
-            await this.txtType.sendKeys(cachegroup.Type);
-            await this.txtLatitude.sendKeys(cachegroup.Latitude);
-            await this.txtLongtitude.sendKeys(cachegroup.Longitude);
-            await 
this.txtParentCacheGroup.sendKeys(cachegroup.ParentCacheGroup);
-            await 
this.txtSecondaryParentCG.sendKeys(cachegroup.SecondaryParentCG);
+    /**
+     * Creates a given Cache Group.
+     *
+     * @param cachegroup The CacheGroup to create.
+     * @param outputMessage The expected output message
+     * @returns Whether or not creation succeeded, which is judged by comparing
+     * the displayed Alert message to outputMessage - they must match
+     * *exactly*!
+     */
+    public async CreateCacheGroups(
+        cachegroup: CreateCacheGroup,
+        outputMessage: string
+    ): Promise<boolean> {
+        await this.OpenCacheGroupsPage();
+        await element(by.buttonText("More")).click();
+        await element(by.linkText("Create New Cache Group")).click();
+
+        if (
+            cachegroup.Type == "EDGE_LOC" &&
+            cachegroup.FailoverCG === undefined
+        ) {
+            throw new Error(
+                `cachegroups with Type 'EDGE_LOC' must have FailoverCG`
+            );
         }
-        await basePage.ClickCreate();
-        await basePage.GetOutputMessage().then(function (value) {
-            if (outputMessage == value) {
-                result = true;
-            } else {
-                result = false;
-            }
-        })
-        return result;
-    }
 
-    public async SearchCacheGroups(nameCG: string): Promise<boolean> {
-        let name = nameCG + 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;
+        const actions = [
+            this.txtName.sendKeys(cachegroup.Name + randomize),
+            element(by.name("shortName")).sendKeys(cachegroup.ShortName),
+            element(by.name("type")).sendKeys(cachegroup.Type),
+            element(by.name("latitude")).sendKeys(cachegroup.Latitude),
+            element(by.name("longitude")).sendKeys(cachegroup.Longitude),
+            element(by.name("parentCacheGroup")).sendKeys(
+                cachegroup.ParentCacheGroup
+            ),
+            element(by.name("secondaryParentCacheGroup")).sendKeys(
+                cachegroup.SecondaryParentCG
+            ),
+        ];
+
+        if (cachegroup.Type == "EDGE_LOC" && cachegroup.FailoverCG) {
+            actions.push(
+                element(by.name("fallbackOptions")).sendKeys(
+                    cachegroup.FailoverCG
+                )
+            );
         }
-        return false;
+
+        await Promise.all(actions);
+        await this.ClickCreate();
+        return this.GetOutputMessage().then((v) => outputMessage === v);
+    }
+
+    /**
+     * Searches the Cache Groups table for a specific Cache Group, and 
navigates to its details
+     * page.
+     *
+     * @param nameCG The Name of the Cache Group for which to search.
+     */
+    public async SearchCacheGroups(nameCG: string): Promise<void> {
+        nameCG += randomize;
+        await this.OpenCacheGroupsPage();
+        const searchInput = element(by.id("quickSearch"));
+        await searchInput.clear();
+        await searchInput.sendKeys(nameCG);
+        await element(by.cssContainingText("span", nameCG)).click();
     }
 
-    public async UpdateCacheGroups(cachegroup: UpdateCacheGroup, 
outputMessage: string | undefined): Promise<boolean | undefined> {
+    /**
+     * Updates a Cache Group's Name.
+     *
+     * @param cachegroup A definition of the CacheGroup renaming.
+     * @param outputMessage The expected output message
+     * @returns Whether or not renaming succeeded.
+     */
+    public async UpdateCacheGroups(
+        cachegroup: UpdateCacheGroup,
+        outputMessage: string | undefined
+    ): Promise<boolean | undefined> {
         let result: boolean | undefined = false;
-        let basePage = new BasePage();
-        let snp = new SideNavigationPage();
         if (cachegroup.Type == "EDGE_LOC") {
-            const name = cachegroup.FailoverCG + this.randomize;
-            await this.txtFailoverCG.click();
-            if (await 
browser.isElementPresent(element(by.css(`select[name="fallbackOptions"] > 
option[label="${name}"]`)))) {
-                await element(by.css(`select[name="fallbackOptions"] > 
option[label="${name}"]`)).click();
+            const name = cachegroup.FailoverCG + randomize;
+            await element(by.name("fallbackOptions")).click();
+            if (
+                await browser.isElementPresent(
+                    element(
+                        by.css(
+                            `select[name="fallbackOptions"] > 
option[label="${name}"]`
+                        )
+                    )
+                )
+            ) {
+                await element(
+                    by.css(
+                        `select[name="fallbackOptions"] > 
option[label="${name}"]`
+                    )
+                ).click();
             } else {
                 result = undefined;
             }
         }
-        await this.txtType.sendKeys(cachegroup.Type);
-        await snp.ClickUpdate();
+        await element(by.name("type")).sendKeys(cachegroup.Type);
+        await this.ClickUpdate();
         if (result !== undefined) {
-            await basePage.GetOutputMessage().then(function (value) {
-                if (outputMessage === value) {
-                    result = true;
-                } else {
-                    result = false;
-                }
-            })
+            return (await this.GetOutputMessage()) === outputMessage;
         }
-        return result;
     }
-    public async DeleteCacheGroups(nameCG: string, outputMessage: string) {
-        let result = false;
-        let basePage = new BasePage();
-        let snp = new SideNavigationPage();
-        let name = nameCG + this.randomize;
-        await this.btnDelete.click();
-        await this.txtConfirmCacheGroupName.sendKeys(name);
-        if (await basePage.ClickDeletePermanently() == true) {
-            result = await basePage.GetOutputMessage().then(function (value) {
-                if (value.indexOf(outputMessage) > -1) {
-                    return true
-                } else {
-                    return false;
-                }
-            })
-        } else {
-            await basePage.ClickCancel();
-        }
-        await snp.NavigateToCacheGroupsPage();
-        return result;
-    }
-    public async CheckCSV(name: string): Promise<boolean> {
-        return element(by.cssContainingText("span", name)).isPresent();
-    }
-    public async ToggleTableColumn(name: string): Promise<boolean> {
-        await this.btnTableColumn.click();
-        const result = await element(by.cssContainingText("th", 
name)).isPresent();
-        await element(by.cssContainingText("label", name)).click();
-        await this.btnTableColumn.click();
-        return !result;
+
+    /**
+     * Deletes a Cache Group.
+     *
+     * @param nameCG The Name of the Cache Group to be deleted.
+     * @param outputMessage The expected output message
+     * @returns Whether or not the deletion succeeded.
+     */
+    public async DeleteCacheGroups(
+        nameCG: string,
+        outputMessage: string
+    ): Promise<boolean> {
+        nameCG += randomize;
+        await element(by.buttonText("Delete")).click();
+        await element(by.name("confirmWithNameInput")).sendKeys(nameCG);
+        await this.ClickDeletePermanently();
+        return (await this.GetOutputMessage()) === outputMessage;
     }
 }
diff --git a/traffic_portal/test/integration/specs/CacheGroup.spec.ts 
b/traffic_portal/test/integration/specs/CacheGroup.spec.ts
index b5e9ec9618..57aaddb654 100644
--- a/traffic_portal/test/integration/specs/CacheGroup.spec.ts
+++ b/traffic_portal/test/integration/specs/CacheGroup.spec.ts
@@ -16,80 +16,77 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { browser } from 'protractor';
+import { browser } from "protractor";
 
-import { LoginPage } from '../PageObjects/LoginPage.po'
-import { CacheGroupPage } from '../PageObjects/CacheGroup.po';
-import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
+import { LoginPage } from "../PageObjects/LoginPage.po";
+import { CacheGroupPage } from "../PageObjects/CacheGroup.po";
+import { TopNavigationPage } from "../PageObjects/TopNavigationPage.po";
 import { cachegroups } from "../Data";
 
-
-
 let loginPage = new LoginPage();
 let topNavigation = new TopNavigationPage();
 let cacheGroupPage = new CacheGroupPage();
 
-cachegroups.tests.forEach(cacheGroupData => {
-    describe(`Traffic Portal - CacheGroup - ${cacheGroupData.testName}`, () => 
{
-        cacheGroupData.logins.forEach(login => {
-            it('can login', async function () {
+cachegroups.tests.forEach((cacheGroupData) => {
+    for (const login of cacheGroupData.logins) {
+        describe(`Traffic Portal - CacheGroup - ${cacheGroupData.testName}`, 
() => {
+            beforeAll(async () => {
                 browser.get(browser.params.baseUrl);
                 await loginPage.Login(login);
                 expect(await loginPage.CheckUserName(login)).toBeTruthy();
-            })
-            it('can open cache group page', async function () {
                 await cacheGroupPage.OpenTopologyMenu();
                 await cacheGroupPage.OpenCacheGroupsPage();
-            })
-            cacheGroupData.check.forEach(check => {
-                it(check.description, async () => {
-                    expect(await 
cacheGroupPage.CheckCSV(check.Name)).toBe(true);
-                    await cacheGroupPage.OpenCacheGroupsPage();
-                });
             });
-            cacheGroupData.toggle.forEach(toggle => {
-                it(toggle.description, async () => {
-                    if(toggle.description.includes('hide')){
-                        expect(await 
cacheGroupPage.ToggleTableColumn(toggle.Name)).toBe(false);
-                        await cacheGroupPage.OpenCacheGroupsPage();
-                    }else{
-                        expect(await 
cacheGroupPage.ToggleTableColumn(toggle.Name)).toBe(true);
-                        await cacheGroupPage.OpenCacheGroupsPage();
-                    }
-                    
+            afterAll(async () => {
+                expect(await topNavigation.Logout()).toBeTruthy();
+            });
+            afterEach(async () => {
+                await cacheGroupPage.OpenCacheGroupsPage();
+            });
+            for (const create of cacheGroupData.create) {
+                it(create.Description, async () => {
+                    expect(
+                        await cacheGroupPage.CreateCacheGroups(
+                            create,
+                            create.validationMessage
+                        )
+                    ).toBeTruthy();
                 });
-            })
-            cacheGroupData.create.forEach(create => {
-                it(create.Description, async function () {
-                    expect(await cacheGroupPage.CreateCacheGroups(create, 
create.validationMessage)).toBeTruthy();
-                    await cacheGroupPage.OpenCacheGroupsPage();
-                })
-            })
-            cacheGroupData.update.forEach(update => {
+            }
+            for (const update of cacheGroupData.update) {
                 if (update.Description.includes("cannot")) {
-                    it(update.Description, async function () {
-                        await cacheGroupPage.SearchCacheGroups(update.Name)
-                        expect(await cacheGroupPage.UpdateCacheGroups(update, 
update.validationMessage)).toBeUndefined();
-                        await cacheGroupPage.OpenCacheGroupsPage();
-                    })
+                    it(update.Description, async () => {
+                        await cacheGroupPage.SearchCacheGroups(update.Name);
+                        expect(
+                            await cacheGroupPage.UpdateCacheGroups(
+                                update,
+                                update.validationMessage
+                            )
+                        ).toBeUndefined();
+                    });
                 } else {
-                    it(update.Description, async function () {
-                        await cacheGroupPage.SearchCacheGroups(update.Name)
-                        expect(await cacheGroupPage.UpdateCacheGroups(update, 
update.validationMessage)).toBeTruthy();
-                        await cacheGroupPage.OpenCacheGroupsPage();
-                    })
+                    it(update.Description, async () => {
+                        await cacheGroupPage.SearchCacheGroups(update.Name);
+                        expect(
+                            await cacheGroupPage.UpdateCacheGroups(
+                                update,
+                                update.validationMessage
+                            )
+                        ).toBeTruthy();
+                    });
                 }
-
-            })
-            cacheGroupData.remove.forEach(remove => {
-                it(remove.Description, async function () {
-                    await cacheGroupPage.SearchCacheGroups(remove.Name)
-                    expect(await cacheGroupPage.DeleteCacheGroups(remove.Name, 
remove.validationMessage)).toBeTruthy();
-                })
-            })
-            it('can logout', async function () {
-                expect(await topNavigation.Logout()).toBeTruthy();
-            })
-        })
-    })
-})
+            }
+            for (const remove of cacheGroupData.remove) {
+                it(remove.Description, async () => {
+                    await cacheGroupPage.SearchCacheGroups(remove.Name);
+                    expect(
+                        await cacheGroupPage.DeleteCacheGroups(
+                            remove.Name,
+                            remove.validationMessage
+                        )
+                    ).toBeTruthy();
+                });
+            }
+        });
+    }
+});


Reply via email to