You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2024/01/22 02:22:58 UTC

(trafficcontrol) branch master updated: Switch TPv2 E2E tests to Cypress (#7894)

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

shamrick 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 d8e78deb56 Switch TPv2 E2E tests to Cypress (#7894)
d8e78deb56 is described below

commit d8e78deb56c21e0c321ff307944b24ac064876a9
Author: ocket8888 <oc...@apache.org>
AuthorDate: Sun Jan 21 19:22:51 2024 -0700

    Switch TPv2 E2E tests to Cypress (#7894)
    
    * Port the login e2e test to cypress
    
    * Add initial mock data loading for Cypress
    
    * Remove unnecessary example file
    
    * Extract login to a custom command for reuse
    
    * Convert CG tests to Cypress
    
    * Simplify mock data structure
    
    * Add missing enablement check
    
    * Convert Divisions tests to Cypress
    
    * Convert Regions tests to Cypress
    
    * Convert Coordinates tests to Cypress
    
    * Convert ASN tests to Cypress
    
    * Convert CDN tests to Cypress
    
    * Convert DS tests to Cypress
    
    * Convert Parameter tests to Cypress
    
    * Convert Profile tests to Cypress
    
    * Convert server tests to Cypress
    
    * Convert server Capability tests to Cypress
    
    * Convert Physical Location tests to Cypress
    
    * Fix weird name in fixture data
    
    * Convert Status tests to Cypress
    
    * Convert Type tests to Cypress
    
    * Add a model for the login data
    
    * Convert User tests to Cypress
    
    * Convert Change Log tests to Cypress
    
    * Convert Role tests to Cypress
    
    * Convert Tenant tests to Cypress
    
    * Remove testing initial stup for nightwatch
    
    * Move documentation to the cypress directory
    
    * Remove lingering nightwatch files
    
    * Add headless run NPM script
    
    * Remove Angular architect runner for nightwatch
    
    * Add screenshots to .gitignore
    
    * Remove lingering Nightwatch references
    
    Also ran npm audit fix
    
    * Remove now-unnecessary step in TPv2 workflow
    
    * Fix waiting for a server that won't ever start
    
    * (Re-?)Install @angular-devkit/architect
    
    * Fix TO API port
    
    * Fix GHA artifact paths
    
    * Remove running TPv2 server and collecting logs
---
 .github/actions/tpv2-integration-tests/config.json |     8 -
 .../actions/tpv2-integration-tests/entrypoint.sh   |     8 +-
 .github/workflows/tpv2.yml                         |     6 +-
 experimental/traffic-portal/.gitignore             |     2 -
 experimental/traffic-portal/README.md              |     7 +-
 experimental/traffic-portal/angular.json           |   457 +-
 experimental/traffic-portal/cypress.config.ts      |   403 +
 experimental/traffic-portal/cypress/.gitignore     |    19 +
 experimental/traffic-portal/cypress/README.md      |    35 +
 .../cypress/e2e/cacheGroups/asns/detail.cy.ts      |    43 +
 .../e2e/cacheGroups/asns/table.cy.ts}              |    16 +-
 .../e2e/cacheGroups/coordinates/detail.cy.ts       |    45 +
 .../e2e/cacheGroups/coordinates/table.cy.ts}       |    16 +-
 .../cypress/e2e/cacheGroups/detail.cy.ts           |    42 +
 .../cypress/e2e/cacheGroups/divisions/detail.cy.ts |    41 +
 .../e2e/cacheGroups/divisions/table.cy.ts}         |    16 +-
 .../cypress/e2e/cacheGroups/regions/detail.cy.ts   |    41 +
 .../e2e/cacheGroups/regions/table.cy.ts}           |    16 +-
 .../e2e/cacheGroups/table.cy.ts}                   |    16 +-
 .../traffic-portal/cypress/e2e/cdns/detail.cy.ts   |    41 +
 .../cypress/e2e/deliveryServices/ds.card.cy.ts     |    42 +
 .../cypress/e2e/deliveryServices/ds.details.cy.ts  |    58 +
 .../e2e/deliveryServices/ds.invalidate.cy.ts       |    58 +
 .../traffic-portal/cypress/e2e/login.cy.ts         |    44 +
 .../cypress/e2e/parameters/detail.cy.ts            |    45 +
 .../e2e/parameters/table.cy.ts}                    |    16 +-
 .../cypress/e2e/profiles/detail.cy.ts              |    49 +
 .../e2e/profiles/table.cy.ts}                      |    16 +-
 .../cypress/e2e/servers/capabilities/detail.cy.ts  |    39 +
 .../e2e/servers/capabilities/table.cy.ts}          |    16 +-
 .../e2e/servers/physical.location/detail.cy.ts     |    51 +
 .../e2e/servers/physical.location/table.cy.ts}     |    16 +-
 .../cypress/e2e/servers/servers.detail.cy.ts       |    86 +
 .../e2e/servers/servers.table.cy.ts}               |    15 +-
 .../cypress/e2e/statuses/detail.cy.ts              |    45 +
 .../e2e/statuses/table.cy.ts}                      |    26 +-
 .../traffic-portal/cypress/e2e/types/detail.cy.ts  |    45 +
 .../e2e/types/table.cy.ts}                         |    26 +-
 .../e2e/users/changeLogs.cy.ts}                    |    18 +-
 .../cypress/e2e/users/role/detail.cy.ts            |    43 +
 .../e2e/users/role/table.cy.ts}                    |    16 +-
 .../cypress/e2e/users/tenant/detail.cy.ts          |    53 +
 .../e2e/users/tenant/table.cy.ts}                  |    26 +-
 .../e2e/users/users.cy.ts}                         |    22 +-
 .../traffic-portal/cypress/fixtures/login.json     |     4 +
 .../traffic-portal/cypress/fixtures/to.config.json |     6 +
 .../traffic-portal/cypress/support/commands.ts     |    61 +
 .../cypress/support/component-index.html           |    25 +
 .../traffic-portal/cypress/support/component.ts    |    53 +
 experimental/traffic-portal/cypress/support/e2e.ts |    31 +
 .../traffic-portal/cypress/support/testing.data.ts |    65 +
 experimental/traffic-portal/cypress/tsconfig.json  |     8 +
 .../traffic-portal/nightwatch/.eslintrc.json       |    51 -
 experimental/traffic-portal/nightwatch/README.md   |    26 -
 experimental/traffic-portal/nightwatch/config.json |     9 -
 .../traffic-portal/nightwatch/dataClient.ts        |   388 -
 .../traffic-portal/nightwatch/globals/globals.ts   |   227 -
 .../nightwatch/globals/tables/index.ts             |   170 -
 .../traffic-portal/nightwatch/nightwatch.conf.js   |   202 -
 .../page_objects/cacheGroups/asnDetail.ts          |    32 -
 .../page_objects/cacheGroups/asnsTable.ts          |    46 -
 .../page_objects/cacheGroups/cacheGroupDetails.ts  |    35 -
 .../page_objects/cacheGroups/cacheGroupsTable.ts   |    46 -
 .../page_objects/cacheGroups/coordinateDetail.ts   |    33 -
 .../page_objects/cacheGroups/coordinatesTable.ts   |    46 -
 .../page_objects/cacheGroups/divisionDetail.ts     |    31 -
 .../page_objects/cacheGroups/divisionsTable.ts     |    46 -
 .../page_objects/cacheGroups/regionDetail.ts       |    32 -
 .../page_objects/cacheGroups/regionsTable.ts       |    46 -
 .../nightwatch/page_objects/cdns/cdnDetail.ts      |    33 -
 .../nightwatch/page_objects/common.ts              |    80 -
 .../deliveryServices/deliveryServiceCard.ts        |    77 -
 .../deliveryServices/deliveryServiceDetail.ts      |    45 -
 .../nightwatch/page_objects/login.ts               |    74 -
 .../page_objects/parameters/parameterDetail.ts     |    48 -
 .../page_objects/parameters/parametersTable.ts     |    45 -
 .../page_objects/profiles/profileDetail.ts         |    51 -
 .../page_objects/profiles/profilesTable.ts         |    46 -
 .../servers/capabilities/capabilitiesTable.ts      |    45 -
 .../servers/capabilities/capabilityDetails.ts      |    36 -
 .../page_objects/servers/physLocDetail.ts          |    41 -
 .../page_objects/servers/physLocTable.ts           |    46 -
 .../page_objects/servers/serversDetail.ts          |    68 -
 .../page_objects/servers/serversTable.ts           |    76 -
 .../page_objects/statuses/statusDetail.ts          |    32 -
 .../page_objects/statuses/statusesTable.ts         |    45 -
 .../nightwatch/page_objects/types/typeDetail.ts    |    33 -
 .../nightwatch/page_objects/types/typesTable.ts    |    45 -
 .../nightwatch/page_objects/users/changeLogs.ts    |    53 -
 .../nightwatch/page_objects/users/roleDetail.ts    |    39 -
 .../nightwatch/page_objects/users/rolesTable.ts    |    46 -
 .../nightwatch/page_objects/users/tenantDetail.ts  |    31 -
 .../nightwatch/page_objects/users/tenants.ts       |    46 -
 .../nightwatch/page_objects/users/users.ts         |    55 -
 .../tests/cacheGroups/asns/detail.spec.ts          |    44 -
 .../tests/cacheGroups/asns/table.spec.ts           |    26 -
 .../tests/cacheGroups/coordinates/detail.spec.ts   |    50 -
 .../tests/cacheGroups/coordinates/table.spec.ts    |    26 -
 .../nightwatch/tests/cacheGroups/detail.spec.ts    |    42 -
 .../tests/cacheGroups/divisions/detail.spec.ts     |    42 -
 .../tests/cacheGroups/divisions/table.spec.ts      |    26 -
 .../tests/cacheGroups/regions/detail.spec.ts       |    44 -
 .../tests/cacheGroups/regions/table.spec.ts        |    26 -
 .../nightwatch/tests/cacheGroups/table.spec.ts     |    26 -
 .../nightwatch/tests/cdns/detail.spec.ts           |    42 -
 .../tests/deliveryServices/ds.card.spec.ts         |    33 -
 .../tests/deliveryServices/ds.details.spec.ts      |    65 -
 .../tests/deliveryServices/ds.invalidate.spec.ts   |    63 -
 .../nightwatch/tests/login/login.spec.ts           |    40 -
 .../nightwatch/tests/parameters/detail.spec.ts     |    47 -
 .../nightwatch/tests/parameters/table.spec.ts      |    26 -
 .../nightwatch/tests/profiles/detail.spec.ts       |    50 -
 .../nightwatch/tests/profiles/table.spec.ts        |    26 -
 .../tests/servers/capabilities/detail.spec.ts      |    39 -
 .../tests/servers/capabilities/table.spec.ts       |    26 -
 .../tests/servers/physLoc/detail.spec.ts           |    52 -
 .../nightwatch/tests/servers/physLoc/table.spec.ts |    26 -
 .../tests/servers/servers.detail.spec.ts           |   101 -
 .../nightwatch/tests/statuses/detail.spec.ts       |    44 -
 .../nightwatch/tests/types/detail.spec.ts          |    46 -
 .../nightwatch/tests/users/role/detail.spec.ts     |    42 -
 .../nightwatch/tests/users/tenant/detail.spec.ts   |    56 -
 .../nightwatch/tests/users/users.spec.ts           |    43 -
 .../traffic-portal/nightwatch/tsconfig.e2e.json    |    13 -
 experimental/traffic-portal/package-lock.json      | 44996 +++++++------------
 experimental/traffic-portal/package.json           |    17 +-
 126 files changed, 17634 insertions(+), 33715 deletions(-)

diff --git a/.github/actions/tpv2-integration-tests/config.json b/.github/actions/tpv2-integration-tests/config.json
deleted file mode 100644
index 00d1287511..0000000000
--- a/.github/actions/tpv2-integration-tests/config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "insecure": true,
-  "port": 4200,
-  "trafficOps": "https://localhost:6443",
-  "tpv1Url": "https://localhost",
-  "useSSL": false,
-  "browserFolder": "$GITHUB_WORKSPACE/$BROWSER_FOLDER"
-}
diff --git a/.github/actions/tpv2-integration-tests/entrypoint.sh b/.github/actions/tpv2-integration-tests/entrypoint.sh
index b184e1e90f..e6d9343021 100755
--- a/.github/actions/tpv2-integration-tests/entrypoint.sh
+++ b/.github/actions/tpv2-integration-tests/entrypoint.sh
@@ -26,13 +26,9 @@ envsubst <../../.github/actions/tpv2-integration-tests/cdn.json >./cdn.conf
 
 ./traffic_ops_golang --cfg ./cdn.conf --dbcfg ../../.github/actions/tpv2-integration-tests/database.json > out.log 2>&1 &
 
-cd "${GITHUB_WORKSPACE}/experimental/traffic-portal/dist/traffic-portal"
-envsubst <${GITHUB_WORKSPACE}/.github/actions/tpv2-integration-tests/config.json >./config.json
-node ./server/main.js -C config.json > "${GITHUB_WORKSPACE}/tp.log" 2>&1 &
-
 timeout 3m bash <<TMOUT
-	while ! curl -k "http://localhost:4200/api/4.0/ping" >/dev/null 2>&1; do
-		echo "waiting for TP dev server to proxy TO API"
+	while ! curl -k "https://localhost:6443/api/4.0/ping" >/dev/null 2>&1; do
+		echo "waiting for TO API"
 		sleep 5
 	done
 TMOUT
diff --git a/.github/workflows/tpv2.yml b/.github/workflows/tpv2.yml
index ed6df39554..354b0ded84 100644
--- a/.github/workflows/tpv2.yml
+++ b/.github/workflows/tpv2.yml
@@ -256,7 +256,6 @@ jobs:
         run: |
           cd experimental/traffic-portal
           npx ng build
-          npx ng run traffic-portal:server
       - name: Run everything and test
         uses: ./.github/actions/tpv2-integration-tests
       - name: Upload Report
@@ -267,10 +266,7 @@ jobs:
           path: |
             traffic_ops/traffic_ops_golang/out.log
             experimental/traffic-portal/logs
-            experimental/traffic-portal/nightwatch/junit
-            experimental/traffic-portal/nightwatch/screens
-            experimental/traffic-portal/tests_output
-            tp.log
+            experimental/traffic-portal/cypress/screenshots
 
       - name: Save Alpine Docker image
         run: .github/actions/save-alpine-tar/entrypoint.sh save ${{ env.ALPINE_VERSION }}
diff --git a/experimental/traffic-portal/.gitignore b/experimental/traffic-portal/.gitignore
index 5a68a22837..cbf572988d 100644
--- a/experimental/traffic-portal/.gitignore
+++ b/experimental/traffic-portal/.gitignore
@@ -22,8 +22,6 @@
 /dist
 /.angular
 /.npm
-/nightwatch/tests_output
-/nightwatch/screens
 /tests_output
 /logs
 # Only exists if Bazel was run
diff --git a/experimental/traffic-portal/README.md b/experimental/traffic-portal/README.md
index cf5d57f73f..adaa925cf8 100644
--- a/experimental/traffic-portal/README.md
+++ b/experimental/traffic-portal/README.md
@@ -90,7 +90,7 @@ Optional arguments:
   -p PORT, --port PORT  Specify the port on which Traffic Portal will listen
                         (Default: 4200)
   -d DIST_PATH, --browser-folder DIST_PATH
-                        Specifiy locaiton for the folder that holds the 
+                        Specifiy locaiton for the folder that holds the
                         browser files
   -c CERT_PATH, --cert-path CERT_PATH
                         Specify a location for an SSL certificate to be used
@@ -164,8 +164,9 @@ use the command `ng test` (dependencies must first be installed). This will
 attempt to open Chrome, Firefox and Opera, so ideally you would have those
 installed prior to running the tests.
 
-End-to-end testing uses NightwatchJS and can be run by using `ng e2e`. More
-detailed instructions can be found in the `nightwatch/` folder
+End-to-end testing uses [Cypress](https://www.cypress.io/) and can be run by
+using `ng e2e`. More detailed instructions can be found in the README in the
+`cypress/` directory.
 
 ## Extending Traffic Portal
 Traffic Portal supports extending functionality through the use of Angular modules.
diff --git a/experimental/traffic-portal/angular.json b/experimental/traffic-portal/angular.json
index a2fa69abff..2353b04a85 100644
--- a/experimental/traffic-portal/angular.json
+++ b/experimental/traffic-portal/angular.json
@@ -1,219 +1,242 @@
 {
-	"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
-	"cli": {
-		"analytics": false
-	},
-	"version": 1,
-	"newProjectRoot": "projects",
-	"projects": {
-		"traffic-portal": {
-			"projectType": "application",
-			"schematics": {
-				"@schematics/angular:component": {
-					"style": "scss"
-				}
-			},
-			"root": "",
-			"sourceRoot": "src",
-			"prefix": "tp",
-			"architect": {
-				"build": {
-					"builder": "@angular-builders/custom-webpack:browser",
-					"options": {
-						"allowedCommonJsDependencies": [
-							"chart.js",
-							"node-forge"
-						],
-						"customWebpackConfig": {
-							"path": "src/compress-webpack.config.js"
-						},
-						"optimization": false,
-						"sourceMap": true,
-						"namedChunks": false,
-						"extractLicenses": false,
-						"vendorChunk": true,
-						"buildOptimizer": false,
-						"outputPath": "dist/traffic-portal/browser",
-						"index": "src/index.html",
-						"main": "src/main.ts",
-						"polyfills": "src/polyfills.ts",
-						"tsConfig": "tsconfig.app.json",
-						"aot": true,
-						"assets": [
-							"src/favicon.ico",
-							"src/assets"
-						],
-						"styles": [
-							"src/styles.scss",
-							{
-								"inject": false,
-								"input": "src/styles/themes/dark-default.scss",
-								"bundleName": "dark-default-theme"
-							}
-						],
-						"scripts": []
-					},
-					"configurations": {
-						"production": {
-							"fileReplacements": [
-								{
-									"replace": "src/environments/environment.ts",
-									"with": "src/environments/environment.prod.ts"
-								}
-							],
-							"optimization": true,
-							"outputHashing": "all",
-							"sourceMap": false,
-							"namedChunks": false,
-							"extractLicenses": true,
-							"vendorChunk": false,
-							"buildOptimizer": true,
-							"budgets": [
-								{
-									"type": "initial",
-									"maximumWarning": "4mb",
-									"maximumError": "5mb"
-								},
-								{
-									"type": "anyComponentStyle",
-									"maximumWarning": "6kb",
-									"maximumError": "10kb"
-								}
-							]
-						}
-					}
-				},
-				"serve": {
-					"builder": "@angular-devkit/build-angular:dev-server",
-					"options": {
-						"browserTarget": "traffic-portal:build",
-						"proxyConfig": "proxy.json"
-					},
-					"configurations": {
-						"production": {
-							"browserTarget": "traffic-portal:build:production"
-						}
-					}
-				},
-				"extract-i18n": {
-					"builder": "@angular-devkit/build-angular:extract-i18n",
-					"options": {
-						"browserTarget": "traffic-portal:build"
-					}
-				},
-				"test": {
-					"builder": "@angular-devkit/build-angular:karma",
-					"options": {
-						"main": "src/test.ts",
-						"polyfills": "src/polyfills.ts",
-						"tsConfig": "tsconfig.spec.json",
-						"karmaConfig": "karma.conf.js",
-						"assets": [
-							"src/favicon.ico",
-							"src/assets"
-						],
-						"styles": [
-							"src/styles.scss"
-						],
-						"scripts": []
-					}
-				},
-				"lint": {
-					"builder": "@angular-eslint/builder:lint",
-					"options": {
-						"lintFilePatterns": [
-							"src/**/*.ts",
-							"src/**/*.html",
-							"nightwatch/**/*.ts"
-						]
-					}
-				},
-				"server": {
-					"builder": "@angular-devkit/build-angular:server",
-					"options": {
-						"outputPath": "dist/traffic-portal/server",
-						"main": "server.ts",
-						"tsConfig": "tsconfig.server.json",
-						"sourceMap": true,
-						"buildOptimizer": false,
-						"optimization": false
-					},
-					"configurations": {
-						"production": {
-							"outputHashing": "media",
-							"fileReplacements": [
-								{
-									"replace": "src/environments/environment.ts",
-									"with": "src/environments/environment.prod.ts"
-								}
-							],
-							"sourceMap": false,
-							"optimization": true,
-							"buildOptimizer": true
-						}
-					}
-				},
-				"serve-ssr": {
-					"builder": "@nguniversal/builders:ssr-dev-server",
-					"options": {
-						"browserTarget": "traffic-portal:build",
-						"serverTarget": "traffic-portal:server"
-					},
-					"configurations": {
-						"production": {
-							"browserTarget": "traffic-portal:build:production",
-							"serverTarget": "traffic-portal:server:production"
-						}
-					}
-				},
-				"prerender": {
-					"builder": "@nguniversal/builders:prerender",
-					"options": {
-						"browserTarget": "traffic-portal:build:production",
-						"serverTarget": "traffic-portal:server:production",
-						"routes": [
-							"/"
-						]
-					},
-					"configurations": {
-						"production": {}
-					}
-				},
-				"nightwatch-run": {
-					"builder": "@nightwatch/schematics:nightwatch",
-					"options": {
-						"devServerTarget": "traffic-portal:serve",
-						"tsConfig": "./nightwatch/tsconfig.e2e.json",
-						"config": "./nightwatch/nightwatch.conf.js"
-					},
-					"configurations": {
-						"production": {
-							"devServerTarget": "traffic-portal:serve:production"
-						}
-					}
-				},
-				"e2e": {
-					"builder": "@nightwatch/schematics:nightwatch",
-					"options": {
-						"devServerTarget": "traffic-portal:serve",
-						"tsConfig": "./nightwatch/tsconfig.e2e.json",
-						"config": "./nightwatch/nightwatch.conf.js"
-					},
-					"configurations": {
-						"production": {
-							"devServerTarget": "traffic-portal:serve:production"
-						}
-					}
-				}
-			}
-		}
-	},
-	"schematics": {
-		"@angular-eslint/schematics:application": {
-			"setParserOptionsProject": true
-		},
-		"@angular-eslint/schematics:library": {
-			"setParserOptionsProject": true
-		}
-	}
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "cli": {
+    "analytics": false,
+    "schematicCollections": [
+      "@cypress/schematic",
+      "@schematics/angular"
+    ]
+  },
+  "version": 1,
+  "newProjectRoot": "projects",
+  "projects": {
+    "traffic-portal": {
+      "projectType": "application",
+      "schematics": {
+        "@schematics/angular:component": {
+          "style": "scss"
+        }
+      },
+      "root": "",
+      "sourceRoot": "src",
+      "prefix": "tp",
+      "architect": {
+        "build": {
+          "builder": "@angular-builders/custom-webpack:browser",
+          "options": {
+            "allowedCommonJsDependencies": [
+              "chart.js",
+              "node-forge"
+            ],
+            "customWebpackConfig": {
+              "path": "src/compress-webpack.config.js"
+            },
+            "optimization": false,
+            "sourceMap": true,
+            "namedChunks": false,
+            "extractLicenses": false,
+            "vendorChunk": true,
+            "buildOptimizer": false,
+            "outputPath": "dist/traffic-portal/browser",
+            "index": "src/index.html",
+            "main": "src/main.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.app.json",
+            "aot": true,
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "src/styles.scss",
+              {
+                "inject": false,
+                "input": "src/styles/themes/dark-default.scss",
+                "bundleName": "dark-default-theme"
+              }
+            ],
+            "scripts": []
+          },
+          "configurations": {
+            "production": {
+              "fileReplacements": [
+                {
+                  "replace": "src/environments/environment.ts",
+                  "with": "src/environments/environment.prod.ts"
+                }
+              ],
+              "optimization": true,
+              "outputHashing": "all",
+              "sourceMap": false,
+              "namedChunks": false,
+              "extractLicenses": true,
+              "vendorChunk": false,
+              "buildOptimizer": true,
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "4mb",
+                  "maximumError": "5mb"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "6kb",
+                  "maximumError": "10kb"
+                }
+              ]
+            }
+          }
+        },
+        "serve": {
+          "builder": "@angular-devkit/build-angular:dev-server",
+          "options": {
+            "browserTarget": "traffic-portal:build",
+            "proxyConfig": "proxy.json"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "traffic-portal:build:production"
+            }
+          }
+        },
+        "extract-i18n": {
+          "builder": "@angular-devkit/build-angular:extract-i18n",
+          "options": {
+            "browserTarget": "traffic-portal:build"
+          }
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "main": "src/test.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.spec.json",
+            "karmaConfig": "karma.conf.js",
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "src/styles.scss"
+            ],
+            "scripts": []
+          }
+        },
+        "lint": {
+          "builder": "@angular-eslint/builder:lint",
+          "options": {
+            "lintFilePatterns": [
+              "src/**/*.ts",
+              "src/**/*.html",
+              "cypress/**/*.ts"
+            ]
+          }
+        },
+        "server": {
+          "builder": "@angular-devkit/build-angular:server",
+          "options": {
+            "outputPath": "dist/traffic-portal/server",
+            "main": "server.ts",
+            "tsConfig": "tsconfig.server.json",
+            "sourceMap": true,
+            "buildOptimizer": false,
+            "optimization": false
+          },
+          "configurations": {
+            "production": {
+              "outputHashing": "media",
+              "fileReplacements": [
+                {
+                  "replace": "src/environments/environment.ts",
+                  "with": "src/environments/environment.prod.ts"
+                }
+              ],
+              "sourceMap": false,
+              "optimization": true,
+              "buildOptimizer": true
+            }
+          }
+        },
+        "serve-ssr": {
+          "builder": "@nguniversal/builders:ssr-dev-server",
+          "options": {
+            "browserTarget": "traffic-portal:build",
+            "serverTarget": "traffic-portal:server"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "traffic-portal:build:production",
+              "serverTarget": "traffic-portal:server:production"
+            }
+          }
+        },
+        "prerender": {
+          "builder": "@nguniversal/builders:prerender",
+          "options": {
+            "browserTarget": "traffic-portal:build:production",
+            "serverTarget": "traffic-portal:server:production",
+            "routes": [
+              "/"
+            ]
+          },
+          "configurations": {
+            "production": {}
+          }
+        },
+        "e2e": {
+          "builder": "@cypress/schematic:cypress",
+          "options": {
+            "devServerTarget": "traffic-portal:serve",
+            "watch": true,
+            "headless": false
+          },
+          "configurations": {
+            "production": {
+              "devServerTarget": "traffic-portal:serve:production"
+            }
+          }
+        },
+        "cypress-run": {
+          "builder": "@cypress/schematic:cypress",
+          "options": {
+            "devServerTarget": "traffic-portal:serve"
+          },
+          "configurations": {
+            "production": {
+              "devServerTarget": "traffic-portal:serve:production"
+            }
+          }
+        },
+        "cypress-open": {
+          "builder": "@cypress/schematic:cypress",
+          "options": {
+            "watch": true,
+            "headless": false
+          }
+        },
+        "ct": {
+          "builder": "@cypress/schematic:cypress",
+          "options": {
+            "devServerTarget": "traffic-portal:serve",
+            "watch": true,
+            "headless": false,
+            "testingType": "component"
+          },
+          "configurations": {
+            "development": {
+              "devServerTarget": "traffic-portal:serve:development"
+            }
+          }
+        }
+      }
+    }
+  },
+  "schematics": {
+    "@angular-eslint/schematics:application": {
+      "setParserOptionsProject": true
+    },
+    "@angular-eslint/schematics:library": {
+      "setParserOptionsProject": true
+    }
+  }
 }
diff --git a/experimental/traffic-portal/cypress.config.ts b/experimental/traffic-portal/cypress.config.ts
new file mode 100644
index 0000000000..98182a620e
--- /dev/null
+++ b/experimental/traffic-portal/cypress.config.ts
@@ -0,0 +1,403 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { promises as fs } from "fs";
+import * as https from "https";
+
+import axios, { type AxiosError } from "axios";
+import { defineConfig } from "cypress";
+import type {
+	CDN,
+	LoginRequest,
+	ProfileType,
+	RequestASN,
+	RequestCacheGroup,
+	RequestCoordinate,
+	RequestDeliveryService,
+	RequestDivision,
+	RequestParameter,
+	RequestPhysicalLocation,
+	RequestProfile,
+	RequestRegion,
+	RequestRole,
+	RequestServer,
+	RequestServerCapability,
+	RequestStatus,
+	RequestSteeringTarget,
+	RequestTenant,
+	RequestType,
+	ResponseCacheGroup,
+	ResponseDeliveryService,
+	ResponseDivision,
+	ResponseParameter,
+	ResponsePhysicalLocation,
+	ResponseProfile,
+	ResponseRegion,
+	ResponseStatus,
+	TypeFromResponse
+} from "trafficops-types";
+
+import type { CreatedData } from "./cypress/support/testing.data";
+
+/**
+ * Creates mock data needed for E2E testing.
+ *
+ * Ideally this functionality would go in a different file, but for some reason
+ * Cypress gets very upset if you import anything but a type from any TypeScript
+ * file in this config file.
+ *
+ * @param toURL The URL of a Traffic Ops instance e.g. 'https://traffic.ops/'.
+ * @param apiVersion The version of the API to use e.g. '4.1'.
+ * @param adminUser The username of the 'admin' Role user that will be used
+ * to set up testing data.
+ * @param adminPass The password of the 'admin' Role user that will be used to
+ * set up testing data.
+ * @returns The data that was created and the unique string that was appended to
+ * all names that are required to be unique.
+ */
+async function createData(toURL: string, apiVersion: string, adminUser: string, adminPass: string): Promise<CreatedData> {
+	const apiUrl = `${toURL}/api/${apiVersion}`;
+	const client = axios.create({
+		httpsAgent: new https.Agent({
+			rejectUnauthorized: false
+		})
+	});
+
+	if (!Object.keys(client.defaults.headers.common).includes("Cookie")) {
+		let accessToken = "";
+		const loginReq: LoginRequest = {
+			p: adminPass,
+			u: adminUser
+		};
+		try {
+			const logResp = await client.post(`${apiUrl}/user/login`, JSON.stringify(loginReq));
+			if (logResp.headers["set-cookie"]) {
+				for (const cookie of logResp.headers["set-cookie"]) {
+					if (cookie.includes("access_token")) {
+						accessToken = cookie;
+						break;
+					}
+				}
+			}
+		} catch (e) {
+			// eslint-disable-next-line no-console
+			console.error((e as AxiosError).message);
+			throw e;
+		}
+		if (accessToken === "") {
+			const e = new Error("Access token is not set");
+			// eslint-disable-next-line no-console
+			console.error(e.message);
+			throw e;
+		}
+		// eslint-disable-next-line @typescript-eslint/naming-convention
+		client.defaults.headers.common = {Cookie: accessToken};
+	}
+
+	let resp = await client.get(`${apiUrl}/types`);
+	const types: Array<TypeFromResponse> = resp.data.response;
+	const httpType = types.find(typ => typ.name === "HTTP" && typ.useInTable === "deliveryservice");
+	if (!httpType) {
+		throw new Error("Unable to find `HTTP` type");
+	}
+	const steeringType = types.find(typ => typ.name === "STEERING" && typ.useInTable === "deliveryservice");
+	if (!steeringType) {
+		throw new Error("Unable to find `STEERING` type");
+	}
+	const steeringWeightType = types.find(typ => typ.name === "STEERING_WEIGHT" && typ.useInTable === "steering_target");
+	if (!steeringWeightType) {
+		throw new Error("Unable to find `STEERING_WEIGHT` type");
+	}
+	const cgType = types.find(typ => typ.useInTable === "cachegroup");
+	if (!cgType) {
+		throw new Error("Unable to find any Cache Group Types");
+	}
+	const edgeType = types.find(typ => typ.useInTable === "server" && typ.name === "EDGE");
+	if (!edgeType) {
+		throw new Error("Unable to find `EDGE` type");
+	}
+
+	const id = (new Date()).getTime().toString();
+	const data = {
+		uniqueString: id
+	} as CreatedData;
+
+	let url = `${apiUrl}/cdns`;
+	try {
+		const cdn: CDN = {
+			dnssecEnabled: false, domainName: `tests${id}.com`, name: `testCDN${id}`
+		};
+
+		resp = await client.post(url, JSON.stringify(cdn));
+		const respCDN = resp.data.response;
+		data.cdn = respCDN;
+
+		const ds: RequestDeliveryService = {
+			active: false,
+			cacheurl: null,
+			cdnId: respCDN.id,
+			displayName: `test DS${id}`,
+			dscp: 0,
+			ecsEnabled: false,
+			edgeHeaderRewrite: null,
+			fqPacingRate: null,
+			geoLimit: 0,
+			geoProvider: 0,
+			httpBypassFqdn: null,
+			infoUrl: null,
+			initialDispersion: 1,
+			ipv6RoutingEnabled: false,
+			logsEnabled: false,
+			maxOriginConnections: 0,
+			maxRequestHeaderBytes: 0,
+			midHeaderRewrite: null,
+			missLat: 0,
+			missLong: 0,
+			multiSiteOrigin: false,
+			orgServerFqdn: "http://test.com",
+			profileId: 1,
+			protocol: 0,
+			qstringIgnore: 0,
+			rangeRequestHandling: 0,
+			regionalGeoBlocking: false,
+			remapText: null,
+			routingName: "test",
+			signed: false,
+			tenantId: 1,
+			typeId: httpType.id,
+			xmlId: `testDS${id}`
+		};
+		url = `${apiUrl}/deliveryservices`;
+		resp = await client.post(url, JSON.stringify(ds));
+		let respDS: ResponseDeliveryService = resp.data.response[0];
+		data.ds = respDS;
+
+		ds.displayName = `test DS2${id}`;
+		ds.xmlId = `testDS2${id}`;
+		resp = await client.post(url, JSON.stringify(ds));
+		respDS = resp.data.response[0];
+		data.ds2 = respDS;
+
+		ds.displayName = `test steering DS${id}`;
+		ds.xmlId = `testSDS${id}`;
+		ds.typeId = steeringType.id;
+		resp = await client.post(url, JSON.stringify(ds));
+		respDS = resp.data.response[0];
+		data.steeringDS = respDS;
+
+		const target: RequestSteeringTarget = {
+			targetId: data.ds.id,
+			typeId: steeringWeightType.id,
+			value: 1
+		};
+		url = `${apiUrl}/steering/${data.steeringDS.id}/targets`;
+		await client.post(url, JSON.stringify(target));
+		target.targetId = data.ds2.id;
+		await client.post(url, JSON.stringify(target));
+
+		const tenant: RequestTenant = {
+			active: true,
+			name: `testT${id}`,
+			parentId: 1
+		};
+		url = `${apiUrl}/tenants`;
+		resp = await client.post(url, JSON.stringify(tenant));
+		data.tenant = resp.data.response;
+
+		const division: RequestDivision = {
+			name: `testD${id}`
+		};
+		url = `${apiUrl}/divisions`;
+		resp = await client.post(url, JSON.stringify(division));
+		const respDivision: ResponseDivision = resp.data.response;
+		data.division = respDivision;
+
+		const region: RequestRegion = {
+			division: respDivision.id,
+			name: `testR${id}`
+		};
+		url = `${apiUrl}/regions`;
+		resp = await client.post(url, JSON.stringify(region));
+		const respRegion: ResponseRegion = resp.data.response;
+		data.region = respRegion;
+
+		const cacheGroup: RequestCacheGroup = {
+			name: `test${id}`,
+			shortName: `test${id}`,
+			typeId: cgType.id
+		};
+		url = `${apiUrl}/cachegroups`;
+		resp = await client.post(url, JSON.stringify(cacheGroup));
+		const responseCG: ResponseCacheGroup = resp.data.response;
+		data.cacheGroup = responseCG;
+
+		const asn: RequestASN = {
+			asn: +id,
+			cachegroupId: responseCG.id
+		};
+		url = `${apiUrl}/asns`;
+		resp = await client.post(url, JSON.stringify(asn));
+		data.asn = resp.data.response;
+
+		const physLoc: RequestPhysicalLocation = {
+			address: "street",
+			city: "city",
+			comments: "someone set us up the bomb",
+			email: "email@test.com",
+			name: `phys${id}`,
+			phone: "111-867-5309",
+			poc: "me",
+			regionId: respRegion.id,
+			shortName: `short${id}`,
+			state: "CA",
+			zip: "80000"
+		};
+		url = `${apiUrl}/phys_locations`;
+		resp = await client.post(url, JSON.stringify(physLoc));
+		const respPhysLoc: ResponsePhysicalLocation = resp.data.response;
+		respPhysLoc.region = respRegion.name;
+		data.physLoc = respPhysLoc;
+
+		const coordinate: RequestCoordinate = {
+			latitude: 0,
+			longitude: 0,
+			name: `coord${id}`
+		};
+		url = `${apiUrl}/coordinates`;
+		resp = await client.post(url, JSON.stringify(coordinate));
+		data.coordinate = resp.data.response;
+
+		const type: RequestType = {
+			description: "blah",
+			name: `type${id}`,
+			useInTable: "server"
+		};
+		url = `${apiUrl}/types`;
+		resp = await client.post(url, JSON.stringify(type));
+
+		data.type = resp.data.response;
+		const status: RequestStatus = {
+			description: "blah",
+			name: `status${id}`,
+		};
+		url = `${apiUrl}/statuses`;
+		resp = await client.post(url, JSON.stringify(status));
+		const respStatus: ResponseStatus = resp.data.response;
+		data.status = respStatus;
+
+		const profile: RequestProfile = {
+			cdn: respCDN.id,
+			description: "blah",
+			name: `profile${id}`,
+			routingDisabled: false,
+			type: "ATS_PROFILE" as ProfileType,
+		};
+		url = `${apiUrl}/profiles`;
+		resp = await client.post(url, JSON.stringify(profile));
+		const respProfile: ResponseProfile = resp.data.response;
+		data.profile = respProfile;
+
+		const parameter: RequestParameter = {
+			configFile: "cfg.txt",
+			name: `param${id}`,
+			secure: false,
+			value: "10",
+		};
+		url = `${apiUrl}/parameters`;
+		resp = await client.post(url, JSON.stringify(parameter));
+		const responseParameter: ResponseParameter = resp.data.response;
+		data.parameter = responseParameter;
+
+		const server: RequestServer = {
+			cachegroupId: responseCG.id,
+			cdnId: respCDN.id,
+			domainName: "domain.com",
+			hostName: id,
+			interfaces: [{
+				ipAddresses: [{
+					address: "192.160.1.0",
+					gateway: null,
+					serviceAddress: true
+				}],
+				maxBandwidth: 0,
+				monitor: true,
+				mtu: 1500,
+				name: "eth0"
+			}],
+			physLocationId: respPhysLoc.id,
+			profileNames: [respProfile.name],
+			statusId: respStatus.id,
+			typeId: edgeType.id
+
+		};
+		url = `${apiUrl}/servers`;
+		resp = await client.post(url, JSON.stringify(server));
+		data.edgeServer = resp.data.response;
+
+		const capability: RequestServerCapability = {
+			name: `test${id}`
+		};
+		url = `${apiUrl}/server_capabilities`;
+		resp = await client.post(url, JSON.stringify(capability));
+		data.capability = resp.data.response;
+
+		const role: RequestRole = {
+			description: "Has access to everything - cannot be modified or deleted",
+			name: `admin${id}`,
+			permissions: [
+				"ALL"
+			]
+		};
+		url = `${apiUrl}/roles`;
+		resp = await client.post(url, JSON.stringify(role));
+		data.role = resp.data.response;
+	} catch (e) {
+		const ae = e as AxiosError;
+		ae.message = `Request (${ae.config.method}) failed to ${url}`;
+		ae.message += ae.response ? ` with response code ${ae.response.status}` : " with no response";
+		throw ae;
+	}
+
+	return data;
+}
+
+/** The schema for the Traffic Ops connection configuration file. */
+interface TOConfig {
+	adminPass: string;
+	adminUser: string;
+	apiVersion: string;
+	toURL: string;
+}
+
+export default defineConfig({
+	component: {
+		devServer: {
+			bundler: "webpack",
+			framework: "angular",
+		},
+		specPattern: "**/*.cy.ts"
+	},
+	e2e: {
+		baseUrl: "http://localhost:4200",
+		setupNodeEvents(on) {
+			on("before:run", async () => {
+				const toConfig: TOConfig = JSON.parse(await fs.readFile("cypress/fixtures/to.config.json", {encoding: "utf-8"}));
+				const data = await createData(toConfig.toURL, toConfig.apiVersion, toConfig.adminUser, toConfig.adminPass);
+				let formattedData = JSON.stringify(data, null, "\t");
+				formattedData += "\n";
+				return fs.writeFile("cypress/fixtures/test.data.json", formattedData);
+			});
+		},
+	},
+	experimentalInteractiveRunEvents: true,
+});
diff --git a/experimental/traffic-portal/cypress/.gitignore b/experimental/traffic-portal/cypress/.gitignore
new file mode 100644
index 0000000000..b215ff1426
--- /dev/null
+++ b/experimental/traffic-portal/cypress/.gitignore
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+fixtures/test.data.json
+screenshots/
diff --git a/experimental/traffic-portal/cypress/README.md b/experimental/traffic-portal/cypress/README.md
new file mode 100644
index 0000000000..1310b028a3
--- /dev/null
+++ b/experimental/traffic-portal/cypress/README.md
@@ -0,0 +1,35 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Running
+From the `traffic-portal` directory, all you have to do is run `ng e2e`.
+`npm run e2e` is functionally equivalent to `ng e2e`. This will run the tests in
+interactive mode, allowing you to specify the test you want to run as well as
+the browser in which to run it. In this mode, the tests will recompile and
+re-run as they are edited in the source file. To run all of the tests once and
+then exit - headlessly - use `ng run traffic-portal:cypress-run` or equivalently
+`npm run e2e:ci`.
+
+## Configuration
+This suite assumes that you have a working Traffic Ops instance. The file
+`cypress/fixtures/to.config.json` defines how the tests will connect to this
+instance, including  the admin user/password needed for testing as well as the
+TO API URL. The admin user defined in this file will be used for data setup, but
+the user as whom the tests will run is given in `cypress/fixtures/login.json`.
+
+## Testing Data
+On each run, some mock data is set up (and **not** cleaned up afterwards) by the
+testing suite and written to a fixtures data file at
+`cypress/fixtures/test.data.json`. Modifying this file after the tests have
+started running will not affect them in any way.
diff --git a/experimental/traffic-portal/cypress/e2e/cacheGroups/asns/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/asns/detail.cy.ts
new file mode 100644
index 0000000000..2ad8f47cd2
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/asns/detail.cy.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("ASN edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing ASN", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {asn} = data;
+				cy.visit(`/core/asns/${asn.id}`);
+				cy.get("mat-card").find("input[name=asn]").should("be.enabled").should("have.value", asn.asn);
+				cy.get("mat-card").find("mat-select[name=cachegroup]");
+				cy.get("mat-card").find("input[name=id]").should("not.be.enabled").should("have.value", asn.id);
+				cy.get("mat-card").find("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new ASNs", () => {
+		cy.visit("/core/asns/new");
+		cy.get("mat-card").find("input[name=asn]").should("be.enabled");
+		cy.get("mat-card").find("mat-select[name=cachegroup]");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/asns/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/cacheGroups/asns/table.cy.ts
index 531eaabb43..54e1fdf210 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/asns/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("ASN table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/asns");
+		cy.find("input[name=fuzzControl]");
+		cy.find("div.ag-row").should("have.length.at.least", 1);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/detail.cy.ts
new file mode 100644
index 0000000000..0d547e4273
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/detail.cy.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { CreatedData } from "cypress/support/testing.data";
+
+describe("Coordinate edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Coordinate", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const coordinate = data.coordinate;
+				cy.visit(`/core/coordinates/${coordinate.id}`);
+				cy.get("mat-card").find("input[name=latitude]").should("be.enabled").should("have.value", coordinate.latitude);
+				cy.get("mat-card").find("input[name=longitude]").should("be.enabled").should("have.value", coordinate.longitude);
+				cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", coordinate.name);
+				cy.get("mat-card").find("input[name=id]").should("not.be.enabled").should("have.value", coordinate.id);
+				cy.get("mat-card").find("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Coordinate", () => {
+		cy.visit("/core/coordinates/new");
+		cy.get("mat-card").find("input[name=latitude]").should("be.enabled");
+		cy.get("mat-card").find("input[name=longitude]").should("be.enabled");
+		cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", "");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/tenant/table.spec.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/table.cy.ts
similarity index 62%
rename from experimental/traffic-portal/nightwatch/tests/users/tenant/table.spec.ts
rename to experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/table.cy.ts
index 92efecaf61..7c6ae62abf 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/tenant/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/coordinates/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Tenants Spec", () => {
+describe("Coordinates table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("tenants", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
+		cy.visit("/core/coordinates");
+		cy.find("input[name=fuzzControl]");
+		cy.find("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/cacheGroups/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/detail.cy.ts
new file mode 100644
index 0000000000..14733e7210
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/detail.cy.ts
@@ -0,0 +1,42 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Cache Group edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Cache Group", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const cg = data.cacheGroup;
+				cy.visit(`/core/cache-groups/${cg.id}`);
+				cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", cg.name);
+				cy.get("mat-card").find("input[name=id]").should("not.be.enabled").should("have.value", String(cg.id));
+				cy.get("mat-card").find("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+
+			}
+		);
+	});
+
+	it("Creates a new Cache Group", () => {
+		cy.visit("/core/cache-groups/new");
+		cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", "");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/detail.cy.ts
new file mode 100644
index 0000000000..fa390638e3
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/detail.cy.ts
@@ -0,0 +1,41 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { CreatedData } from "cypress/support/testing.data";
+
+describe("Division edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Division", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const div = data.division;
+				cy.visit(`/core/divisions/${div.id}`);
+				cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", div.name);
+				cy.get("mat-card").find("input[name=id]").should("not.be.enabled").should("have.value", div.id);
+				cy.get("mat-card").find("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Divisions", () => {
+		cy.visit("/core/divisions/new");
+		cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", "");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/types/table.spec.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/table.cy.ts
similarity index 62%
rename from experimental/traffic-portal/nightwatch/tests/types/table.spec.ts
rename to experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/table.cy.ts
index 1237559d19..39f57dc385 100644
--- a/experimental/traffic-portal/nightwatch/tests/types/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/divisions/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Types Spec", () => {
+describe("Divisions table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("types", ["configurationContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
+		cy.visit("/core/divisions");
+		cy.find("input[name=fuzzControl]").should("exist");
+		cy.find("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/cacheGroups/regions/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/regions/detail.cy.ts
new file mode 100644
index 0000000000..ed57988791
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/regions/detail.cy.ts
@@ -0,0 +1,41 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { CreatedData } from "cypress/support/testing.data";
+
+describe("Region edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Region", () => {
+		cy.fixture("test.data").then((data: CreatedData) => {
+			const {region} = data;
+			cy.visit(`/core/regions/${region.id}`);
+			cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", region.name);
+			cy.get("mat-card").find("mat-select[name=division]");
+			cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+			cy.get("mat-card").find("input[name=lastUpdated]").should("not.be.enabled");
+			cy.get("mat-card").find("input[name=id]").should("not.be.enabled").should("have.value", region.id);
+		});
+	});
+
+	it("Creates new Regions", () => {
+		cy.visit("/core/regions/new");
+		cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", "");
+		cy.get("mat-card").find("mat-select[name=division]");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/regions/table.cy.ts
similarity index 62%
rename from experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts
rename to experimental/traffic-portal/cypress/e2e/cacheGroups/regions/table.cy.ts
index a02d46be71..e3c4298dc8 100644
--- a/experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/regions/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Statuses Spec", () => {
+describe("Regions table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("statuses", ["serversContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
+		cy.visit("regions");
+		cy.find("input[name=fuzzControl]");
+		cy.find("div.ag-row").should("have.length.at.least", 3);
 	});
 });
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/cacheGroups/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/cacheGroups/table.cy.ts
index 531eaabb43..ce627a40d6 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/cacheGroups/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Cache Groups table", () => {
+	before(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/cache-groups");
+		cy.get("body").find("input[name=fuzzControl]");
+		cy.find("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/cdns/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/cdns/detail.cy.ts
new file mode 100644
index 0000000000..3f2dfcb7c2
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/cdns/detail.cy.ts
@@ -0,0 +1,41 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("CDN edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing CDN", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {cdn} = data;
+				cy.visit(`/core/cdns/${cdn.id}`);
+				cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", cdn.name);
+				cy.get("mat-card").find("input[name=id]").should("be.disabled").should("have.value", cdn.id);
+				cy.get("mat-card").find("input[name=lastUpdated]").should("be.disabled");
+				cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new CDNs", () => {
+		cy.visit("/core/cdns/new");
+		cy.get("mat-card").find("input[name=name]").should("be.enabled").should("have.value", "");
+		cy.get("mat-card").find("input[name=id]").should("not.exist");
+		cy.get("mat-card").find("input[name=lastUpdated]").should("not.exist");
+		cy.get("mat-card").find("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.card.cy.ts b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.card.cy.ts
new file mode 100644
index 0000000000..6a1b4dc5ec
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.card.cy.ts
@@ -0,0 +1,42 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+import type { ResponseDeliveryService } from "trafficops-types";
+
+describe("Delivery Service info card", () => {
+	let ds: ResponseDeliveryService;
+	beforeEach(() => {
+		cy.login();
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				({ds} = data);
+			}
+		);
+	});
+	it("Expands and collapses", () => {
+		cy.visit("/core");
+		const selector = `#${ds.xmlId}`;
+		cy.get(selector).find("mat-card-content > div").should("not.exist");
+		cy.get(selector).click().find("mat-card-content > div").should("exist");
+	});
+
+	it("Has a button that takes you to a details view", async (): Promise<void> => {
+		cy.visit("/core");
+		const selector = `#${ds.xmlId}`;
+		cy.get(selector).click();
+		cy.get(selector).find("a").contains("View Details").click();
+		cy.window().its("location.pathname").should("eq", `/core/deliveryservice/${ds.id}`);
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.details.cy.ts b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.details.cy.ts
new file mode 100644
index 0000000000..cf1befa2f4
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.details.cy.ts
@@ -0,0 +1,58 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Delivery Service details page", () => {
+	let testData: CreatedData;
+	before(() => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				testData = data;
+			}
+		);
+	});
+	beforeEach(() => {
+		cy.login();
+		cy.visit(`/core/deliveryservice/${testData.ds.id}`);
+	});
+
+	it("Contains all the correct charts and controls", (): void => {
+		cy.get("#bandwidthData").should("be.visible");
+		cy.get("#tpsChartData").should("be.visible");
+		cy.get("#invalidate").should("be.visible");
+		cy.get("input[name=fromdate]").should("be.enabled");
+		cy.get("input[name=fromtime]").should("be.enabled");
+		cy.get("input[name=todate]").should("be.enabled");
+		cy.get("input[name=totime]").should("be.enabled");
+		cy.get("button[name=timespanRefresh]").should("not.be.disabled");
+	});
+
+	it("Sets default properties appropriately", (): void => {
+		const now = new Date();
+		const date = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2, "0")}-${now.getDate().toString().padStart(2, "0")}`;
+		const time = `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}`;
+
+		cy.get("input[name=fromdate]").should("have.value", date);
+		cy.get("input[name=fromtime]").should("have.value", "00:00");
+		cy.get("input[name=todate]").should("have.value", date);
+		cy.get("input[name=totime]").should("have.value", time);
+	});
+
+	it("Displays the steering action based on the DS type", (): void => {
+		cy.get("div.actions > mat-icon").should("be.visible");
+		cy.visit(`/core/deliveryservice/${testData.steeringDS.id}`);
+		cy.get("div.actions > mat-icon").should("not.exist");
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.invalidate.cy.ts b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.invalidate.cy.ts
new file mode 100644
index 0000000000..401b1f53dc
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/deliveryServices/ds.invalidate.cy.ts
@@ -0,0 +1,58 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("DS Content Invalidation Jobs page", () => {
+	let id: number;
+	before(() => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				({id} = data.ds);
+			}
+		);
+	});
+	beforeEach(() => {
+		cy.login();
+		cy.visit(`/core/deliveryservice/${id}/invalidation-jobs`);
+	});
+
+	it("Manage Job", () => {
+		cy.get("tp-header").find("h1").should("contain.text", "Content Invalidation Jobs").and("not.contain.text", "Loading");
+		cy.get("button#new").focus().trigger("click");
+
+		const startDate = new Date();
+		startDate.setDate(startDate.getDate() + 1);
+		cy.get("input[name='startDate']").should("be.visible").and("have.value", startDate.toLocaleDateString());
+		cy.get("input[name='regexp']").click().focus().type("invalidateMe");
+		cy.get("button[type=submit]").click();
+		cy.get("simple-snack-bar").contains("created");
+		cy.get("simple-snack-bar").find("button").first().click();
+
+		cy.get("div.invalidation-job").find("button").should("be.visible").should("have.length", 2).should("not.be.disabled");
+		cy.get("div.invalidation-job button").first().click();
+		cy.get("tp-new-invalidation-job-dialog")
+			.find("input[name='startDate']")
+			.should("be.visible")
+			.and("have.value", startDate.toLocaleDateString());
+		cy.get("input[name='regexp']").should("have.value", "invalidateMe").click().focus().clear().type("/invalidateMe2");
+		cy.get("button[type=submit]").click();
+
+		cy.get("simple-snack-bar").contains("created");
+		cy.get("simple-snack-bar").find("button").first().click();
+
+		cy.get("div.invalidation-job button").last().click();
+		cy.get("simple-snack-bar").contains("was deleted");
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/login.cy.ts b/experimental/traffic-portal/cypress/e2e/login.cy.ts
new file mode 100644
index 0000000000..4f59f15480
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/login.cy.ts
@@ -0,0 +1,44 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+describe("Login Page", () => {
+	beforeEach(() => {
+		cy.visit("/login");
+	});
+	it("Clears the form when the 'Clear' button is clicked", () => {
+		const usernameInput = cy.get("input").first();
+		usernameInput.type("test");
+
+		const passwordInput = cy.get("input").last();
+		passwordInput.type("asdf");
+
+		cy.contains("button", "Clear").click();
+
+		usernameInput.should("have.value", "");
+		passwordInput.should("have.value", "");
+	});
+
+	it("Rejects incorrect passwords", () => {
+		cy.get("input").first().type("test");
+		cy.get("input").last().type("asdf");
+
+		cy.contains("button", "Login").click();
+
+		cy.contains("Invalid username or password");
+	});
+
+	it("Logs in", () => {
+		cy.login();
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/parameters/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/parameters/detail.cy.ts
new file mode 100644
index 0000000000..19f5092b64
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/parameters/detail.cy.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Parameter edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Test parameter", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const param = data.parameter;
+				cy.visit(`/core/parameters/${param.id}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", param.name);
+				cy.get("input[name=configFile]").should("be.enabled").and("have.value", param.configFile);
+				cy.get("textarea[name=value]").should("be.enabled").and("have.value", param.value);
+				cy.get("input[name=id]").should("not.be.enabled").and("have.value", param.id);
+				cy.get("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Parameters", () => {
+		cy.visit("/core/parameters/new");
+		cy.get("input[name=name]").should("be.enabled").and("have.value", "");
+		cy.get("input[name=configFile]").should("be.enabled").and("have.value", "");
+		cy.get("textarea[name=value]").should("be.enabled").and("have.value", "");
+		cy.get("input[name=id]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/parameters/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/parameters/table.cy.ts
index 531eaabb43..8cb76ec366 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/parameters/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Parameters table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/parameters");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/profiles/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/profiles/detail.cy.ts
new file mode 100644
index 0000000000..f5715d9477
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/profiles/detail.cy.ts
@@ -0,0 +1,49 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Profile creation/edit page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Profile", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {profile} = data;
+				cy.visit(`/core/profiles/${profile.id}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", profile.name);
+				cy.get("mat-select[name=cdn]").should("not.be.disabled");
+				cy.get("mat-select[name=type]").should("not.be.disabled");
+				cy.get("mat-select[name=routingDisabled]").should("not.be.disabled");
+				cy.get("textarea[name=description]").should("be.enabled").and("have.value", profile.description);
+				cy.get("input[name=id]").should("not.be.enabled").and("have.value", profile.id);
+				cy.get("input[name=lastUpdated]").should("not.be.enabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Profiles", () => {
+		cy.visit("/core/profiles/new");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("mat-select[name=cdn]").should("not.be.disabled");
+		cy.get("mat-select[name=type]").should("not.be.disabled");
+		cy.get("mat-select[name=routingDisabled]").should("not.be.disabled");
+		cy.get("textarea[name=description]").should("be.enabled").and("be.empty");
+		cy.get("input[name=id]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/profiles/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/profiles/table.cy.ts
index 531eaabb43..151e3612ae 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/profiles/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Profiles table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/profiles");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 1);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/servers/capabilities/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/servers/capabilities/detail.cy.ts
new file mode 100644
index 0000000000..8cb57d3b41
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/servers/capabilities/detail.cy.ts
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Server Capability edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Capability", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {capability} = data;
+				cy.visit(`/core/capabilities/${capability.name}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", capability.name);
+				cy.get("input[name=lastUpdated]").should("be.disabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Capabilities", () => {
+		cy.visit("/core/new-capability");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("button").contains("Save").should("not.be.disabled");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/servers/capabilities/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/servers/capabilities/table.cy.ts
index 531eaabb43..ae3f4d8273 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/servers/capabilities/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Capabilities table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/capabilities");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/servers/physical.location/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/servers/physical.location/detail.cy.ts
new file mode 100644
index 0000000000..5fa6a5a7ba
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/servers/physical.location/detail.cy.ts
@@ -0,0 +1,51 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Physical Location creation/edit page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Physical Location", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {physLoc} = data;
+				cy.visit(`/core/phys-locs/${physLoc.id}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", physLoc.name);
+				cy.get("input[name=id]").should("be.disabled").and("have.value", physLoc.id);
+				cy.get("input[name=lastUpdated]").should("be.disabled");
+				cy.get("input[name=address]").should("be.enabled").and("have.value", physLoc.address);
+				cy.get("input[name=city]").should("be.enabled").and("have.value", physLoc.city);
+				cy.get("textarea[name=comments]").should("be.enabled").and("have.value", physLoc.comments ?? "");
+				cy.get("input[name=email]").should("be.enabled").and("have.value", physLoc.email ?? "");
+				cy.get("input[name=poc]").should("be.enabled").and("have.value", physLoc.poc ?? "");
+				cy.get("input[name=shortName]").should("be.enabled").and("have.value", physLoc.shortName);
+				cy.get("input[name=state]").should("be.enabled").and("have.value", physLoc.state);
+				cy.get("input[name=postalCode]").should("be.enabled").and("have.value", physLoc.zip);
+				cy.get("input[name=phone]").should("be.enabled").and("have.value", physLoc.phone ?? "");
+				cy.get("mat-select[name=region]").should("contain.text", physLoc.region ?? "");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Physical Locations", () => {
+		cy.visit("/core/phys-locs/new");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("input[name=id]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/servers/physical.location/table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/servers/physical.location/table.cy.ts
index 531eaabb43..ef49fcd05f 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/servers/physical.location/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Physical Locations table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/phys-locs");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/servers/servers.detail.cy.ts b/experimental/traffic-portal/cypress/e2e/servers/servers.detail.cy.ts
new file mode 100644
index 0000000000..15f0b48d81
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/servers/servers.detail.cy.ts
@@ -0,0 +1,86 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Servers edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Loads a new server form correctly", () => {
+		cy.visit("core/servers/new");
+		cy.get("input[name=hostname]").should("be.enabled").and("have.value", "");
+		cy.get("mat-select[name=cdn]").should("exist");
+		cy.get("mat-select[name=cachegroup]").should("exist");
+		cy.get("mat-select[name=physLocation]").should("exist");
+		cy.get("mat-select[name=status]").should("exist");
+		cy.get("input[name=offlineReason]").should("not.exist");
+		cy.get("mat-select[name=type]").should("exist");
+		cy.get("input[name=httpport]").should("be.enabled");
+		cy.get("input[name=httpsport]").should("be.enabled");
+		cy.get("input[name=rack]").should("be.enabled");
+		cy.get("input[name=serverId]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("input[name=statusLastUpdated]").should("not.exist");
+		cy.get("mat-select[name=profiles]").should("exist");
+		cy.get("button[aria-label='Add An Interface']").should("not.be.disabled");
+		cy.get("input[name=iloIP]").should("be.enabled");
+		cy.get("input[name=iloGateway]").should("be.enabled");
+		cy.get("input[name=iloNetmask]").should("be.enabled");
+		cy.get("input[name=iloUsername]").should("be.enabled");
+		cy.get("input[name=iloPassword]").should("be.enabled");
+		cy.get("input[name=mgmtIP]").should("be.enabled");
+		cy.get("input[name=mgmtIpGateway]").should("be.enabled");
+		cy.get("input[name=mgmtIpNetmask]").should("be.enabled");
+		cy.get("button[aria-label='Delete Server']").should("not.exist");
+		cy.get("button[aria-label='Submit Server']").should("not.be.disabled");
+	});
+
+	it("Loads an existing server's edit form correctly", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {edgeServer} = data;
+				cy.visit(`/core/servers/${edgeServer.id}`);
+				cy.get("input[name=hostname]").should("be.enabled").and("have.value", edgeServer.hostName);
+				cy.get("mat-select[name=cdn]").should("contain.text", edgeServer.cdnName);
+				cy.get("mat-select[name=cachegroup]").should("contain.text", edgeServer.cachegroup);
+				cy.get("mat-select[name=physLocation]").should("contain.text", edgeServer.physLocation);
+				cy.get("input[name=status]").should("be.disabled").and("have.value", edgeServer.status);
+				cy.get("input[name=offlineReason]").should("not.exist");
+				cy.get("mat-select[name=type]").should("contain.text", edgeServer.type);
+				cy.get("input[name=httpport]").should("be.enabled").and("have.value", edgeServer.tcpPort ?? "");
+				cy.get("input[name=httpsport]").should("be.enabled").and("have.value", edgeServer.httpsPort ?? "");
+				cy.get("input[name=rack]").should("be.enabled").and("have.value", edgeServer.rack ?? "");
+				cy.get("input[name=serverId]").should("be.disabled").and("have.value", edgeServer.id);
+				cy.get("input[name=lastUpdated]").should("be.disabled");
+				// TODO: verified that this field doesn't show up until the
+				// server's status actually changed - so how were the Nightwatch
+				// tests passing?
+				// cy.get("input[name=statusLastUpdated]").should("be.disabled");
+				cy.get("mat-select[name=profiles]").should("contain.text", edgeServer.profileNames[0]);
+				cy.get("button[aria-label='Add An Interface']").should("not.be.disabled");
+				cy.get("input[name=iloIP]").should("be.enabled").and("have.value", edgeServer.iloIpAddress ?? "");
+				cy.get("input[name=iloGateway]").should("be.enabled").and("have.value", edgeServer.iloIpGateway ?? "" );
+				cy.get("input[name=iloNetmask]").should("be.enabled").and("have.value", edgeServer.iloIpNetmask ?? "");
+				cy.get("input[name=iloUsername]").should("be.enabled").and("have.value", edgeServer.iloUsername ?? "");
+				cy.get("input[name=iloPassword]").should("be.enabled").and("have.value", edgeServer.iloPassword ?? "");
+				cy.get("input[name=mgmtIP]").should("be.enabled").and("have.value", edgeServer.mgmtIpAddress ?? "");
+				cy.get("input[name=mgmtIpGateway]").should("be.enabled").and("have.value", edgeServer.mgmtIpGateway ?? "");
+				cy.get("input[name=mgmtIpNetmask]").should("be.enabled").and("have.value", edgeServer.mgmtIpNetmask ?? "");
+				cy.get("button[aria-label='Delete Server']").should("not.be.disabled");
+				cy.get("button[aria-label='Submit Server']").should("not.be.disabled");
+			}
+		);
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts b/experimental/traffic-portal/cypress/e2e/servers/servers.table.cy.ts
similarity index 62%
copy from experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts
copy to experimental/traffic-portal/cypress/e2e/servers/servers.table.cy.ts
index 5715f4416a..f5bcda64c8 100644
--- a/experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/servers/servers.table.cy.ts
@@ -12,12 +12,13 @@
 * limitations under the License.
 */
 
-describe("Servers Table Spec", () => {
-	it("Filter by hostname", async () => {
-		const page = browser.page.servers.serversTable();
-		await page.section.serversTable.open();
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		page.section.serversTable.searchText("edge");
-		await page.assert.urlContains("search=edge");
+describe("Servers table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Filters servers by hostname", () => {
+		cy.visit("/core/servers");
+		cy.get("input[name=fuzzControl]").focus().type("edge");
+		cy.window().its("location.search").should("contain", "search=edge");
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/statuses/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/statuses/detail.cy.ts
new file mode 100644
index 0000000000..e24ea965ec
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/statuses/detail.cy.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Status edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Status", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {status} = data;
+				cy.visit(`/core/statuses/${status.id}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", status.name);
+				cy.get("input[name=description]").should("be.enabled").and("have.value", status.description);
+				cy.get("input[name=id]").should("be.disabled").and("have.value", status.id);
+				cy.get("input[name=lastUpdated]").should("be.disabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+				cy.get("button").contains("Create").should("not.exist");
+			}
+		);
+	});
+
+	it("Creates a new Status", () => {
+		cy.visit("/core/statuses/new");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("input[name=description]").should("be.enabled").and("be.empty");
+		cy.get("input[name=id]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("button").contains("Save").should("not.exist");
+		cy.get("button").contains("Create").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts b/experimental/traffic-portal/cypress/e2e/statuses/table.cy.ts
similarity index 61%
copy from experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
copy to experimental/traffic-portal/cypress/e2e/statuses/table.cy.ts
index 5ce3e4117b..4e5315c152 100644
--- a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
+++ b/experimental/traffic-portal/cypress/e2e/statuses/table.cy.ts
@@ -12,19 +12,13 @@
  * limitations under the License.
  */
 
-import {
-	EnhancedPageObject
-} from "nightwatch";
-
-/**
- * Define the type for our PO
- */
-export type DeliveryServiceInvalidPageObject = EnhancedPageObject<{}, typeof deliveryServiceInvalidPageObject.elements>;
-
-const deliveryServiceInvalidPageObject = {
-	elements: {
-		addButton: "button#new",
-	}
-};
-
-export default deliveryServiceInvalidPageObject;
+describe("Statuses table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Loads elements", () => {
+		cy.visit("/core/statuses");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
+	});
+});
diff --git a/experimental/traffic-portal/cypress/e2e/types/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/types/detail.cy.ts
new file mode 100644
index 0000000000..2faa061935
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/types/detail.cy.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Type creation/edit page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Type", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {type} = data;
+				cy.visit(`/core/types/${type.id}`);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", type.name);
+				cy.get("input[name=description]").should("be.enabled").and("have.value", type.description);
+				cy.get("input[name=useInTable]").should("be.disabled").and("have.value", type.useInTable);
+				cy.get("input[name=id]").should("be.disabled").and("have.value", type.id);
+				cy.get("input[name=lastUpdated]").should("be.disabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates a new Type", () => {
+		cy.visit("/core/types/new");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("input[name=description]").should("be.enabled").and("be.empty");
+		cy.get("input[name=useInTable]").should("be.disabled").and("have.value", "server");
+		cy.get("input[name=id]").should("not.exist");
+		cy.get("input[name=lastUpdated]").should("not.exist");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts b/experimental/traffic-portal/cypress/e2e/types/table.cy.ts
similarity index 61%
copy from experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
copy to experimental/traffic-portal/cypress/e2e/types/table.cy.ts
index 5ce3e4117b..aecf9ec8a6 100644
--- a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
+++ b/experimental/traffic-portal/cypress/e2e/types/table.cy.ts
@@ -12,19 +12,13 @@
  * limitations under the License.
  */
 
-import {
-	EnhancedPageObject
-} from "nightwatch";
-
-/**
- * Define the type for our PO
- */
-export type DeliveryServiceInvalidPageObject = EnhancedPageObject<{}, typeof deliveryServiceInvalidPageObject.elements>;
-
-const deliveryServiceInvalidPageObject = {
-	elements: {
-		addButton: "button#new",
-	}
-};
-
-export default deliveryServiceInvalidPageObject;
+describe("Types table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Loads elements", () => {
+		cy.visit("/core/types");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/changeLogs.spec.ts b/experimental/traffic-portal/cypress/e2e/users/changeLogs.cy.ts
similarity index 59%
rename from experimental/traffic-portal/nightwatch/tests/users/changeLogs.spec.ts
rename to experimental/traffic-portal/cypress/e2e/users/changeLogs.cy.ts
index bdb3d867f2..458fc6bdf5 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/changeLogs.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/users/changeLogs.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Change Logs Spec", () => {
-	it("Filter by text", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("changeLogs", ["otherContainer"]);
-		const page = browser.page.users.changeLogs();
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-
-		page.section.changeLogsTable.searchText("test");
-		await page.assert.urlContains("search=test");
+describe("Change Logs table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Filters by text", () => {
+		cy.visit("/core/change-logs");
+		cy.get("input[name=fuzzControl]").click().focus().type("test");
+		cy.window().its("location.search").should("contain", "search=test");
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/users/role/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/users/role/detail.cy.ts
new file mode 100644
index 0000000000..3710b56237
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/users/role/detail.cy.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { CreatedData } from "cypress/support/testing.data";
+
+describe("Role creation/edit page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits an existing Role", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {role} = data;
+				cy.visit(`/core/roles/${role.name}`);
+				cy.get("input[name=description]").should("be.enabled").and("have.value", role.description);
+				cy.get("input[name=name]").should("be.enabled").and("have.value", role.name);
+				cy.get("textarea[name=permissions]")
+					.should("have.value", (role.permissions ?? [""]).join("\n"))
+					.and("not.have.attr", "readonly");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates new Roles", () => {
+		cy.visit("/core/new-role");
+		cy.get("input[name=description]").should("be.enabled").and("be.empty");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("textarea[name=permissions]").should("be.enabled").and("not.have.attr", "readonly");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts b/experimental/traffic-portal/cypress/e2e/users/role/table.cy.ts
similarity index 62%
rename from experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
rename to experimental/traffic-portal/cypress/e2e/users/role/table.cy.ts
index 531eaabb43..8ee2624d79 100644
--- a/experimental/traffic-portal/nightwatch/tests/users/role/table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/users/role/table.cy.ts
@@ -12,15 +12,13 @@
  * limitations under the License.
  */
 
-describe("Roles Spec", () => {
+describe("Roles table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
 	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("roles", ["usersContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
+		cy.visit("/core/roles");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 1);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/e2e/users/tenant/detail.cy.ts b/experimental/traffic-portal/cypress/e2e/users/tenant/detail.cy.ts
new file mode 100644
index 0000000000..6f42b63dfc
--- /dev/null
+++ b/experimental/traffic-portal/cypress/e2e/users/tenant/detail.cy.ts
@@ -0,0 +1,53 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { CreatedData } from "cypress/support/testing.data";
+
+describe("Tenant edit/creation page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Edits the 'root' Tenant", () => {
+		// TODO: There's no guarantee that any Tenant exists  with the ID '1',
+		// let alone that this be some special "root" tenant.
+		cy.visit("/core/tenants/1");
+		// TODO: idk how this was working in nightwatch. but it ain't working in
+		// Cypress.
+		// cy.get("input[name=active]").should("not.be.disabled").and("have.value", "on");
+		// cy.get("input[name=parentTenant-tree-select]").should("not.be.disabled");
+		cy.get("input[name=name]").should("be.disabled").and("have.value", "root");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+
+	it("Edits an existing Tenant", () => {
+		cy.fixture("test.data").then(
+			(data: CreatedData) => {
+				const {tenant} = data;
+				cy.visit(`/core/tenants/${tenant.id}`);
+				cy.get("input[name=active]").should("be.enabled");
+				cy.get("input[name=name").should("be.enabled").and("have.value", tenant.name);
+				cy.get("input[name=parentTenant-tree-select]").should("be.enabled");
+				cy.get("button").contains("Save").should("not.be.disabled");
+			}
+		);
+	});
+
+	it("Creates a new Tenant", () => {
+		cy.visit("/core/tenants/new");
+		cy.get("input[name=active]").should("be.enabled").and("have.value", "on");
+		cy.get("input[name=name]").should("be.enabled").and("be.empty");
+		cy.get("input[name=parentTenant-tree-select]").should("be.enabled").and("be.empty");
+		cy.get("button").contains("Save").should("not.be.disabled");
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts b/experimental/traffic-portal/cypress/e2e/users/tenant/table.cy.ts
similarity index 61%
rename from experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
rename to experimental/traffic-portal/cypress/e2e/users/tenant/table.cy.ts
index 5ce3e4117b..c133f3747a 100644
--- a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs.ts
+++ b/experimental/traffic-portal/cypress/e2e/users/tenant/table.cy.ts
@@ -12,19 +12,13 @@
  * limitations under the License.
  */
 
-import {
-	EnhancedPageObject
-} from "nightwatch";
-
-/**
- * Define the type for our PO
- */
-export type DeliveryServiceInvalidPageObject = EnhancedPageObject<{}, typeof deliveryServiceInvalidPageObject.elements>;
-
-const deliveryServiceInvalidPageObject = {
-	elements: {
-		addButton: "button#new",
-	}
-};
-
-export default deliveryServiceInvalidPageObject;
+describe("Tenants table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Loads elements", () => {
+		cy.visit("/core/tenants");
+		cy.get("input[name=fuzzControl]");
+		cy.get("div.ag-row").should("have.length.at.least", 2);
+	});
+});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts b/experimental/traffic-portal/cypress/e2e/users/users.cy.ts
similarity index 51%
rename from experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts
rename to experimental/traffic-portal/cypress/e2e/users/users.cy.ts
index 5715f4416a..b2b4612419 100644
--- a/experimental/traffic-portal/nightwatch/tests/servers/servers.table.spec.ts
+++ b/experimental/traffic-portal/cypress/e2e/users/users.cy.ts
@@ -11,13 +11,21 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+import type { LoginData } from "cypress/support/testing.data";
 
-describe("Servers Table Spec", () => {
-	it("Filter by hostname", async () => {
-		const page = browser.page.servers.serversTable();
-		await page.section.serversTable.open();
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		page.section.serversTable.searchText("edge");
-		await page.assert.urlContains("search=edge");
+describe("Users table page", () => {
+	beforeEach(() => {
+		cy.login();
+	});
+	it("Filters by username", () => {
+		cy.fixture("login").then(
+			(data: LoginData) => {
+				const {username} = data;
+				cy.visit("/core/users");
+				cy.get("input[name=fuzzControl]").focus().click().type(username);
+				cy.window().its("location.search").should("contain", `search=${username}`);
+				cy.get(".ag-row:not(.ag-hidden .ag-row)").should("have.length", 1);
+			}
+		);
 	});
 });
diff --git a/experimental/traffic-portal/cypress/fixtures/login.json b/experimental/traffic-portal/cypress/fixtures/login.json
new file mode 100644
index 0000000000..35e1fa737e
--- /dev/null
+++ b/experimental/traffic-portal/cypress/fixtures/login.json
@@ -0,0 +1,4 @@
+{
+	"username": "admin",
+	"password": "twelve12"
+}
diff --git a/experimental/traffic-portal/cypress/fixtures/to.config.json b/experimental/traffic-portal/cypress/fixtures/to.config.json
new file mode 100644
index 0000000000..124f624875
--- /dev/null
+++ b/experimental/traffic-portal/cypress/fixtures/to.config.json
@@ -0,0 +1,6 @@
+{
+	"adminPass": "twelve12",
+	"adminUser": "admin",
+	"apiVersion": "4.1",
+	"toURL": "https://localhost:6443"
+}
diff --git a/experimental/traffic-portal/cypress/support/commands.ts b/experimental/traffic-portal/cypress/support/commands.ts
new file mode 100644
index 0000000000..6b5b1c8b03
--- /dev/null
+++ b/experimental/traffic-portal/cypress/support/commands.ts
@@ -0,0 +1,61 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+//
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+
+/**
+ * Logs in as the configured testing user. This ends with the URL at /core.
+ */
+function login(): void {
+	cy.visit("login");
+	cy.fixture("login").then(
+		({username, password}: {username: string; password: string}) => {
+			cy.get("input").first().type(username);
+			cy.get("input").last().type(password);
+			cy.contains("button", "Login").click();
+			cy.window().its("location.pathname").should("eq", "/core");
+		}
+	);
+}
+
+// This is how Cypress is organized; we don't have control over it.
+// eslint-disable-next-line @typescript-eslint/no-namespace
+declare namespace Cypress {
+	// All declarations in the namespace must match in type, and Cypress itself
+	// uses `any`, so we are powerless to do something better here.
+	/* eslint-disable @typescript-eslint/no-explicit-any */
+	// This type parameter is necessary because it makes the interface match the
+	// expanded definition provided by Cypress itself.
+	/* eslint-disable @typescript-eslint/no-unused-vars */
+	// We shouldn't document Cypress interfaces.
+	// eslint-disable-next-line jsdoc/require-jsdoc
+	interface Chainable<Subject = any> {
+		login(): typeof login;
+	}
+	/* eslint-enable @typescript-eslint/no-explicit-any */
+	/* eslint-enable @typescript-eslint/no-unused-vars */
+}
+Cypress.Commands.add("login", login);
diff --git a/experimental/traffic-portal/cypress/support/component-index.html b/experimental/traffic-portal/cypress/support/component-index.html
new file mode 100644
index 0000000000..ecd66648fd
--- /dev/null
+++ b/experimental/traffic-portal/cypress/support/component-index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <title>Components App</title>
+  </head>
+  <body>
+    <div data-cy-root></div>
+  </body>
+</html>
diff --git a/experimental/traffic-portal/cypress/support/component.ts b/experimental/traffic-portal/cypress/support/component.ts
new file mode 100644
index 0000000000..fdcb1d9566
--- /dev/null
+++ b/experimental/traffic-portal/cypress/support/component.ts
@@ -0,0 +1,53 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// ***********************************************************
+// This example support/component.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+// import "./commands";
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
+
+// import { mount } from "cypress/angular";
+
+// Augment the Cypress namespace to include type definitions for
+// your custom command.
+// Alternatively, can be defined in cypress/support/component.d.ts
+// with a <reference path="./component" /> at the top of your spec.
+// declare global {
+// 	namespace Cypress {
+// 		interface Chainable {
+// 			mount: typeof mount;
+// 		}
+// 	}
+// }
+
+// Cypress.Commands.add("mount", mount);
+
+// Example use:
+// cy.mount(MyComponent)
diff --git a/experimental/traffic-portal/cypress/support/e2e.ts b/experimental/traffic-portal/cypress/support/e2e.ts
new file mode 100644
index 0000000000..2a4481da9f
--- /dev/null
+++ b/experimental/traffic-portal/cypress/support/e2e.ts
@@ -0,0 +1,31 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// ***********************************************************
+// This example support/e2e.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// When a command from ./commands is ready to use, import with `import './commands'` syntax
+import "./commands";
diff --git a/experimental/traffic-portal/cypress/support/testing.data.ts b/experimental/traffic-portal/cypress/support/testing.data.ts
new file mode 100644
index 0000000000..d21a45cd58
--- /dev/null
+++ b/experimental/traffic-portal/cypress/support/testing.data.ts
@@ -0,0 +1,65 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import type {
+	ResponseASN,
+	ResponseCDN,
+	ResponseCacheGroup,
+	ResponseCoordinate,
+	ResponseDeliveryService,
+	ResponseDivision,
+	ResponseParameter,
+	ResponsePhysicalLocation,
+	ResponseProfile,
+	ResponseRegion,
+	ResponseRole,
+	ResponseServer,
+	ResponseServerCapability,
+	ResponseStatus,
+	ResponseTenant,
+	TypeFromResponse
+} from "trafficops-types";
+
+/**
+ * The mock data created for use in E2E testing.
+ */
+export interface CreatedData {
+	asn: ResponseASN;
+	cacheGroup: ResponseCacheGroup;
+	capability: ResponseServerCapability;
+	cdn: ResponseCDN;
+	coordinate: ResponseCoordinate;
+	division: ResponseDivision;
+	ds: ResponseDeliveryService;
+	ds2: ResponseDeliveryService;
+	edgeServer: ResponseServer;
+	parameter: ResponseParameter;
+	physLoc: ResponsePhysicalLocation;
+	region: ResponseRegion;
+	role: ResponseRole;
+	steeringDS: ResponseDeliveryService;
+	tenant: ResponseTenant;
+	type: TypeFromResponse;
+	status: ResponseStatus;
+	profile: ResponseProfile;
+	uniqueString: string;
+}
+
+/**
+ * Contains data used by the E2E tests to authenticate with Traffic Ops.
+ */
+export interface LoginData {
+	username: string;
+	password: string;
+}
diff --git a/experimental/traffic-portal/cypress/tsconfig.json b/experimental/traffic-portal/cypress/tsconfig.json
new file mode 100644
index 0000000000..041e894509
--- /dev/null
+++ b/experimental/traffic-portal/cypress/tsconfig.json
@@ -0,0 +1,8 @@
+{
+	"extends": "../tsconfig.json",
+	"include": ["**/*.ts"],
+	"compilerOptions": {
+		"sourceMap": false,
+		"types": ["cypress"]
+	}
+}
diff --git a/experimental/traffic-portal/nightwatch/.eslintrc.json b/experimental/traffic-portal/nightwatch/.eslintrc.json
deleted file mode 100644
index 382602e237..0000000000
--- a/experimental/traffic-portal/nightwatch/.eslintrc.json
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-{
-	"extends": [
-		"../.eslintrc.json"
-	],
-	"overrides": [
-		{
-			"parserOptions": {
-				"project": [
-					"nightwatch/tsconfig.e2e.json"
-				]
-			},
-			"files": [
-				"*.ts",
-				"*.js"
-			],
-			"rules": {
-				"@typescript-eslint/naming-convention": [
-					"error",
-					{
-						"selector": "objectLiteralProperty",
-						"format": [
-							"camelCase"
-						],
-						"filter": {
-							"regex": "^[a-zA-Z0-9.,!?_: ]+$",
-							"match": false
-						}
-					}
-				],
-				"no-restricted-imports": [
-					"error",
-					"../"
-				],
-				"no-console": "off"
-			}
-		}
-	]
-}
diff --git a/experimental/traffic-portal/nightwatch/README.md b/experimental/traffic-portal/nightwatch/README.md
deleted file mode 100644
index 127a7c41c8..0000000000
--- a/experimental/traffic-portal/nightwatch/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-# Running
-From the `traffic-portal` directory, all npm run scripts with `e2e` can be used to run e2e tests. 
-`ng e2e` also works and is functionally equivalent to `npm run e2e`.
-
-## Requirements
-This suite assumes that you have a working Traffic Ops/Traffic Portal v2 instance. The file `globals/globals.ts` defines 
-the admin user/password needed for testing as well as the TO API URL.
-
-## Browsers
-It is possible to run tests using your browser of choice (see `nightwatch.conf.js`), this repo has the necessary packages
-to run using Firefox and Chrome with Chrome being the default. To run under a specific environment pass
-the `--env {browserName}` flag to the npm run script e.g. `npm run e2e -- --env firefox`.
diff --git a/experimental/traffic-portal/nightwatch/config.json b/experimental/traffic-portal/nightwatch/config.json
deleted file mode 100644
index c82b5a0e28..0000000000
--- a/experimental/traffic-portal/nightwatch/config.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-	"adminPass": "twelve12",
-	"adminUser": "admin",
-	"disableColors": false,
-	"retryAssertionTimeoutMS": 5000,
-	"to_url": "https://localhost:6443",
-	"tp_url": "http://localhost:4200",
-	"waitForConditionTimeoutMS": 5000
-}
diff --git a/experimental/traffic-portal/nightwatch/dataClient.ts b/experimental/traffic-portal/nightwatch/dataClient.ts
deleted file mode 100644
index 67d267700c..0000000000
--- a/experimental/traffic-portal/nightwatch/dataClient.ts
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import * as https from "https";
-
-import axios, { AxiosError, AxiosInstance } from "axios";
-import { CreatedData } from "nightwatch/globals/globals";
-import {
-	CDN,
-	GeoLimit,
-	GeoProvider,
-	LoginRequest,
-	ProfileType,
-	Protocol,
-	RequestASN,
-	RequestCacheGroup,
-	RequestCoordinate,
-	RequestDeliveryService,
-	RequestDivision,
-	RequestParameter,
-	RequestPhysicalLocation,
-	RequestProfile,
-	RequestRegion,
-	RequestRole,
-	RequestServer,
-	RequestServerCapability,
-	RequestStatus,
-	RequestSteeringTarget,
-	RequestTenant,
-	RequestType,
-	ResponseCacheGroup,
-	ResponseDeliveryService,
-	ResponseDivision,
-	ResponseParameter,
-	ResponsePhysicalLocation,
-	ResponseProfile,
-	ResponseRegion,
-	ResponseStatus,
-	TypeFromResponse
-} from "trafficops-types";
-
-/**
- * Generates a unique string used for tests, uses the current epoch time.
- *
- * @returns a unique string
- */
-export function generateUniqueString(): string {
-	return new Date().getTime().toString();
-}
-
-/**
- * Defines the class used to create test data for the E2E environment
- */
-export class DataClient {
-	private readonly toURL: string;
-	private readonly apiVersion: string;
-	private readonly adminUser: string;
-	private readonly adminPass: string;
-	/** Tracks if the client has logged in */
-	public loggedIn = false;
-	/** Client used to talk to the TO API */
-	private readonly client: AxiosInstance;
-
-	public constructor(toURL: string, apiVersion: string, adminUser: string, adminPass: string) {
-		this.toURL = toURL;
-		this.apiVersion = apiVersion;
-		this.adminUser = adminUser;
-		this.adminPass = adminPass;
-
-		this.client = axios.create({
-			httpsAgent: new https.Agent({
-				rejectUnauthorized: false
-			})
-		});
-	}
-
-	/**
-	 * Creates data needed for the E2E tests
-	 *
-	 * @param id ID added to various fields to ensure that creation occurs regardless of environment
-	 */
-	public async createData(id: string): Promise<CreatedData> {
-		const apiUrl = `${this.toURL}/api/${this.apiVersion}`;
-		if (Object.keys(this.client.defaults.headers.common).indexOf("Cookie") === -1) {
-			this.loggedIn = false;
-			let accessToken = "";
-			const loginReq: LoginRequest = {
-				p: this.adminPass,
-				u: this.adminUser
-			};
-			try {
-				const logResp = await this.client.post(`${apiUrl}/user/login`, JSON.stringify(loginReq));
-				if (logResp.headers["set-cookie"]) {
-					for (const cookie of logResp.headers["set-cookie"]) {
-						if (cookie.indexOf("access_token") > -1) {
-							accessToken = cookie;
-							break;
-						}
-					}
-				}
-			} catch (e) {
-				console.error((e as AxiosError).message);
-				throw e;
-			}
-			if (accessToken === "") {
-				const e = new Error("Access token is not set");
-				console.error(e.message);
-				throw e;
-			}
-			this.loggedIn = true;
-			this.client.defaults.headers.common = {Cookie: accessToken};
-		}
-
-		const cdn: CDN = {
-			dnssecEnabled: false, domainName: `tests${id}.com`, name: `testCDN${id}`
-		};
-
-		let resp = await this.client.get(`${apiUrl}/types`);
-		const types: Array<TypeFromResponse> = resp.data.response;
-		const httpType = types.find(typ => typ.name === "HTTP" && typ.useInTable === "deliveryservice");
-		if (httpType === undefined) {
-			throw new Error("Unable to find `HTTP` type");
-		}
-		const steeringType = types.find(typ => typ.name === "STEERING" && typ.useInTable === "deliveryservice");
-		if (steeringType === undefined) {
-			throw new Error("Unable to find `STEERING` type");
-		}
-		const steeringWeightType = types.find(typ => typ.name === "STEERING_WEIGHT" && typ.useInTable === "steering_target");
-		if (steeringWeightType === undefined) {
-			throw new Error("Unable to find `STEERING_WEIGHT` type");
-		}
-		const cgType = types.find(typ => typ.useInTable === "cachegroup");
-		if (!cgType) {
-			throw new Error("Unable to find any Cache Group Types");
-		}
-		const edgeType = types.find(typ => typ.useInTable === "server" && typ.name === "EDGE");
-		if (edgeType === undefined) {
-			throw new Error("Unable to find `EDGE` type");
-		}
-
-		const data = {} as CreatedData;
-		let url = `${apiUrl}/cdns`;
-		try {
-			resp = await this.client.post(url, JSON.stringify(cdn));
-			const respCDN = resp.data.response;
-			data.cdn = respCDN;
-
-			const ds: RequestDeliveryService = {
-				active: false,
-				cacheurl: null,
-				cdnId: respCDN.id,
-				displayName: `test DS${id}`,
-				dscp: 0,
-				ecsEnabled: false,
-				edgeHeaderRewrite: null,
-				fqPacingRate: null,
-				geoLimit: GeoLimit.NONE,
-				geoProvider: GeoProvider.MAX_MIND,
-				httpBypassFqdn: null,
-				infoUrl: null,
-				initialDispersion: 1,
-				ipv6RoutingEnabled: false,
-				logsEnabled: false,
-				maxOriginConnections: 0,
-				maxRequestHeaderBytes: 0,
-				midHeaderRewrite: null,
-				missLat: 0,
-				missLong: 0,
-				multiSiteOrigin: false,
-				orgServerFqdn: "http://test.com",
-				profileId: 1,
-				protocol: Protocol.HTTP,
-				qstringIgnore: 0,
-				rangeRequestHandling: 0,
-				regionalGeoBlocking: false,
-				remapText: null,
-				routingName: "test",
-				signed: false,
-				tenantId: 1,
-				typeId: httpType.id,
-				xmlId: `testDS${id}`
-			};
-			url = `${apiUrl}/deliveryservices`;
-			resp = await this.client.post(url, JSON.stringify(ds));
-			let respDS: ResponseDeliveryService = resp.data.response[0];
-			data.ds = respDS;
-
-			ds.displayName = `test DS2${id}`;
-			ds.xmlId = `testDS2${id}`;
-			resp = await this.client.post(url, JSON.stringify(ds));
-			respDS = resp.data.response[0];
-			data.ds2 = respDS;
-
-			ds.displayName = `test steering DS${id}`;
-			ds.xmlId = `testSDS${id}`;
-			ds.typeId = steeringType.id;
-			resp = await this.client.post(url, JSON.stringify(ds));
-			respDS = resp.data.response[0];
-			data.steeringDS = respDS;
-
-			const target: RequestSteeringTarget = {
-				targetId: data.ds.id,
-				typeId: steeringWeightType.id,
-				value: 1
-			};
-			url = `${apiUrl}/steering/${data.steeringDS.id}/targets`;
-			await this.client.post(url, JSON.stringify(target));
-			target.targetId = data.ds2.id;
-			await this.client.post(url, JSON.stringify(target));
-
-			const tenant: RequestTenant = {
-				active: true,
-				name: `testT${id}`,
-				parentId: 1
-			};
-			url = `${apiUrl}/tenants`;
-			resp = await this.client.post(url, JSON.stringify(tenant));
-			data.tenant = resp.data.response;
-
-			const division: RequestDivision = {
-				name: `testD${id}`
-			};
-			url = `${apiUrl}/divisions`;
-			resp = await this.client.post(url, JSON.stringify(division));
-			const respDivision: ResponseDivision = resp.data.response;
-			data.division = respDivision;
-
-			const region: RequestRegion = {
-				division: respDivision.id,
-				name: `testR${id}`
-			};
-			url = `${apiUrl}/regions`;
-			resp = await this.client.post(url, JSON.stringify(region));
-			const respRegion: ResponseRegion = resp.data.response;
-			data.region = respRegion;
-
-			const cacheGroup: RequestCacheGroup = {
-				name: `test${id}`,
-				shortName: `test${id}`,
-				typeId: cgType.id
-			};
-			url = `${apiUrl}/cachegroups`;
-			resp = await this.client.post(url, JSON.stringify(cacheGroup));
-			const responseCG: ResponseCacheGroup = resp.data.response;
-			data.cacheGroup = responseCG;
-
-			const asn: RequestASN = {
-				asn: +id,
-				cachegroupId: responseCG.id
-			};
-			url = `${apiUrl}/asns`;
-			resp = await this.client.post(url, JSON.stringify(asn));
-			data.asn = resp.data.response;
-
-			const physLoc: RequestPhysicalLocation = {
-				address: "street",
-				city: "city",
-				comments: "someone set us up the bomb",
-				email: "email@test.com",
-				name: `phys${id}`,
-				phone: "111-867-5309",
-				poc: "me",
-				regionId: respRegion.id,
-				shortName: `short${id}`,
-				state: "CA",
-				zip: "80000"
-			};
-			url = `${apiUrl}/phys_locations`;
-			resp = await this.client.post(url, JSON.stringify(physLoc));
-			const respPhysLoc: ResponsePhysicalLocation = resp.data.response;
-			respPhysLoc.region = respRegion.name;
-			data.physLoc = respPhysLoc;
-
-			const coordinate: RequestCoordinate = {
-				latitude: 0,
-				longitude: 0,
-				name: `coord${id}`
-			};
-			url = `${apiUrl}/coordinates`;
-			resp = await this.client.post(url, JSON.stringify(coordinate));
-			data.coordinate = resp.data.response;
-
-			const type: RequestType = {
-				description: "blah",
-				name: `type${id}`,
-				useInTable: "server"
-			};
-			url = `${apiUrl}/types`;
-			resp = await this.client.post(url, JSON.stringify(type));
-
-			data.type = resp.data.response;
-			const status: RequestStatus = {
-				description: "blah",
-				name: `status${id}`,
-			};
-			url = `${apiUrl}/statuses`;
-			resp = await this.client.post(url, JSON.stringify(status));
-			const respStatus: ResponseStatus = resp.data.response;
-			data.statuses = respStatus;
-
-			const profile: RequestProfile = {
-				cdn: respCDN.id,
-				description: "blah",
-				name: `profile${id}`,
-				routingDisabled: false,
-				type: ProfileType.ATS_PROFILE,
-			};
-			url = `${apiUrl}/profiles`;
-			resp = await this.client.post(url, JSON.stringify(profile));
-			const respProfile: ResponseProfile = resp.data.response;
-			data.profile = respProfile;
-
-			const parameter: RequestParameter = {
-				configFile: "cfg.txt",
-				name: `param${id}`,
-				secure: false,
-				value: "10",
-			};
-			url = `${apiUrl}/parameters`;
-			resp = await this.client.post(url, JSON.stringify(parameter));
-			const responseParameter: ResponseParameter = resp.data.response;
-			console.log(`Successfully created Parameter ${responseParameter.name}`);
-			data.parameter = responseParameter;
-
-			const server: RequestServer = {
-				cachegroupId: responseCG.id,
-				cdnId: respCDN.id,
-				domainName: "domain.com",
-				hostName: id,
-				interfaces: [{
-					ipAddresses: [{
-						address: "192.160.1.0",
-						gateway: null,
-						serviceAddress: true
-					}],
-					maxBandwidth: 0,
-					monitor: true,
-					mtu: 1500,
-					name: "eth0"
-				}],
-				physLocationId: respPhysLoc.id,
-				profileNames: [respProfile.name],
-				statusId: respStatus.id,
-				typeId: edgeType.id
-
-			};
-			url = `${apiUrl}/servers`;
-			resp = await this.client.post(url, JSON.stringify(server));
-			data.edgeServer = resp.data.response;
-
-			const capability: RequestServerCapability = {
-				name: `test${id}`
-			};
-			url = `${apiUrl}/server_capabilities`;
-			resp = await this.client.post(url, JSON.stringify(capability));
-			data.capability = resp.data.response;
-
-			const role: RequestRole = {
-				description: "Has access to everything - cannot be modified or deleted",
-				name: `admin${id}`,
-				permissions: [
-					"ALL"
-				]
-			};
-			url = `${apiUrl}/roles`;
-			resp = await this.client.post(url, JSON.stringify(role));
-			data.role = resp.data.response;
-		} catch (e) {
-			const ae = e as AxiosError;
-			ae.message = `Request (${ae.config.method}) failed to ${url}`;
-			ae.message += ae.response ? ` with response code ${ae.response.status}` : " with no response";
-			throw ae;
-		}
-
-		return data;
-	}
-}
diff --git a/experimental/traffic-portal/nightwatch/globals/globals.ts b/experimental/traffic-portal/nightwatch/globals/globals.ts
deleted file mode 100644
index 4eaf080822..0000000000
--- a/experimental/traffic-portal/nightwatch/globals/globals.ts
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import { AxiosError } from "axios";
-import { NightwatchBrowser } from "nightwatch";
-import type { AsnDetailPageObject } from "nightwatch/page_objects/cacheGroups/asnDetail";
-import type { AsnsPageObject } from "nightwatch/page_objects/cacheGroups/asnsTable";
-import type { CacheGroupDetailPageObject } from "nightwatch/page_objects/cacheGroups/cacheGroupDetails";
-import type { CacheGroupsPageObject } from "nightwatch/page_objects/cacheGroups/cacheGroupsTable";
-import type { CoordinateDetailPageObject } from "nightwatch/page_objects/cacheGroups/coordinateDetail";
-import type { CoordinatesPageObject } from "nightwatch/page_objects/cacheGroups/coordinatesTable";
-import type { DivisionDetailPageObject } from "nightwatch/page_objects/cacheGroups/divisionDetail";
-import type { DivisionsPageObject } from "nightwatch/page_objects/cacheGroups/divisionsTable";
-import type { RegionDetailPageObject } from "nightwatch/page_objects/cacheGroups/regionDetail";
-import type { RegionsPageObject } from "nightwatch/page_objects/cacheGroups/regionsTable";
-import type { CDNDetailPageObject } from "nightwatch/page_objects/cdns/cdnDetail";
-import type { CommonPageObject } from "nightwatch/page_objects/common";
-import type { DeliveryServiceCardPageObject } from "nightwatch/page_objects/deliveryServices/deliveryServiceCard";
-import type { DeliveryServiceDetailPageObject } from "nightwatch/page_objects/deliveryServices/deliveryServiceDetail";
-import type { DeliveryServiceInvalidPageObject } from "nightwatch/page_objects/deliveryServices/deliveryServiceInvalidationJobs";
-import type { LoginPageObject } from "nightwatch/page_objects/login";
-import type { ParametersPageObject } from "nightwatch/page_objects/parameters/parametersTable";
-import type { ProfileDetailPageObject } from "nightwatch/page_objects/profiles/profileDetail";
-import type { ProfilePageObject } from "nightwatch/page_objects/profiles/profilesTable";
-import type { PhysLocDetailPageObject } from "nightwatch/page_objects/servers/physLocDetail";
-import type { PhysLocTablePageObject } from "nightwatch/page_objects/servers/physLocTable";
-import type { ServersDetailPageObject } from "nightwatch/page_objects/servers/serversDetail";
-import type { ServersTablePageObject } from "nightwatch/page_objects/servers/serversTable";
-import type { StatusDetailPageObject } from "nightwatch/page_objects/statuses/statusDetail";
-import type { StatusesTablePageObject } from "nightwatch/page_objects/statuses/statusesTable";
-import type { ChangeLogsPageObject } from "nightwatch/page_objects/users/changeLogs";
-import type { RoleDetailPageObject } from "nightwatch/page_objects/users/roleDetail";
-import type { RolesPageObject } from "nightwatch/page_objects/users/rolesTable";
-import type { TenantDetailPageObject } from "nightwatch/page_objects/users/tenantDetail";
-import type { TenantsPageObject } from "nightwatch/page_objects/users/tenants";
-import type { UsersPageObject } from "nightwatch/page_objects/users/users";
-import {
-	ResponseCDN,
-	ResponseDeliveryService,
-	ResponseTenant,
-	TypeFromResponse,
-	ResponseASN,
-	ResponseDivision,
-	ResponseRegion,
-	ResponseCacheGroup,
-	ResponsePhysicalLocation,
-	ResponseCoordinate,
-	ResponseStatus,
-	ResponseProfile,
-	ResponseServer, ResponseServerCapability, ResponseRole, ResponseParameter,
-} from "trafficops-types";
-
-import * as config from "../config.json";
-import { DataClient, generateUniqueString } from "../dataClient";
-import {ParameterDetailPageObject} from "../page_objects/parameters/parameterDetail";
-import type { CapabilitiesPageObject } from "../page_objects/servers/capabilities/capabilitiesTable";
-import type { CapabilityDetailsPageObject } from "../page_objects/servers/capabilities/capabilityDetails";
-import type { TypeDetailPageObject } from "../page_objects/types/typeDetail";
-import type { TypesPageObject } from "../page_objects/types/typesTable";
-
-declare module "nightwatch" {
-	/**
-	 * Defines the global nightwatch browser type with our types mixed in.
-	 */
-	export interface NightwatchCustomPageObjects {
-		common: () => CommonPageObject;
-		cacheGroups: {
-			cacheGroupDetails: () => CacheGroupDetailPageObject;
-			cacheGroupsTable: () => CacheGroupsPageObject;
-			coordinateDetail: () => CoordinateDetailPageObject;
-			coordinatesTable: () => CoordinatesPageObject;
-			divisionDetail: () => DivisionDetailPageObject;
-			divisionsTable: () => DivisionsPageObject;
-			regionDetail: () => RegionDetailPageObject;
-			regionsTable: () => RegionsPageObject;
-			asnsTable: () => AsnsPageObject;
-			asnDetail: () => AsnDetailPageObject;
-		};
-		cdns: {
-			cdnDetail: () => CDNDetailPageObject;
-		};
-		deliveryServices: {
-			deliveryServiceCard: () => DeliveryServiceCardPageObject;
-			deliveryServiceDetail: () => DeliveryServiceDetailPageObject;
-			deliveryServiceInvalidationJobs: () => DeliveryServiceInvalidPageObject;
-		};
-		login: () => LoginPageObject;
-		profiles: {
-			parametersTable: () => ParametersPageObject;
-			parameterDetail: () => ParameterDetailPageObject;
-			profileTable: () => ProfilePageObject;
-			profileDetail: () => ProfileDetailPageObject;
-		};
-		servers: {
-			capabilities: {
-				capabilityDetails: () => CapabilityDetailsPageObject;
-				capabilitiesTable: () => CapabilitiesPageObject;
-			};
-			physLocDetail: () => PhysLocDetailPageObject;
-			physLocTable: () => PhysLocTablePageObject;
-			serversTable: () => ServersTablePageObject;
-			serversDetail: () => ServersDetailPageObject;
-		};
-		statuses: {
-			statusesTable: () => StatusesTablePageObject;
-			statusDetail: () => StatusDetailPageObject;
-		};
-		users: {
-			changeLogs: () => ChangeLogsPageObject;
-			roles: () => RolesPageObject;
-			roleDetail: () => RoleDetailPageObject;
-			tenants: () => TenantsPageObject;
-			tenantDetail: () => TenantDetailPageObject;
-			users: () => UsersPageObject;
-		};
-		types: {
-			typesTable: () => TypesPageObject;
-			typeDetail: () => TypeDetailPageObject;
-		};
-	}
-
-	/**
-	 * Defines the additional types needed for the test environment.
-	 */
-	export interface NightwatchGlobals {
-		adminPass: string;
-		adminUser: string;
-		trafficOpsURL: string;
-		apiVersion: string;
-		uniqueString: string;
-		testData: CreatedData;
-	}
-}
-
-/**
- * Contains the data created by the client before the test suite runs.
- */
-export interface CreatedData {
-	asn: ResponseASN;
-	cacheGroup: ResponseCacheGroup;
-	capability: ResponseServerCapability;
-	cdn: ResponseCDN;
-	coordinate: ResponseCoordinate;
-	division: ResponseDivision;
-	ds: ResponseDeliveryService;
-	ds2: ResponseDeliveryService;
-	edgeServer: ResponseServer;
-	parameter: ResponseParameter;
-	physLoc: ResponsePhysicalLocation;
-	region: ResponseRegion;
-	role: ResponseRole;
-	steeringDS: ResponseDeliveryService;
-	tenant: ResponseTenant;
-	type: TypeFromResponse;
-	statuses: ResponseStatus;
-	profile: ResponseProfile;
-}
-
-let testData = {};
-let client: DataClient;
-let dataCreateFailed = false;
-
-const globals = {
-	adminPass: config.adminPass,
-	adminUser: config.adminUser,
-	after: async (done: () => void): Promise<void> => {
-		if (dataCreateFailed){
-			return done();
-		} else if(client.loggedIn) {
-			try {
-				await client.createData(generateUniqueString());
-			} catch(e) {
-				console.error("Idempotency test failed, err:", e);
-				throw e;
-			}
-			console.log("Data creation is idempotent");
-		} else {
-			console.log("Client not logged in, skipping idempotency test");
-		}
-		done();
-	},
-	afterEach: (browser: NightwatchBrowser, done: () => void): void => {
-		browser.end(() => {
-			done();
-		});
-	},
-	apiVersion: "4.0",
-	before: async (done: () => void): Promise<void> => {
-		client = new DataClient(globals.trafficOpsURL, globals.apiVersion, globals.adminUser, globals.adminPass);
-		try {
-			testData = await client.createData(globals.uniqueString);
-		} catch(e) {
-			dataCreateFailed = true;
-			console.error("Request for", globals.trafficOpsURL, "failed:", (e as AxiosError).message);
-			throw e;
-		}
-		done();
-	},
-	beforeEach: (browser: NightwatchBrowser, done: () => void): void => {
-		browser.globals.testData = testData as CreatedData;
-		browser.page.login()
-			.navigate().section.loginForm
-			.loginAndWait(browser.globals.adminUser, browser.globals.adminPass);
-		// This ensures that we call done after loginAndWait is finished
-		browser.pause(1, () => {
-			done();
-		});
-	},
-	retryAssertionTimeout: config.retryAssertionTimeoutMS,
-	testData,
-	trafficOpsURL: config.to_url,
-	uniqueString: generateUniqueString(),
-	waitForConditionTimeout:config.waitForConditionTimeoutMS
-};
-
-module.exports = globals;
diff --git a/experimental/traffic-portal/nightwatch/globals/tables/index.ts b/experimental/traffic-portal/nightwatch/globals/tables/index.ts
deleted file mode 100644
index b9c5d1b0d3..0000000000
--- a/experimental/traffic-portal/nightwatch/globals/tables/index.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-import type {
-	Awaitable,
-	EnhancedElementInstance,
-	EnhancedPageObject,
-	EnhancedSectionInstance,
-	WebDriverProtocolUserActions
-} from "nightwatch";
-
-/**
- * TableSectionCommands is the base type for page object sections representing
- * pages containing AG-Grid generic tables.
- */
-export interface TableSectionCommands extends EnhancedSectionInstance,
-	EnhancedElementInstance<EnhancedPageObject>, WebDriverProtocolUserActions {
-	doubleClickRow<T extends this>(row: number): Promise<T>;
-
-	filterTableByColumn<T extends this>(column: string, search: string): Promise<T>;
-
-	gotoRowByColumn<T extends this>(column: string, search: string): Promise<T>;
-
-	getColumnState(column: string): Promise<boolean>;
-
-	searchText<T extends this>(text: string): T;
-
-	toggleColumn(column: string): Promise<this>;
-}
-
-/**
- * A CSS selector for an AG-Grid generic table's column visibility dropdown
- * menu.
- */
-export const columnMenuBtnSelector = "div.toggle-columns > button.mat-mdc-menu-trigger";
-
-/**
- * A CSS selector for an AG-Grid generic table's column visibility dropdown
- * menu.
- */
-export const columnMenuCloseSelector = ".cdk-overlay-backdrop";
-
-/**
- * A CSS selector for an AG-Grid generic table's "Fuzzy Search" input text box.
- */
-export const searchboxSelector = "input[name='fuzzControl']";
-
-/**
- * CSS selector for the AG-Grid row(s).
- */
-export const tableRowsSelector = ".ag-center-cols-clipper .ag-row";
-
-/**
- * Gets the state of an AG-Grid column by checking whether it's checked
- * in the column visibility menu (doesn't actually verify that this means the
- * column is visible).
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param column The name of the column being retrieved.
- * @returns The state of the column named `column`. Behavior is undefined if
- * multiple columns exist with the same given name.
- */
-export async function getColumnState(this: TableSectionCommands, column: string): Promise<boolean> {
-	const selector = `input[type='checkbox'][name='${column}']`;
-	await this.click(columnMenuBtnSelector);
-	const selected = await browser.isSelected(selector);
-	return Promise.resolve(selected);
-}
-
-/**
- * Filters a table by a column
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param column Which column to filter
- * @param text Text to filter by
- */
-export async function filterTableByColumn<T extends TableSectionCommands>(
-	this: TableSectionCommands,
-	column: string,
-	text: string): Promise<T> {
-	if (!await this.getColumnState(column)) {
-		await this.toggleColumn(column);
-	}
-	this.searchText(text);
-	return Promise.resolve(this) as Promise<T>;
-}
-
-/**
- * Double-clicks the nth row on a table
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param rowNumber Which row to click
- * @returns The calling command section for call-chaining the way Nightwatch
- * likes to do.
- */
-export function doubleClickRow<T extends TableSectionCommands>(this: TableSectionCommands, rowNumber: number): Awaitable<T, null> {
-	return this.doubleClick("css selector", `${tableRowsSelector}:nth-of-type(${rowNumber})`) as Awaitable<T, null>;
-}
-
-/**
- * Filters a table by a column, then double-clicks the first row resulting from the filtering.
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param column Which column to filter
- * @param text Text to filter by
- */
-export async function gotoRowByColumn<T extends TableSectionCommands>(
-	this: TableSectionCommands, column: string, text: string): Promise<T> {
-	await this.filterTableByColumn(column, text);
-	return this.doubleClickRow(1);
-}
-
-/**
- * Sets the text of the table's "Fuzzy Search" searchbox.
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param text The text to set in the search input.
- * @returns The calling command section for call-chaining the way Nightwatch
- * likes to do.
- */
-export function searchText<T extends TableSectionCommands>(this: T, text: string): Awaitable<T, null> {
-	return this.setValue(searchboxSelector, text);
-}
-
-/**
- * Toggles the presence of a given column.
- *
- * @param this Special parameter that tells the compiler what `this` is in a
- * valid context for this function.
- * @param column The name of the column to be toggled.
- * @returns The calling command section for call-chaining the way Nightwatch
- * likes to do.
- */
-export async function toggleColumn<T extends TableSectionCommands>(this: T, column: string): Promise<T> {
-	const selector = `input[type='checkbox'][name='${column}']`;
-	await browser.findElement(".mat-mdc-menu-panel")
-		.getLocationInView(selector)
-		.click(selector);
-	await browser.click(columnMenuCloseSelector);
-	return Promise.resolve(this);
-}
-
-/**
- * This is meant to be mixed-in to generic table page object command sections,
- * to most easily provide all the functionality of a table.
- */
-export const TABLE_COMMANDS = {
-	doubleClickRow,
-	filterTableByColumn,
-	getColumnState,
-	gotoRowByColumn,
-	searchText,
-	toggleColumn
-};
diff --git a/experimental/traffic-portal/nightwatch/nightwatch.conf.js b/experimental/traffic-portal/nightwatch/nightwatch.conf.js
deleted file mode 100644
index 0d91229e3e..0000000000
--- a/experimental/traffic-portal/nightwatch/nightwatch.conf.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-// eslint-disable-next-line @typescript-eslint/no-require-imports
-const fs = require("fs");
-
-let config = {};
-try {
-	config = JSON.parse(fs.readFileSync("./dist/nightwatch/config.json", "utf8"));
-} catch(e) {
-	console.warn("Cannot read config.json");
-	config = {
-		disableColors: false,
-		tp_url: "http://localhost:4200"
-	};
-}
-
-// Refer to the online docs for more details: https://nightwatchjs.org/gettingstarted/configuration/
-const Services = {};
-
-module.exports = {
-	// See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-assertions
-	custom_assertions_path: "",
-
-	// See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands
-	custom_commands_path: "",
-
-	// See https://nightwatchjs.org/guide/#external-globals
-	globals_path: "../dist/nightwatch/globals/globals.js",
-
-	// See https://nightwatchjs.org/guide/working-with-page-objects/
-	page_objects_path: "./dist/nightwatch/page_objects",
-	// An array of folders (excluding subfolders) where your tests are located;
-	// if this is not specified, the test source must be passed as the second argument to the test runner.
-	src_folders: ["./dist/nightwatch/tests/"],
-
-	test_settings: {
-		chrome: {
-			desiredCapabilities: {
-				browserName: "chrome",
-				"goog:chromeOptions": {
-					args: [
-						"--no-sandbox",
-						"--ignore-certificate-errors",
-						//'--headless'
-					],
-					// More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/
-					//
-					// w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78)
-					w3c: true
-				}
-			},
-
-			webdriver: {
-				cli_args: [
-					// --verbose
-				],
-				server_path: "",
-				start_process: true
-			}
-		},
-
-		chrome_headless: {
-			desiredCapabilities: {
-				"goog:chromeOptions": {
-					args: [
-						"--no-sandbox",
-						"--disable-dev-shm-usage",
-						"--ignore-certificate-errors",
-						"--headless",
-						"--window-size=1920,1080",
-						"--ignore-certificate-errors",
-						"--allow-insecure-localhost"
-					]
-				}
-			},
-			enable_fail_fast: false,
-
-			extends: "chrome"
-		},
-
-		default: {
-			desiredCapabilities: {
-				browserName: "chrome",
-				"goog:chromeOptions": {
-					args: [
-						"--ignore-certificate-errors",
-						"--window-size=1920,1080",
-						"--allow-insecure-localhost"
-					]
-				}
-			},
-			disable_colors: config.disableColors,
-			disable_error_log: false,
-			enable_fail_fast: true,
-			launch_url: config.tp_url,
-			output_folder: "nightwatch/junit",
-			screenshots: {
-				enabled: true,
-				on_failure: true,
-				path: "nightwatch/screens"
-			},
-			webdriver: {
-				server_path: "",
-				start_process: true
-			}
-		},
-
-		edge: {
-			desiredCapabilities: {
-				browserName: "MicrosoftEdge",
-				"ms:edgeOptions": {
-					// More info on EdgeDriver: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options
-					args: [
-						//'--headless'
-					],
-					w3c: true
-				}
-			},
-
-			webdriver: {
-				cli_args: [
-					// --verbose
-				],
-				// Download msedgedriver from https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/
-				//  and set the location below:
-				server_path: "",
-				start_process: true
-			}
-		},
-
-		firefox: {
-			desiredCapabilities: {
-				alwaysMatch: {
-					acceptInsecureCerts: true,
-					"moz:firefoxOptions": {
-						args: [
-							// '-headless',
-							// '-verbose'
-						]
-					}
-				},
-				browserName: "firefox",
-			},
-			webdriver: {
-				cli_args: [
-					// very verbose geckodriver logs
-					// '-vv'
-				],
-				server_path: "",
-				start_process: true
-			}
-		},
-		safari: {
-			desiredCapabilities: {
-				alwaysMatch: {
-					acceptInsecureCerts: false
-				},
-				browserName: "safari"
-			},
-			webdriver: {
-				server_path: "",
-				start_process: true
-			}
-		},
-
-	},
-
-	webdriver: {}
-};
-
-/**
- * Loads browser drivers if available
- *
- */
-function loadServices() {
-	try {
-		// eslint-disable-next-line @typescript-eslint/no-require-imports
-		Services.chromedriver = require("chromedriver");
-	} catch (err) {
-		console.log("Unable to load chromedriver");
-	}
-
-	try {
-		// eslint-disable-next-line @typescript-eslint/no-require-imports
-		Services.geckodriver = require("geckodriver");
-	} catch (err) {
-		console.log("Unable to load geckodriver");
-	}
-}
-
-loadServices();
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnDetail.ts
deleted file mode 100644
index 4548996aa0..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnDetail.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for ASN Details.
- */
-export type AsnDetailPageObject = EnhancedPageObject<{}, typeof asnDetailPageObject.elements>;
-
-const asnDetailPageObject = {
-	elements: {
-		asn: "input[name='asn']",
-		cachegroup: "mat-select[name='cachegroup']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default asnDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnsTable.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnsTable.ts
deleted file mode 100644
index e5ea84a2e6..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/asnsTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the ASNs table commands
- */
-type AsnsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the ASNs page.
- */
-export type AsnsPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<AsnsTableCommands>>;
-
-const asnsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		asnsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/asns`;
-	}
-};
-
-export default asnsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupDetails.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupDetails.ts
deleted file mode 100644
index 96a9f29a87..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupDetails.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Cache Group Details.
- */
-export type CacheGroupDetailPageObject = EnhancedPageObject<{}, typeof cacheGroupDetailPageObject.elements>;
-
-const cacheGroupDetailPageObject = {
-	elements: {
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		latitude: "input[name='latitude']",
-		longitude: "input[name='longitude']",
-		name: "input[name='name']",
-		parent: "mat-select[name='parentCacheGroup']",
-		saveBtn: "button[type='submit']",
-		secondaryParent: "mat-select[name='secondaryParentCacheGroup]",
-	},
-};
-
-export default cacheGroupDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupsTable.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupsTable.ts
deleted file mode 100644
index d1d013d24a..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/cacheGroupsTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Cache Group table commands
- */
-type CacheGroupsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Cache Groups page.
- */
-export type CacheGroupsPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<CacheGroupsTableCommands>>;
-
-const divisionsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		divisionsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/cache-groups`;
-	}
-};
-
-export default divisionsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinateDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinateDetail.ts
deleted file mode 100644
index 18a2db2e05..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinateDetail.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Coordinate Details.
- */
-export type CoordinateDetailPageObject = EnhancedPageObject<{}, typeof coordinateDetailPageObject.elements>;
-
-const coordinateDetailPageObject = {
-	elements: {
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		latitude: "input[name='latitude']",
-		longitude: "input[name='longitude']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default coordinateDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinatesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinatesTable.ts
deleted file mode 100644
index cec1df756d..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/coordinatesTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Coordinates table commands
- */
-type CoordinatesTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Coordinates page.
- */
-export type CoordinatesPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<CoordinatesTableCommands>>;
-
-const coordinatesPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		coordinatesTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/coordinates`;
-	}
-};
-
-export default coordinatesPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionDetail.ts
deleted file mode 100644
index 9bdbdeb1bd..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionDetail.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Division Details.
- */
-export type DivisionDetailPageObject = EnhancedPageObject<{}, typeof divisionDetailPageObject.elements>;
-
-const divisionDetailPageObject = {
-	elements: {
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default divisionDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionsTable.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionsTable.ts
deleted file mode 100644
index 0def680ed8..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/divisionsTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Divisions table commands
- */
-type DivisionsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Divisions page.
- */
-export type DivisionsPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<DivisionsTableCommands>>;
-
-const divisionsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		divisionsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/divisions`;
-	}
-};
-
-export default divisionsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionDetail.ts
deleted file mode 100644
index 62d8cd5593..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionDetail.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Region Details.
- */
-export type RegionDetailPageObject = EnhancedPageObject<{}, typeof regionDetailPageObject.elements>;
-
-const regionDetailPageObject = {
-	elements: {
-		division: "mat-select[name='division']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default regionDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionsTable.ts b/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionsTable.ts
deleted file mode 100644
index 8c481f5d56..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cacheGroups/regionsTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Regions table commands
- */
-type RegionsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Regions page.
- */
-export type RegionsPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<RegionsTableCommands>>;
-
-const regionsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		regionsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/regions`;
-	}
-};
-
-export default regionsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/cdns/cdnDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/cdns/cdnDetail.ts
deleted file mode 100644
index f33c90de0c..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/cdns/cdnDetail.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for CDN Detail.
- */
-export type CDNDetailPageObject = EnhancedPageObject<{}, typeof cdnDetailPageObject.elements>;
-
-const cdnDetailPageObject = {
-	elements: {
-		dnssecEnabled: "input[name='dnssecEnabled']",
-		domainName: "input[name='domainName']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default cdnDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/common.ts b/experimental/traffic-portal/nightwatch/page_objects/common.ts
deleted file mode 100644
index f757cf9728..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/common.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedElementInstance, EnhancedPageObject, EnhancedSectionInstance } from "nightwatch";
-
-/**
- * Defines the commands for the sidebar
- */
-interface SidebarCommands  extends EnhancedSectionInstance, EnhancedElementInstance<EnhancedPageObject> {
-	navigateToNode(node: string, path: Array<string>): Promise<void>;
-}
-
-/**
- * Defines the sidebar section
- */
-type SidebarSection = EnhancedSectionInstance<SidebarCommands, typeof commonPageObject.sections.sidebar.elements>;
-
-/**
- * Defines the type for the common PO
- */
-export type CommonPageObject = EnhancedPageObject<{}, typeof commonPageObject.elements, { sidebar: SidebarSection }>;
-
-const commonPageObject = {
-	elements: {
-		snackbarEle: {
-			selector: "simple-snack-bar"
-		}
-	},
-	sections: {
-		sidebar: {
-			commands: {
-				async navigateToNode(node: string, path: Array<string>): Promise<void> {
-					for (const pathNode of path) {
-						await this.click(`@${pathNode}`);
-					}
-					await this.click(`@${node}`);
-				}
-			} as SidebarCommands,
-			elements: {
-				asns: "[aria-label='Navigate to ASNs']",
-				cacheGroups: "[aria-label='Navigate to Cache Groups']",
-				cacheGroupsContainer: "[aria-label='Toggle Cache Groups']",
-				capabilities: "[aria-label='Navigate to Capabilities']",
-				changeLogs: "[aria-label='Navigate to Change Logs']",
-				configurationContainer: "[aria-label='Toggle Configuration']",
-				coordinates: "[aria-label='Navigate to Coordinates']",
-				dashboard: "[aria-label='Navigate to Dashboard']",
-				divisions: "[aria-label='Navigate to Divisions']",
-				otherContainer: "[aria-label='Toggle Other']",
-				parameters: "[aria-label='Navigate to Parameters']",
-				physicalLocations: "[aria-label='Navigate to Physical Locations']",
-				profile: "[aria-label='Navigate to My Profile']",
-				profiles: "[aria-label='Navigate to Profiles']",
-				regions: "[aria-label='Navigate to Regions']",
-				roles: "[aria-label='Navigate to Roles']",
-				servers: "[aria-label='Navigate to Servers']",
-				serversContainer: "[aria-label='Toggle Servers']",
-				statuses: "[aria-label='Navigate to Statuses']",
-				tenants: "[aria-label='Navigate to Tenants']",
-				types: "[aria-label='Navigate to Types']",
-				users: "[aria-label='Navigate to Users']",
-				usersContainer: "[aria-label='Toggle Users']"
-			},
-			selector: "#sidebar-nav-tree"
-		}
-	}
-};
-
-export default commonPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceCard.ts b/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceCard.ts
deleted file mode 100644
index 40abb4fe6c..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceCard.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import {
-	EnhancedElementInstance,
-	EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI
-} from "nightwatch";
-
-/**
- * Defines the commands for the loginForm section
- */
-interface DeliveryServiceCardCommands extends EnhancedSectionInstance, EnhancedElementInstance<EnhancedPageObject> {
-	expandDS(xmlId: string): Promise<boolean>;
-	viewDetails(xmlId: string): Promise<boolean>;
-}
-
-/**
- * Defines the loginForm section
- */
-type DeliveryServiceCardSection = EnhancedSectionInstance<DeliveryServiceCardCommands,
-	typeof deliveryServiceCardPageObject.sections.cards.elements>;
-
-/**
- * Define the type for our PO
- */
-export type DeliveryServiceCardPageObject = EnhancedPageObject<{}, {}, { cards: DeliveryServiceCardSection }>;
-
-const deliveryServiceCardPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		cards: {
-			commands: {
-				async expandDS(xmlId: string): Promise<boolean> {
-					return new Promise((resolve, reject) => {
-						this.click("css selector", `mat-card#${xmlId}`, result => {
-							if (result.status === 1) {
-								reject(new Error(`Unable to find by css mat-card#${xmlId}`));
-								return;
-							}
-							this.waitForElementVisible(`mat-card#${xmlId} mat-card-content > div`,
-								undefined, undefined, undefined, () => {
-									resolve(true);
-								});
-						});
-					});
-				},
-				async viewDetails(xmlId: string): Promise<boolean> {
-					await this.expandDS(xmlId);
-					return new Promise((resolve) => {
-						this.click("css selector", `mat-card#${xmlId} mat-card-actions > a`, () => {
-							browser.assert.urlContains("deliveryservice");
-							resolve(true);
-						});
-					});
-				}
-			} as DeliveryServiceCardCommands,
-			elements: {
-			},
-			selector: "article#deliveryservices"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core`;
-	}
-};
-
-export default deliveryServiceCardPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceDetail.ts
deleted file mode 100644
index 661846f3f7..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/deliveryServices/deliveryServiceDetail.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import {
-	EnhancedPageObject, EnhancedSectionInstance
-} from "nightwatch";
-
-/**
- * Define the type for our PO
- */
-export type DeliveryServiceDetailPageObject = EnhancedPageObject<{}, typeof deliveryServiceDetailPageObject.elements,
-{ dateInputForm: EnhancedSectionInstance<{}, typeof deliveryServiceDetailPageObject.sections.dateInputForm.elements> }>;
-
-const deliveryServiceDetailPageObject = {
-	elements: {
-		bandwidthChart: "canvas#bandwidthData",
-		invalidateJobs: "a#invalidate",
-		tpsChart: "canvas#tpsChartData",
-	},
-	sections: {
-		dateInputForm: {
-			elements: {
-				fromDate: "input[name='fromdate']",
-				fromTime: "input[name='fromtime']",
-				refreshBtn: "button[name='timespanRefresh']",
-				steeringIcon: "div.actions > mat-icon",
-				toDate: "input[name='todate']",
-				toTime: "input[name='totime']",
-			},
-			selector: "form[name='timespan']"
-		}
-	}
-};
-
-export default deliveryServiceDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/login.ts b/experimental/traffic-portal/nightwatch/page_objects/login.ts
deleted file mode 100644
index 1a598ba0c4..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/login.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import {
-	EnhancedElementInstance,
-	EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI
-} from "nightwatch";
-
-/**
- * Defines the commands for the loginForm section
- */
-interface LoginFormSectionCommands extends EnhancedSectionInstance, EnhancedElementInstance<EnhancedPageObject> {
-	fillOut(username: string, password: string): this;
-	login(username: string, password: string): this;
-	loginAndWait(username: string, password: string): this;
-}
-
-/**
- * Defines the loginForm section
- */
-type LoginFormSection = EnhancedSectionInstance<LoginFormSectionCommands, typeof loginPageObject.sections.loginForm.elements>;
-
-/**
- * Define the type for our PO
- */
-export type LoginPageObject = EnhancedPageObject<{}, {}, { loginForm: LoginFormSection }>;
-
-const loginPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		loginForm: {
-			commands: {
-				fillOut(username: string, password: string): LoginFormSectionCommands {
-					return this
-						.setValue("@usernameTxt", username)
-						.setValue("@passwordTxt", password);
-				},
-				login(username: string, password: string): LoginFormSectionCommands {
-					return this.fillOut(username, password)
-						.click("@loginBtn");
-				},
-				loginAndWait(username: string, password: string): LoginFormSectionCommands {
-					const ret = this.login(username, password);
-					browser.page.common()
-						.assert.textContains("@snackbarEle", "Success");
-					return ret;
-				}
-			} as LoginFormSectionCommands,
-			elements: {
-				clearBtn: "button[name='clear']",
-				loginBtn: "button[name='login']",
-				passwordTxt: "input[name='p']",
-				resetBtn: "button[name='reset']",
-				usernameTxt: "input[name='u']",
-			},
-			selector: "form[name='login']"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/login`;
-	}
-};
-
-export default loginPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/parameters/parameterDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/parameters/parameterDetail.ts
deleted file mode 100644
index 6e26bb0893..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/parameters/parameterDetail.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Parameter Details.
- */
-export type ParameterDetailPageObject = EnhancedPageObject<{}, typeof parameterDetailPageObject.elements>;
-
-const parameterDetailPageObject = {
-	elements: {
-		configFile: {
-			selector: "input[name='configFile']"
-		},
-		id: {
-			selector: "input[name='id']"
-		},
-		lastUpdated: {
-			selector: "input[name='lastUpdated']"
-		},
-		name: {
-			selector: "input[name='name']"
-		},
-		saveBtn: {
-			selector: "button[type='submit']"
-		},
-		secure: {
-			selector: "input[name='secure']"
-		},
-		value: {
-			selector: "textarea[name='value']"
-		}
-	},
-};
-
-export default parameterDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/parameters/parametersTable.ts b/experimental/traffic-portal/nightwatch/page_objects/parameters/parametersTable.ts
deleted file mode 100644
index ad8a64c27e..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/parameters/parametersTable.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Parameters table commands
- */
-type ParametersTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Parameters page.
- */
-export type ParametersPageObject = EnhancedPageObject<{}, {}, EnhancedSectionInstance<ParametersTableCommands>>;
-
-const parametersPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		parametersTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/parameters`;
-	}
-};
-
-export default parametersPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/profiles/profileDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/profiles/profileDetail.ts
deleted file mode 100644
index a8f5593c03..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/profiles/profileDetail.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Profile Details.
- */
-export type ProfileDetailPageObject = EnhancedPageObject<{}, typeof profileDetailPageObject.elements>;
-
-const profileDetailPageObject = {
-	elements: {
-		cdn: {
-			selector: "mat-select[name='cdn']"
-		},
-		description: {
-			selector: "textarea[name='description']"
-		},
-		id: {
-			selector: "input[name='id']"
-		},
-		lastUpdated: {
-			selector: "input[name='lastUpdated']"
-		},
-		name: {
-			selector: "input[name='name']"
-		},
-		routingDisabled: {
-			selector: "mat-select[name='routingDisabled']"
-		},
-		saveBtn: {
-			selector: "button[type='submit']"
-		},
-		type: {
-			selector: "mat-select[name='type']"
-		}
-	},
-};
-
-export default profileDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/profiles/profilesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/profiles/profilesTable.ts
deleted file mode 100644
index 60e36b9cb9..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/profiles/profilesTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Profiles table commands
- */
-type ProfileTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Profiles page.
- */
-export type ProfilePageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<ProfileTableCommands>>;
-
-const profilePageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		profileTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/profiles`;
-	}
-};
-
-export default profilePageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilitiesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilitiesTable.ts
deleted file mode 100644
index 3bf94cc7de..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilitiesTable.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../../globals/tables";
-
-/**
- * Defines the Capabilities table commands.
- */
-type CapabilitiesTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Capabilities page.
- */
-export type CapabilitiesPageObject = EnhancedPageObject<{}, {}, EnhancedSectionInstance<CapabilitiesTableCommands>>;
-
-const capabilitiesPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		capabilitiesTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/capabilities`;
-	}
-};
-
-export default capabilitiesPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilityDetails.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilityDetails.ts
deleted file mode 100644
index a1dfb5d621..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/capabilities/capabilityDetails.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import type { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Capability Details.
- */
-export type CapabilityDetailsPageObject = EnhancedPageObject<{}, typeof capabilityDetailsPageObject.elements>;
-
-const capabilityDetailsPageObject = {
-	elements: {
-		lastUpdated: {
-			selector: "input[name='lastUpdated']"
-		},
-		name: {
-			selector: "input[name='name']"
-		},
-		saveBtn: {
-			selector: "button[type='submit']"
-		},
-	},
-};
-
-export default capabilityDetailsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/physLocDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/physLocDetail.ts
deleted file mode 100644
index a1f3a0922e..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/physLocDetail.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for PhysLoc Details.
- */
-export type PhysLocDetailPageObject = EnhancedPageObject<{}, typeof physLocDetailPageObject.elements>;
-
-const physLocDetailPageObject = {
-	elements: {
-		address: "input[name='address']",
-		city: "input[name='city']",
-		comments: "textarea[name='comments']",
-		email: "input[name='email']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		phone: "input[name='phone']",
-		poc: "input[name='poc']",
-		region: "mat-select[name='region']",
-		saveBtn: "button[type='submit']",
-		shortName: "input[name='shortName']",
-		state: "input[name='state']",
-		zip: "input[name='postalCode']",
-	},
-};
-
-export default physLocDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/physLocTable.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/physLocTable.ts
deleted file mode 100644
index e2c520ce79..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/physLocTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the PhysLocs table commands
- */
-type PhysLocsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the PhysLocs page.
- */
-export type PhysLocTablePageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<PhysLocsTableCommands>>;
-
-const physLocsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		physLocsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/phys-locs`;
-	}
-};
-
-export default physLocsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/serversDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/serversDetail.ts
deleted file mode 100644
index 790b6c17c0..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/serversDetail.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance } from "nightwatch";
-
-/**
- * Defines the Section Instance for the Servers Details.
- */
-type ServersDetailSection = EnhancedSectionInstance<
-EnhancedSectionInstance<typeof serversDetailPageObject.sections.detailCard.commands,
-		typeof serversDetailPageObject.sections.detailCard.elements>>;
-
-/**
- * Defines the PageObject for Servers Details.
- */
-export type ServersDetailPageObject = EnhancedPageObject<{}, {}, { detailCard: ServersDetailSection}>;
-
-const serversDetailPageObject = {
-	sections: {
-		detailCard: {
-			commands: {
-			},
-			elements: {
-				cacheGroup: "mat-select[name='cachegroup']",
-				cdn: "mat-select[name='cdn']",
-				deleteBtn: "button[aria-label='Delete Server']",
-				domainName: "input[name='domainname']",
-				hostName: "input[name='hostname']",
-				httpPort: "input[name='httpport']",
-				httpsPort: "input[name='httpsport']",
-				id: "input[name='serverId']",
-				iloGateway: "input[name='iloGateway']",
-				iloIP: "input[name='iloIP']",
-				iloNetmask: "input[name='iloNetmask']",
-				iloPassword: "input[name='iloPassword']",
-				iloUsername: "input[name='iloUsername']",
-				intfAddBtn: "button[aria-label='Add An Interface']",
-				lastUpdated: "input[name='lastUpdated']",
-				mgmtGateway: "input[name='mgmtIpGateway']",
-				mgmtIP: "input[name='mgmtIP']",
-				mgmtNetmask: "input[name='mgmtIpNetmask']",
-				offlineReason: "mat-select[name='offlineReason']",
-				physLoc: "mat-select[name='physLocation']",
-				profileNames: "mat-select[name='profiles']",
-				rack: "input[name='rack']",
-				status: "mat-select[name='status']",
-				statusDisabled: "input[name='status']",
-				statusLastUpdated: "input[name='statusLastUpdated']",
-				submitBtn: "button[aria-label='Submit Server']",
-				type: "mat-select[name='type']"
-			},
-			selector: "mat-card.page-content"
-		}
-	}
-};
-
-export default serversDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/servers/serversTable.ts b/experimental/traffic-portal/nightwatch/page_objects/servers/serversTable.ts
deleted file mode 100644
index 5e3f2e93d6..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/servers/serversTable.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import {
-	EnhancedPageObject,
-	EnhancedSectionInstance,
-	NightwatchAPI
-} from "nightwatch";
-import { type ResponseServer } from "trafficops-types";
-
-import { TableSectionCommands, TABLE_COMMANDS } from "../../globals/tables";
-
-/**
- * Defines the commands for the servers table section.
- */
-interface ServersTableSectionCommands extends TableSectionCommands {
-	createNew(): Promise<void>;
-	openDetails(s: ResponseServer): Promise<void>;
-	open(): Promise<void>;
-}
-
-const serversPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		serversTable: {
-			commands: {
-				async createNew(): Promise<void> {
-					await this.open();
-					await browser.click("a.page-fab[routerLink='new']");
-				},
-				async open(): Promise<void> {
-					await browser.page.common()
-						.section.sidebar
-						.navigateToNode("servers", ["serversContainer"]);
-				},
-				async openDetails(server: ResponseServer): Promise<void> {
-					await this.open();
-					const table = browser.page.servers.serversTable().section.serversTable;
-					await table
-						.filterTableByColumn("Host", server.hostName);
-					await table.doubleClickRow(1);
-				},
-				...TABLE_COMMANDS
-			} as ServersTableSectionCommands,
-			elements: {
-			},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/servers`;
-	}
-};
-
-/**
- * Defines the servers table section.
- */
-type ServersTableSection = EnhancedSectionInstance<ServersTableSectionCommands, typeof serversPageObject.sections.serversTable.elements>;
-
-/**
- * The type of the servers table page object as provided by the Nightwatch API at
- * runtime.
- */
-export type ServersTablePageObject = EnhancedPageObject<{}, {}, { serversTable: ServersTableSection }>;
-
-export default serversPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts
deleted file mode 100644
index 78358cdcaa..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Status Details.
- */
-export type StatusDetailPageObject = EnhancedPageObject<{}, typeof statusDetailPageObject.elements>;
-
-const statusDetailPageObject = {
-	elements: {
-		description: "input[name='description']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default statusDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts
deleted file mode 100644
index 6072bd6bb3..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Statuses table commands
- */
-type StatusesTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Statuses page.
- */
-export type StatusesTablePageObject = EnhancedPageObject<{}, {}, EnhancedSectionInstance<StatusesTableCommands>>;
-
-const statusesTablePageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		statusesTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/statuses`;
-	}
-};
-
-export default statusesTablePageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/types/typeDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/types/typeDetail.ts
deleted file mode 100644
index 29383bf717..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/types/typeDetail.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Type Details.
- */
-export type TypeDetailPageObject = EnhancedPageObject<{}, typeof typeDetailPageObject.elements>;
-
-const typeDetailPageObject = {
-	elements: {
-		description: "input[name='description']",
-		id: "input[name='id']",
-		lastUpdated: "input[name='lastUpdated']",
-		name: "input[name='name']",
-		saveBtn: "button[type='submit']",
-		useInTable: "input[name='useInTable']",
-	},
-};
-
-export default typeDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/types/typesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/types/typesTable.ts
deleted file mode 100644
index a0cca5735a..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/types/typesTable.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Types table commands
- */
-type TypesTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Types page.
- */
-export type TypesPageObject = EnhancedPageObject<{}, {}, EnhancedSectionInstance<TypesTableCommands>>;
-
-const typesPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		typesTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/types`;
-	}
-};
-
-export default typesPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/changeLogs.ts b/experimental/traffic-portal/nightwatch/page_objects/users/changeLogs.ts
deleted file mode 100644
index 3f26ac2bdb..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/changeLogs.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines Change Logs Commands
- */
-type ChangeLogsTableSectionCommand = TableSectionCommands;
-
-const changeLogsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		changeLogsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			} as ChangeLogsTableSectionCommand,
-			elements: {
-			},
-			selector: "tp-change-logs mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/change-logs`;
-	}
-};
-
-/**
- * Defines the changeLogs table section.
- */
-type ChangeLogsTableSection = EnhancedSectionInstance<ChangeLogsTableSectionCommand,
-	typeof changeLogsPageObject.sections.changeLogsTable.elements>;
-
-/**
- * The type of the changeLogs table page object as provided by the Nightwatch API at
- * runtime.
- */
-export type ChangeLogsPageObject = EnhancedPageObject<{}, {}, { changeLogsTable: ChangeLogsTableSection }>;
-
-export default changeLogsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts
deleted file mode 100644
index 7ba2499d7b..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/roleDetail.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Role Details.
- */
-export type RoleDetailPageObject = EnhancedPageObject<{}, typeof roleDetailPageObject.elements>;
-
-const roleDetailPageObject = {
-	elements: {
-		description: {
-			selector: "input[name='description']"
-		},
-		name: {
-			selector: "input[name='name']"
-		},
-		permissions: {
-			selector: "textarea[name='permissions']"
-		},
-		saveBtn: {
-			selector: "button[type='submit']"
-		}
-	},
-};
-
-export default roleDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/rolesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/users/rolesTable.ts
deleted file mode 100644
index b66168b151..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/rolesTable.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Roles table commands
- */
-type RolesTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Roles page.
- */
-export type RolesPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<RolesTableCommands>>;
-
-const rolesPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		rolesTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/roles`;
-	}
-};
-
-export default rolesPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/tenantDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/users/tenantDetail.ts
deleted file mode 100644
index 3ea46f7eef..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/tenantDetail.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject } from "nightwatch";
-
-/**
- * Defines the PageObject for Tenant Details.
- */
-export type TenantDetailPageObject = EnhancedPageObject<{}, typeof tenantDetailPageObject.elements>;
-
-const tenantDetailPageObject = {
-	elements: {
-		active: "input[name='active']",
-		name: "input[name='name']",
-		parent: "input[name='parentTenant-tree-select']",
-		saveBtn: "button[type='submit']",
-	},
-};
-
-export default tenantDetailPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/tenants.ts b/experimental/traffic-portal/nightwatch/page_objects/users/tenants.ts
deleted file mode 100644
index 71ad93f5b4..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/tenants.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch";
-
-import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables";
-
-/**
- * Defines the Tenants table commands
- */
-type TenantsTableCommands = TableSectionCommands;
-
-/**
- * Defines the Page Object for the Tenants page.
- */
-export type TenantsPageObject = EnhancedPageObject<{}, {},
-EnhancedSectionInstance<TenantsTableCommands>>;
-
-const tenantsPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		tenantsTable: {
-			commands: {
-				...TABLE_COMMANDS
-			},
-			elements: {},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/tenants`;
-	}
-};
-
-export default tenantsPageObject;
diff --git a/experimental/traffic-portal/nightwatch/page_objects/users/users.ts b/experimental/traffic-portal/nightwatch/page_objects/users/users.ts
deleted file mode 100644
index 40403e9116..0000000000
--- a/experimental/traffic-portal/nightwatch/page_objects/users/users.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import {
-	EnhancedPageObject,
-	EnhancedSectionInstance,
-	NightwatchAPI
-} from "nightwatch";
-
-import { TableSectionCommands, TABLE_COMMANDS } from "../../globals/tables";
-
-/**
- * Defines the commands for the users table section.
- */
-type UsersTableSectionCommands = TableSectionCommands;
-
-const usersPageObject = {
-	api: {} as NightwatchAPI,
-	sections: {
-		usersTable: {
-			commands: {
-				...TABLE_COMMANDS
-			} as UsersTableSectionCommands,
-			elements: {
-			},
-			selector: "mat-card"
-		}
-	},
-	url(): string {
-		return `${this.api.launchUrl}/core/users`;
-	}
-};
-
-/**
- * Defines the users table section.
- */
-type UsersTableSection = EnhancedSectionInstance<UsersTableSectionCommands, typeof usersPageObject.sections.usersTable.elements>;
-
-/**
- * The type of the users table page object as provided by the Nightwatch API at
- * runtime.
- */
-export type UsersPageObject = EnhancedPageObject<{}, {}, { usersTable: UsersTableSection }>;
-
-export default usersPageObject;
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/detail.spec.ts
deleted file mode 100644
index aa1f6a20bf..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/detail.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Asn Detail Spec", () => {
-	it("Test Asn", () => {
-		const page = browser.page.cacheGroups.asnDetail();
-		browser.url(`${page.api.launchUrl}/core/asns/${browser.globals.testData.asn.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@asn")
-				.assert.enabled("@cachegroup")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@asn", String(browser.globals.testData.asn.asn))
-				.assert.valueEquals("@id", String(browser.globals.testData.asn.id));
-		});
-	});
-
-	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");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/table.spec.ts
deleted file mode 100644
index b7f3273735..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/asns/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("ASNs Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("asns", ["serversContainer", "cacheGroupsContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/detail.spec.ts
deleted file mode 100644
index a82be9dd4d..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/detail.spec.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Coordinate Detail Spec", () => {
-	it("Test coordinate", () => {
-		const page = browser.page.cacheGroups.coordinateDetail();
-		browser.url(`${page.api.launchUrl}/core/coordinates/${browser.globals.testData.coordinate.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@latitude")
-				.assert.enabled("@longitude")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@latitude", String(browser.globals.testData.coordinate.latitude))
-				.assert.valueEquals("@longitude", String(browser.globals.testData.coordinate.longitude))
-				.assert.valueEquals("@name", browser.globals.testData.coordinate.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.coordinate.id));
-		});
-	});
-
-	it("New coordinate", () => {
-		const page = browser.page.cacheGroups.coordinateDetail();
-		browser.url(`${page.api.launchUrl}/core/coordinates/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@latitude")
-				.assert.enabled("@longitude")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@latitude", String(browser.globals.testData.coordinate.latitude))
-				.assert.valueEquals("@longitude", String(browser.globals.testData.coordinate.longitude))
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/table.spec.ts
deleted file mode 100644
index 289ee2e6a7..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/coordinates/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Coordinates Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("coordinates", ["serversContainer", "cacheGroupsContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/detail.spec.ts
deleted file mode 100644
index 417bae9778..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/detail.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Cache Group Detail Spec", () => {
-	it("Test Cache Group", () => {
-		const page = browser.page.cacheGroups.cacheGroupDetails();
-		browser.url(`${page.api.launchUrl}/core/cache-groups/${browser.globals.testData.cacheGroup.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.cacheGroup.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.cacheGroup.id));
-		});
-	});
-
-	it("New Cache Group", () => {
-		const page = browser.page.cacheGroups.cacheGroupDetails();
-		browser.url(`${page.api.launchUrl}/core/cache-groups/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/detail.spec.ts
deleted file mode 100644
index 98ef7fff3a..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/detail.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Division Detail Spec", () => {
-	it("Test division", () => {
-		const page = browser.page.cacheGroups.divisionDetail();
-		browser.url(`${page.api.launchUrl}/core/divisions/${browser.globals.testData.division.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.division.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.division.id));
-		});
-	});
-
-	it("New division", () => {
-		const page = browser.page.cacheGroups.divisionDetail();
-		browser.url(`${page.api.launchUrl}/core/divisions/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/table.spec.ts
deleted file mode 100644
index 224fc749c0..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/divisions/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Divisions Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("divisions", ["serversContainer", "cacheGroupsContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/detail.spec.ts
deleted file mode 100644
index cd58223adf..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/detail.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Region Detail Spec", () => {
-	it("Test region", () => {
-		const page = browser.page.cacheGroups.regionDetail();
-		browser.url(`${page.api.launchUrl}/core/regions/${browser.globals.testData.region.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@division")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.region.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.region.id));
-		});
-	});
-
-	it("New region", () => {
-		const page = browser.page.cacheGroups.regionDetail();
-		browser.url(`${page.api.launchUrl}/core/regions/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@division")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/table.spec.ts
deleted file mode 100644
index db7c468de4..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/regions/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Regions Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("regions", ["serversContainer", "cacheGroupsContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 3);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cacheGroups/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/cacheGroups/table.spec.ts
deleted file mode 100644
index 1cd9817a6a..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cacheGroups/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Cache Groups Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("cacheGroups", ["serversContainer", "cacheGroupsContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/cdns/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/cdns/detail.spec.ts
deleted file mode 100644
index 6031c8eb15..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/cdns/detail.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("CDN Detail Spec", () => {
-	it("Test CDN", () => {
-		const page = browser.page.cdns.cdnDetail();
-		browser.url(`${page.api.launchUrl}/core/cdns/${browser.globals.testData.cdn.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.cdn.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.cdn.id));
-		});
-	});
-
-	it("New CDN", () => {
-		const page = browser.page.cdns.cdnDetail();
-		browser.url(`${page.api.launchUrl}/core/cdns/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.card.spec.ts b/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.card.spec.ts
deleted file mode 100644
index 61c7893ae8..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.card.spec.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("DS Card Spec", () => {
-	it("Verify expand test", async (): Promise<void> => {
-		await browser.page.common()
-			.section.sidebar
-			.click("@dashboard");
-		await browser.page.deliveryServices.deliveryServiceCard()
-			.section.cards
-			.expandDS(`testDS${browser.globals.uniqueString}`);
-	});
-
-	it("Verify detail test", async (): Promise<void> => {
-		await browser.page.common()
-			.section.sidebar
-			.click("@dashboard");
-		await browser.page.deliveryServices.deliveryServiceCard()
-			.section.cards
-			.viewDetails(`testDS${browser.globals.uniqueString}`);
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.details.spec.ts b/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.details.spec.ts
deleted file mode 100644
index 8aefdfe27e..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.details.spec.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-describe("DS Detail Spec", () => {
-	beforeEach(() => {
-		browser.page.deliveryServices.deliveryServiceCard()
-			.navigate()
-			.section.cards
-			.viewDetails(browser.globals.testData.ds.xmlId);
-	});
-
-	it("Verify page test", (): void => {
-		const page = browser.page.deliveryServices.deliveryServiceDetail();
-		page.assert.visible("@bandwidthChart")
-			.assert.visible("@tpsChart")
-			.assert.enabled("@invalidateJobs");
-
-		page.section.dateInputForm
-			.assert.enabled("@fromDate")
-			.assert.enabled("@fromTime")
-			.assert.enabled("@toDate")
-			.assert.enabled("@toTime")
-			.assert.enabled("@refreshBtn");
-	});
-
-	it("Default values test", (): void => {
-		const page = browser.page.deliveryServices.deliveryServiceDetail();
-		const now = new Date();
-		const nowString = now.toISOString();
-		const date = nowString.split("T")[0];
-		let time = nowString.split("T")[1].substring(0, 5);
-		time = `${(+time.split(":")[0] - now.getTimezoneOffset()/60).toString().padStart(2, "0")}:${time.split(":")[1]}`;
-
-		page.section.dateInputForm
-			.assert.valueEquals("@fromDate", date)
-			.assert.valueEquals("@fromTime", "00:00")
-			.assert.valueEquals("@toDate", date)
-			.assert.valueEquals("@toTime", time);
-	});
-
-	it("Is steering target test", (): void => {
-		let page = browser.page.deliveryServices.deliveryServiceDetail();
-		page.section.dateInputForm
-			.assert.visible("@steeringIcon");
-
-		browser.page.deliveryServices.deliveryServiceCard()
-			.navigate()
-			.section.cards
-			.viewDetails(browser.globals.testData.steeringDS.xmlId);
-		page = browser.page.deliveryServices.deliveryServiceDetail();
-		page.section.dateInputForm
-			.assert.not.visible("@steeringIcon");
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts b/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
deleted file mode 100644
index 3a755dedc4..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("DS Invalidation Jobs Spec", () => {
-	beforeEach(() => {
-		browser.page.deliveryServices.deliveryServiceCard()
-			.navigate()
-			.section.cards
-			.viewDetails(`testDS${browser.globals.uniqueString}`);
-		browser.page.deliveryServices.deliveryServiceDetail()
-			.click("@invalidateJobs")
-			.assert.urlContains("invalidation-jobs");
-	});
-
-	it("Verify page", () => {
-		browser.page.deliveryServices.deliveryServiceInvalidationJobs()
-			.assert.enabled("@addButton");
-	});
-
-	it("Manage Job", async () => {
-		const page = browser.page.deliveryServices.deliveryServiceInvalidationJobs();
-		const common = browser.page.common();
-		await page
-			.click("@addButton");
-		const startDate = new Date();
-		startDate.setDate(startDate.getDate() + 1);
-		await browser.waitForElementVisible("tp-new-invalidation-job-dialog")
-			.assert.valueEquals("input[name='startDate']", startDate.toLocaleDateString())
-			.setValue("input[name='regexp']", "/invalidateMe")
-			.click("button[type=submit]");
-		await common
-			.assert.textContains("@snackbarEle", "created")
-			.click("simple-snack-bar button");
-		await page.assert.visible({index: 0, selector: "div.invalidation-job"})
-			.assert.enabled({index: 0, selector: "div.invalidation-job button"})
-			.assert.enabled({index: 1, selector: "div.invalidation-job button"});
-		await page
-			.click({index: 0, selector: "div.invalidation-job button"});
-		await browser.waitForElementVisible("tp-new-invalidation-job-dialog")
-			.assert.valueEquals("input[name='startDate']", startDate.toLocaleDateString())
-			.assert.valueEquals("input[name='regexp']", "invalidateMe")
-			.setValue("input[name='regexp']", "/invalidateMe2")
-			.click("button[type=submit]");
-		await common
-			.assert.textContains("@snackbarEle", "created")
-			.click("simple-snack-bar button");
-		await page
-			.click({index: 1, selector: "div.invalidation-job button"});
-		await common
-			.assert.textContains("@snackbarEle", "was deleted");
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/login/login.spec.ts b/experimental/traffic-portal/nightwatch/tests/login/login.spec.ts
deleted file mode 100644
index 781fcec46f..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/login/login.spec.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-describe("Login Spec", () => {
-
-	it("Clear form test", () => {
-		browser.page.login()
-			.navigate().section.loginForm
-			.fillOut("test", "asdf")
-			.click("@clearBtn")
-			.assert.textContains("@usernameTxt", "")
-			.assert.textContains("@passwordTxt", "");
-	});
-	it("Incorrect password test", () => {
-		browser.page.login()
-			.navigate().section.loginForm
-			.login("test", "asdf")
-			.assert.valueEquals("@usernameTxt", "test")
-			.assert.valueEquals("@passwordTxt", "asdf");
-		browser.page.common()
-			.assert.textContains("@snackbarEle", "Invalid");
-	});
-	it("Login test", () => {
-		browser.page.login()
-			.navigate()
-			.section.loginForm
-			.loginAndWait(browser.globals.adminUser, browser.globals.adminPass);
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/parameters/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/parameters/detail.spec.ts
deleted file mode 100644
index bf0a67b741..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/parameters/detail.spec.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Parameter Detail Spec", () => {
-	it("Test parameter", () => {
-		const page = browser.page.parameters.parameterDetail();
-		browser.url(`${page.api.launchUrl}/core/parameters/${browser.globals.testData.parameter.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			console.log(res.value);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@configFile")
-				.assert.enabled("@value")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.parameter.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.parameter.id));
-		});
-	});
-
-	it("New parameter", () => {
-		const page = browser.page.parameters.parameterDetail();
-		browser.url(`${page.api.launchUrl}/core/parameters/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@configFile")
-				.assert.enabled("@value")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/parameters/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/parameters/table.spec.ts
deleted file mode 100644
index 6a5126282e..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/parameters/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Parameters Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("parameters", ["configurationContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/profiles/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/profiles/detail.spec.ts
deleted file mode 100644
index 4c1faf0c5b..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/profiles/detail.spec.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Profile Detail Spec", () => {
-	it("Test Profile", () => {
-		const page = browser.page.profiles.profileDetail();
-		browser.url(`${page.api.launchUrl}/core/profiles/${browser.globals.testData.profile.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@cdn")
-				.assert.enabled("@type")
-				.assert.enabled("@routingDisabled")
-				.assert.enabled("@description")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.enabled("@saveBtn")
-				.assert.valueEquals("@name", browser.globals.testData.profile.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.profile.id));
-		});
-	});
-
-	it("New Profile", () => {
-		const page = browser.page.profiles.profileDetail();
-		browser.url(`${page.api.launchUrl}/core/profiles/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@cdn")
-				.assert.enabled("@type")
-				.assert.enabled("@routingDisabled")
-				.assert.enabled("@description")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.enabled("@saveBtn")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/profiles/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/profiles/table.spec.ts
deleted file mode 100644
index f7a2e78e30..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/profiles/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Profiles Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("profiles", ["configurationContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 1);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/capabilities/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/servers/capabilities/detail.spec.ts
deleted file mode 100644
index cd1ae41540..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/servers/capabilities/detail.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Capability Details Spec", () => {
-	it("Test capability", () => {
-		const page = browser.page.servers.capabilities.capabilityDetails();
-		browser.url(`${page.api.launchUrl}/core/capabilities/${browser.globals.testData.capability.name}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("form")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.capability.name);
-		});
-	});
-
-	it("New capability", () => {
-		const page = browser.page.servers.capabilities.capabilityDetails();
-		browser.url(`${page.api.launchUrl}/core/new-capability`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/capabilities/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/servers/capabilities/table.spec.ts
deleted file mode 100644
index 245de9ef74..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/servers/capabilities/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Capabilities Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("capabilities", ["serversContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/physLoc/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/servers/physLoc/detail.spec.ts
deleted file mode 100644
index a43a6d85f9..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/servers/physLoc/detail.spec.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Phys Location Detail Spec", () => {
-	it("Test physLoc", () => {
-		const page = browser.page.servers.physLocDetail();
-		browser.url(`${page.api.launchUrl}/core/phys-locs/${browser.globals.testData.physLoc.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("form")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.physLoc.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.physLoc.id))
-				.assert.valueEquals("@address", browser.globals.testData.physLoc.address)
-				.assert.valueEquals("@city", browser.globals.testData.physLoc.city)
-				.assert.valueEquals("@comments", browser.globals.testData.physLoc.comments ?? "")
-				.assert.valueEquals("@email", browser.globals.testData.physLoc.email ?? "")
-				.assert.valueEquals("@poc", browser.globals.testData.physLoc.poc ?? "")
-				.assert.valueEquals("@shortName", browser.globals.testData.physLoc.shortName)
-				.assert.valueEquals("@state", browser.globals.testData.physLoc.state)
-				.assert.valueEquals("@zip", browser.globals.testData.physLoc.zip)
-				.assert.valueEquals("@phone", browser.globals.testData.physLoc.phone ?? "")
-				.assert.textEquals("@region", String(browser.globals.testData.physLoc.region));
-		});
-	});
-
-	it("New physLoc", () => {
-		const page = browser.page.servers.physLocDetail();
-		browser.url(`${page.api.launchUrl}/core/phys-locs/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/physLoc/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/servers/physLoc/table.spec.ts
deleted file mode 100644
index 466f9b7b2f..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/servers/physLoc/table.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("PhysLocs Spec", () => {
-	it("Loads elements", async () => {
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("physicalLocations", ["serversContainer"]);
-		await browser.waitForElementPresent("input[name=fuzzControl]");
-		await browser.elements("css selector", "div.ag-row", rows => {
-			browser.assert.ok(rows.status === 0);
-			browser.assert.ok((rows.value as []).length >= 2);
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/servers/servers.detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/servers/servers.detail.spec.ts
deleted file mode 100644
index c526f7708a..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/servers/servers.detail.spec.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Servers Detail Spec", () => {
-	it("New server loads correctly", async () => {
-		await browser.page.servers.serversTable()
-			.section.serversTable
-			.createNew();
-		await browser.assert.urlContains("core/servers/new");
-		const page = browser.page.servers.serversDetail();
-		await page.section.detailCard
-			.assert.enabled("@hostName")
-			.assert.enabled("@cdn")
-			.assert.enabled("@cacheGroup")
-			.assert.enabled("@physLoc")
-			.assert.enabled("@status")
-			.assert.not.elementPresent("@offlineReason")
-			.assert.enabled("@type")
-			.assert.enabled("@httpPort")
-			.assert.enabled("@httpsPort")
-			.assert.enabled("@rack")
-			.assert.not.elementPresent("@id")
-			.assert.not.elementPresent("@lastUpdated")
-			.assert.not.elementPresent("@statusLastUpdated")
-			.assert.enabled("@profileNames")
-			.assert.enabled("@intfAddBtn")
-			.assert.enabled("@iloIP")
-			.assert.enabled("@iloGateway")
-			.assert.enabled("@iloNetmask")
-			.assert.enabled("@iloUsername")
-			.assert.enabled("@iloPassword")
-			.assert.enabled("@mgmtIP")
-			.assert.enabled("@mgmtGateway")
-			.assert.enabled("@mgmtNetmask")
-			.assert.not.elementPresent("@deleteBtn")
-			.assert.enabled("@submitBtn");
-	});
-
-	it("Fields are loaded correctly", async () => {
-		await browser.page.servers.serversTable()
-			.section.serversTable
-			.openDetails(browser.globals.testData.edgeServer);
-		await browser.assert.urlContains(`core/servers/${  browser.globals.testData.edgeServer.id}`);
-		const page = browser.page.servers.serversDetail();
-		await page.section.detailCard
-			.assert.enabled("@hostName")
-			.assert.valueEquals("@hostName", browser.globals.testData.edgeServer.hostName)
-			.assert.enabled("@cdn")
-			.assert.textEquals("@cdn", browser.globals.testData.edgeServer.cdnName)
-			.assert.enabled("@cacheGroup")
-			.assert.textEquals("@cacheGroup", browser.globals.testData.edgeServer.cachegroup)
-			.assert.enabled("@physLoc")
-			.assert.textEquals("@physLoc", browser.globals.testData.edgeServer.physLocation)
-			.assert.not.enabled("@statusDisabled")
-			.assert.valueEquals("@statusDisabled", browser.globals.testData.edgeServer.status)
-			.assert.not.elementPresent("@offlineReason")
-			.assert.enabled("@type")
-			.assert.textEquals("@type", browser.globals.testData.edgeServer.type)
-			.assert.enabled("@httpPort")
-			.assert.enabled("@httpsPort")
-			.assert.textEquals("@httpsPort", String(browser.globals.testData.edgeServer.httpsPort ?? ""))
-			.assert.enabled("@rack")
-			.assert.textEquals("@rack", browser.globals.testData.edgeServer.rack ?? "")
-			.assert.not.enabled("@id")
-			.assert.valueEquals("@id", String(browser.globals.testData.edgeServer.id))
-			.assert.not.enabled("@lastUpdated")
-			.assert.not.elementPresent("@statusLastUpdated")
-			.assert.enabled("@profileNames")
-			.assert.textEquals("@profileNames", browser.globals.testData.edgeServer.profileNames[0])
-			.assert.enabled("@intfAddBtn")
-			.assert.enabled("@iloIP")
-			.assert.valueEquals("@iloIP", browser.globals.testData.edgeServer.iloIpAddress ?? "")
-			.assert.enabled("@iloGateway")
-			.assert.valueEquals("@iloGateway", browser.globals.testData.edgeServer.iloIpGateway ?? "" )
-			.assert.enabled("@iloNetmask")
-			.assert.valueEquals("@iloNetmask", browser.globals.testData.edgeServer.iloIpNetmask ?? "")
-			.assert.enabled("@iloUsername")
-			.assert.valueEquals("@iloUsername", browser.globals.testData.edgeServer.iloUsername ?? "")
-			.assert.enabled("@iloPassword")
-			.assert.valueEquals("@iloPassword", browser.globals.testData.edgeServer.iloPassword ?? "")
-			.assert.enabled("@mgmtIP")
-			.assert.valueEquals("@mgmtIP", browser.globals.testData.edgeServer.mgmtIpAddress ?? "")
-			.assert.enabled("@mgmtGateway")
-			.assert.valueEquals("@mgmtGateway", browser.globals.testData.edgeServer.mgmtIpGateway ?? "")
-			.assert.enabled("@mgmtNetmask")
-			.assert.valueEquals("@mgmtNetmask", browser.globals.testData.edgeServer.mgmtIpNetmask ?? "")
-			.assert.enabled("@deleteBtn")
-			.assert.enabled("@submitBtn");
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts
deleted file mode 100644
index 65203fbf84..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Status Detail Spec", () => {
-	it("Test status", () => {
-		const page = browser.page.statuses.statusDetail();
-		browser.url(`${page.api.launchUrl}/core/statuses/${browser.globals.testData.statuses.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@description")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.statuses.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.statuses.id));
-		});
-	});
-
-	it("New Status", () => {
-		const page = browser.page.statuses.statusDetail();
-		browser.url(`${page.api.launchUrl}/core/statuses/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@description")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/types/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/types/detail.spec.ts
deleted file mode 100644
index 884c810061..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/types/detail.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Type Detail Spec", () => {
-	it("Test type", () => {
-		const page = browser.page.types.typeDetail();
-		browser.url(`${page.api.launchUrl}/core/types/${browser.globals.testData.type.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@description")
-				.assert.not.enabled("@useInTable")
-				.assert.enabled("@saveBtn")
-				.assert.not.enabled("@id")
-				.assert.not.enabled("@lastUpdated")
-				.assert.valueEquals("@name", browser.globals.testData.type.name)
-				.assert.valueEquals("@id", String(browser.globals.testData.type.id));
-		});
-	});
-
-	it("New type", () => {
-		const page = browser.page.types.typeDetail();
-		browser.url(`${page.api.launchUrl}/core/types/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@name")
-				.assert.enabled("@description")
-				.assert.not.enabled("@useInTable")
-				.assert.enabled("@saveBtn")
-				.assert.not.elementPresent("@id")
-				.assert.not.elementPresent("@lastUpdated")
-				.assert.valueEquals("@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
deleted file mode 100644
index 3bec78a984..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/users/role/detail.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Role Detail Spec", () => {
-	it("Test Role", () => {
-		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("@description")
-				.assert.enabled("@name")
-				.assert.not.hasAttribute("@permissions", "readonly")
-				.assert.enabled("@saveBtn")
-				.assert.valueEquals("@name", String(browser.globals.testData.role.name))
-				.assert.valueEquals("@permissions", String(browser.globals.testData.role.permissions));
-		});
-	});
-
-	it("New Role", () => {
-		const page = browser.page.users.roleDetail();
-		browser.url(`${page.api.launchUrl}/core/new-role`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@description")
-				.assert.enabled("@name")
-				.assert.not.hasAttribute("@permissions", "readonly")
-				.assert.enabled("@saveBtn")
-				.assert.valueEquals("@name", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/tenant/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/users/tenant/detail.spec.ts
deleted file mode 100644
index c3e8a2e44f..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/users/tenant/detail.spec.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-describe("Tenant Detail Spec", () => {
-	it("Root tenant", () => {
-		const page = browser.page.users.tenantDetail();
-		browser.url(`${page.api.launchUrl}/core/tenants/1`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.not.enabled("@active")
-				.assert.not.enabled("@name")
-				.assert.not.enabled("@parent")
-				.assert.not.enabled("@saveBtn")
-				.assert.valueEquals("@name", "root")
-				.assert.valueEquals("@active", "on");
-		});
-	});
-
-	it("Test tenant", () => {
-		const page = browser.page.users.tenantDetail();
-		browser.url(`${page.api.launchUrl}/core/tenants/${browser.globals.testData.tenant.id}`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@active")
-				.assert.enabled("@name")
-				.assert.enabled("@parent")
-				.assert.enabled("@saveBtn");
-		});
-	});
-
-	it("New tenant", () => {
-		const page = browser.page.users.tenantDetail();
-		browser.url(`${page.api.launchUrl}/core/tenants/new`, res => {
-			browser.assert.ok(res.status === 0);
-			page.waitForElementVisible("mat-card")
-				.assert.enabled("@active")
-				.assert.enabled("@name")
-				.assert.enabled("@parent")
-				.assert.enabled("@saveBtn")
-				.assert.textContains("@name", "")
-				.assert.valueEquals("@active", "on")
-				.assert.valueEquals("@parent", "");
-		});
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tests/users/users.spec.ts b/experimental/traffic-portal/nightwatch/tests/users/users.spec.ts
deleted file mode 100644
index 8f701f88b4..0000000000
--- a/experimental/traffic-portal/nightwatch/tests/users/users.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import type { UsersPageObject } from "nightwatch/page_objects/users/users";
-
-describe("Users Spec", () => {
-	it("Filter by username", async () => {
-		const username = browser.globals.adminUser;
-
-		await browser.page.common()
-			.section.sidebar
-			.navigateToNode("users", ["usersContainer"]);
-		const page: UsersPageObject = browser.page.users.users();
-		await browser.waitForElementPresent(".ag-row");
-		let tbl = page.section.usersTable;
-		if (! await tbl.getColumnState("Username")) {
-			await tbl.toggleColumn("Username");
-		}
-
-		tbl = tbl.searchText(username);
-		await page.assert.urlContains(`search=${username}`);
-
-		await tbl.api.elements("css selector", ".ag-row:not(.ag-hidden .ag-row)",
-			result => {
-				if (result.status === 1) {
-					browser.assert.equal(true, false, `failed to select ag-grid rows: ${result.value.message}`);
-					return;
-				}
-				browser.assert.equal(result.value.length, 1);
-			}
-		);
-	});
-});
diff --git a/experimental/traffic-portal/nightwatch/tsconfig.e2e.json b/experimental/traffic-portal/nightwatch/tsconfig.e2e.json
deleted file mode 100644
index 2da491a4a7..0000000000
--- a/experimental/traffic-portal/nightwatch/tsconfig.e2e.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-	"extends": "../tsconfig.json",
-	"compilerOptions": {
-		"outDir": "../dist/nightwatch",
-		"module": "commonjs",
-		"resolveJsonModule": true,
-		"target": "es5",
-		"types": [
-			"node",
-			"nightwatch"
-		]
-	},
-}
diff --git a/experimental/traffic-portal/package-lock.json b/experimental/traffic-portal/package-lock.json
index 67dc5fe570..6d41abd827 100644
--- a/experimental/traffic-portal/package-lock.json
+++ b/experimental/traffic-portal/package-lock.json
@@ -1,7 +1,7 @@
 {
   "name": "traffic-portal",
   "version": "0.5.0",
-  "lockfileVersion": 2,
+  "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
@@ -35,6 +35,7 @@
       },
       "devDependencies": {
         "@angular-builders/custom-webpack": "^16.0.0",
+        "@angular-devkit/architect": "^0.1600.6",
         "@angular-devkit/build-angular": "^16.0.4",
         "@angular-eslint/builder": "^16.0.3",
         "@angular-eslint/eslint-plugin": "^16.0.3",
@@ -43,13 +44,12 @@
         "@angular-eslint/template-parser": "^16.0.3",
         "@angular/cli": "^16.0.4",
         "@angular/compiler-cli": "^16.0.4",
-        "@nightwatch/schematics": "^1.0.0",
+        "@cypress/schematic": "^2.5.1",
         "@types/argparse": "^2.0.2",
         "@types/chart.js": "^2.9.34",
         "@types/express": "^4.17.0",
         "@types/jasmine": "~3.6.0",
         "@types/jasminewd2": "~2.0.3",
-        "@types/nightwatch": "^2.3.22",
         "@types/node": "^16.18.11",
         "@types/node-forge": "^1.3.2",
         "@typescript-eslint/eslint-plugin": "^5.59.2",
@@ -57,6 +57,7 @@
         "axios": "^0.27.2",
         "chromedriver": "^119.0.1",
         "codelyzer": "^6.0.0",
+        "cypress": "^13.6.2",
         "eslint": "^8.39.0",
         "eslint-plugin-import": "^2.25.3",
         "eslint-plugin-jsdoc": "^37.0.3",
@@ -69,7 +70,6 @@
         "karma-jasmine": "~4.0.0",
         "karma-jasmine-html-reporter": "^1.5.0",
         "karma-junit-reporter": "^2.0.1",
-        "nightwatch": "^2.6.17",
         "ts-node": "~8.3.0",
         "typescript": "^4.9.5"
       },
@@ -121,12 +121,12 @@
       }
     },
     "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/architect": {
-      "version": "0.1601.0",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1601.0.tgz",
-      "integrity": "sha512-lrO++pcB+NFGXLZrFBhRMPbGCMpZuJyJEKSK8zknw9/7ipRz1MSlRaJFWUKEHRlVI/+hsBTWtBRUnR5WcgqvvA==",
+      "version": "0.1602.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.11.tgz",
+      "integrity": "sha512-qC1tPL/82gxqCS1z9pTpLn5NQH6uqbV6UNjbkFEQpTwEyWEK6VLChAJsybHHfbpssPS2HWf31VoUzX7RqDjoQQ==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.1.0",
+        "@angular-devkit/core": "16.2.11",
         "rxjs": "7.8.1"
       },
       "engines": {
@@ -135,157 +135,41 @@
         "yarn": ">= 1.13.0"
       }
     },
-    "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/core": {
-      "version": "16.1.0",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.1.0.tgz",
-      "integrity": "sha512-mrWpuDvttmhrCGcLc68RIXKtTzUhkBTsE5ZZFZNO1+FSC+vO/ZpyCpPd6C+6coM68NfXYjHlms5XF6KbxeGn/Q==",
-      "dev": true,
-      "dependencies": {
-        "ajv": "8.12.0",
-        "ajv-formats": "2.1.1",
-        "jsonc-parser": "3.2.0",
-        "rxjs": "7.8.1",
-        "source-map": "0.7.4"
-      },
-      "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
-      },
-      "peerDependencies": {
-        "chokidar": "^3.5.2"
-      },
-      "peerDependenciesMeta": {
-        "chokidar": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@angular-builders/custom-webpack/node_modules/diff": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
-      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.3.1"
-      }
-    },
-    "node_modules/@angular-builders/custom-webpack/node_modules/rxjs": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
-      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.1.0"
-      }
-    },
-    "node_modules/@angular-builders/custom-webpack/node_modules/ts-node": {
-      "version": "10.9.1",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
-      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
-      "dev": true,
-      "dependencies": {
-        "@cspotcode/source-map-support": "^0.8.0",
-        "@tsconfig/node10": "^1.0.7",
-        "@tsconfig/node12": "^1.0.7",
-        "@tsconfig/node14": "^1.0.0",
-        "@tsconfig/node16": "^1.0.2",
-        "acorn": "^8.4.1",
-        "acorn-walk": "^8.1.1",
-        "arg": "^4.1.0",
-        "create-require": "^1.1.0",
-        "diff": "^4.0.1",
-        "make-error": "^1.1.1",
-        "v8-compile-cache-lib": "^3.0.1",
-        "yn": "3.1.1"
-      },
-      "bin": {
-        "ts-node": "dist/bin.js",
-        "ts-node-cwd": "dist/bin-cwd.js",
-        "ts-node-esm": "dist/bin-esm.js",
-        "ts-node-script": "dist/bin-script.js",
-        "ts-node-transpile-only": "dist/bin-transpile.js",
-        "ts-script": "dist/bin-script-deprecated.js"
-      },
-      "peerDependencies": {
-        "@swc/core": ">=1.2.50",
-        "@swc/wasm": ">=1.2.50",
-        "@types/node": "*",
-        "typescript": ">=2.7"
-      },
-      "peerDependenciesMeta": {
-        "@swc/core": {
-          "optional": true
-        },
-        "@swc/wasm": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@angular-builders/custom-webpack/node_modules/tsconfig-paths": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
-      "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
-      "dev": true,
-      "dependencies": {
-        "json5": "^2.2.2",
-        "minimist": "^1.2.6",
-        "strip-bom": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/@angular-devkit/architect": {
-      "version": "0.1502.8",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.8.tgz",
-      "integrity": "sha512-rTltw2ABHrcKc8EGimALvXmrDTP5hlNbEy6nYolJoXEI9EwHgriWrVLVPs3OEF+/ed47dbJi9EGOXUOgzgpB5A==",
-      "dev": true,
-      "dependencies": {
-        "@angular-devkit/core": "15.2.8",
-        "rxjs": "6.6.7"
-      },
-      "engines": {
-        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
-      }
-    },
-    "node_modules/@angular-devkit/build-angular": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.0.4.tgz",
-      "integrity": "sha512-avNUNuLFSJOJ52z4Yk/5yyMVlMdpqbhPqW+RdinYrBRjkJZoscndV1bshrLZQHmVvoUpJes0mLXa1u+NRhqd3Q==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/build-angular": {
+      "version": "16.2.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.11.tgz",
+      "integrity": "sha512-yNzUiAeg1WHMsFG9IBg4S/7dsMcEAMYQ1I360ib80c0T/IwRb8pHhOokrl5Mu8zfNqZ/dxH4ItKY1uIMDmuMGQ==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "2.2.1",
-        "@angular-devkit/architect": "0.1600.4",
-        "@angular-devkit/build-webpack": "0.1600.4",
-        "@angular-devkit/core": "16.0.4",
-        "@babel/core": "7.21.4",
-        "@babel/generator": "7.21.4",
-        "@babel/helper-annotate-as-pure": "7.18.6",
-        "@babel/helper-split-export-declaration": "7.18.6",
+        "@angular-devkit/architect": "0.1602.11",
+        "@angular-devkit/build-webpack": "0.1602.11",
+        "@angular-devkit/core": "16.2.11",
+        "@babel/core": "7.22.9",
+        "@babel/generator": "7.22.9",
+        "@babel/helper-annotate-as-pure": "7.22.5",
+        "@babel/helper-split-export-declaration": "7.22.6",
         "@babel/plugin-proposal-async-generator-functions": "7.20.7",
-        "@babel/plugin-transform-async-to-generator": "7.20.7",
-        "@babel/plugin-transform-runtime": "7.21.4",
-        "@babel/preset-env": "7.21.4",
-        "@babel/runtime": "7.21.0",
-        "@babel/template": "7.20.7",
+        "@babel/plugin-transform-async-to-generator": "7.22.5",
+        "@babel/plugin-transform-runtime": "7.22.9",
+        "@babel/preset-env": "7.22.9",
+        "@babel/runtime": "7.22.6",
+        "@babel/template": "7.22.5",
         "@discoveryjs/json-ext": "0.5.7",
-        "@ngtools/webpack": "16.0.4",
+        "@ngtools/webpack": "16.2.11",
         "@vitejs/plugin-basic-ssl": "1.0.1",
         "ansi-colors": "4.1.3",
         "autoprefixer": "10.4.14",
-        "babel-loader": "9.1.2",
+        "babel-loader": "9.1.3",
         "babel-plugin-istanbul": "6.1.1",
-        "browserslist": "4.21.5",
-        "cacache": "17.0.6",
+        "browserslist": "^4.21.5",
         "chokidar": "3.5.3",
         "copy-webpack-plugin": "11.0.0",
-        "critters": "0.0.16",
-        "css-loader": "6.7.3",
-        "esbuild-wasm": "0.17.18",
-        "glob": "8.1.0",
+        "critters": "0.0.20",
+        "css-loader": "6.8.1",
+        "esbuild-wasm": "0.18.17",
+        "fast-glob": "3.3.1",
+        "guess-parser": "0.4.22",
         "https-proxy-agent": "5.0.1",
         "inquirer": "8.2.4",
         "jsonc-parser": "3.2.0",
@@ -294,32 +178,32 @@
         "less-loader": "11.1.0",
         "license-webpack-plugin": "4.0.2",
         "loader-utils": "3.2.1",
-        "magic-string": "0.30.0",
-        "mini-css-extract-plugin": "2.7.5",
+        "magic-string": "0.30.1",
+        "mini-css-extract-plugin": "2.7.6",
         "mrmime": "1.0.1",
         "open": "8.4.2",
         "ora": "5.4.1",
         "parse5-html-rewriting-stream": "7.0.0",
         "picomatch": "2.3.1",
-        "piscina": "3.2.0",
-        "postcss": "8.4.23",
-        "postcss-loader": "7.2.4",
+        "piscina": "4.0.0",
+        "postcss": "8.4.31",
+        "postcss-loader": "7.3.3",
         "resolve-url-loader": "5.0.0",
         "rxjs": "7.8.1",
-        "sass": "1.62.1",
-        "sass-loader": "13.2.2",
-        "semver": "7.4.0",
+        "sass": "1.64.1",
+        "sass-loader": "13.3.2",
+        "semver": "7.5.4",
         "source-map-loader": "4.0.1",
         "source-map-support": "0.5.21",
-        "terser": "5.17.1",
+        "terser": "5.19.2",
         "text-table": "0.2.0",
         "tree-kill": "1.2.2",
-        "tslib": "2.5.0",
-        "vite": "4.3.1",
-        "webpack": "5.80.0",
-        "webpack-dev-middleware": "6.0.2",
-        "webpack-dev-server": "4.13.2",
-        "webpack-merge": "5.8.0",
+        "tslib": "2.6.1",
+        "vite": "4.5.1",
+        "webpack": "5.88.2",
+        "webpack-dev-middleware": "6.1.1",
+        "webpack-dev-server": "4.15.1",
+        "webpack-merge": "5.9.0",
         "webpack-subresource-integrity": "5.1.0"
       },
       "engines": {
@@ -328,7 +212,7 @@
         "yarn": ">= 1.13.0"
       },
       "optionalDependencies": {
-        "esbuild": "0.17.18"
+        "esbuild": "0.18.17"
       },
       "peerDependencies": {
         "@angular/compiler-cli": "^16.0.0",
@@ -341,7 +225,7 @@
         "ng-packagr": "^16.0.0",
         "protractor": "^7.0.0",
         "tailwindcss": "^2.0.0 || ^3.0.0",
-        "typescript": ">=4.9.3 <5.1"
+        "typescript": ">=4.9.3 <5.2"
       },
       "peerDependenciesMeta": {
         "@angular/localize": {
@@ -373,30 +257,35 @@
         }
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": {
-      "version": "0.1600.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.4.tgz",
-      "integrity": "sha512-wW35EURt4QQhOTuQoMQ91Y9Jt7Fm4fs7KCUIQpkGisVu3P79TRgWyXUKv5IBXzLMGC3aJDv25zbLRchP+4Y7uA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/build-webpack": {
+      "version": "0.1602.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.11.tgz",
+      "integrity": "sha512-2Au6xRMxNugFkXP0LS1TwNE5gAfGW4g6yxC9P5j5p3kdGDnAVaZRTOKB9dg73i3uXtJHUMciYOThV0b78XRxwA==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.0.4",
+        "@angular-devkit/architect": "0.1602.11",
         "rxjs": "7.8.1"
       },
       "engines": {
         "node": "^16.14.0 || >=18.10.0",
         "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
         "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "webpack": "^5.30.0",
+        "webpack-dev-server": "^4.0.0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.4.tgz",
-      "integrity": "sha512-RK8+W+BVQcD2RHyQcy4iKSIFO1BexVUU2htu3+A9SY7VN/szoMgHIP/sx7Pj+LIJfDrvyve57aIOv0KPWk3WOg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/core": {
+      "version": "16.2.11",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.11.tgz",
+      "integrity": "sha512-u3cEQHqhSMWyAFIaPdRukCJwEUJt7Fy3C02gTlTeCB4F/OnftVFIm2e5vmCqMo9rgbfdvjWj9V+7wWiCpMrzAQ==",
       "dev": true,
       "dependencies": {
         "ajv": "8.12.0",
         "ajv-formats": "2.1.1",
         "jsonc-parser": "3.2.0",
+        "picomatch": "2.3.1",
         "rxjs": "7.8.1",
         "source-map": "0.7.4"
       },
@@ -414,27 +303,27 @@
         }
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": {
-      "version": "7.21.4",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
-      "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/core": {
+      "version": "7.22.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
+      "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.21.4",
-        "@babel/generator": "^7.21.4",
-        "@babel/helper-compilation-targets": "^7.21.4",
-        "@babel/helper-module-transforms": "^7.21.2",
-        "@babel/helpers": "^7.21.0",
-        "@babel/parser": "^7.21.4",
-        "@babel/template": "^7.20.7",
-        "@babel/traverse": "^7.21.4",
-        "@babel/types": "^7.21.4",
+        "@babel/code-frame": "^7.22.5",
+        "@babel/generator": "^7.22.9",
+        "@babel/helper-compilation-targets": "^7.22.9",
+        "@babel/helper-module-transforms": "^7.22.9",
+        "@babel/helpers": "^7.22.6",
+        "@babel/parser": "^7.22.7",
+        "@babel/template": "^7.22.5",
+        "@babel/traverse": "^7.22.8",
+        "@babel/types": "^7.22.5",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
         "json5": "^2.2.2",
-        "semver": "^6.3.0"
+        "semver": "^6.3.1"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -444,7 +333,7 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": {
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/core/node_modules/semver": {
       "version": "6.3.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
       "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
@@ -453,10985 +342,11416 @@
         "semver": "bin/semver.js"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/cosmiconfig": {
-      "version": "8.2.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
-      "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/generator": {
+      "version": "7.22.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
+      "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
       "dev": true,
       "dependencies": {
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.0",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0"
+        "@babel/types": "^7.22.5",
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "@jridgewell/trace-mapping": "^0.3.17",
+        "jsesc": "^2.5.1"
       },
       "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/d-fischer"
+        "node": ">=6.9.0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/cosmiconfig-typescript-loader": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz",
-      "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.22.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
+      "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
       "dev": true,
-      "engines": {
-        "node": ">=12",
-        "npm": ">=6"
+      "dependencies": {
+        "@babel/types": "^7.22.5"
       },
-      "peerDependencies": {
-        "@types/node": "*",
-        "cosmiconfig": ">=7",
-        "ts-node": ">=10",
-        "typescript": ">=3"
-      }
-    },
-    "node_modules/@angular-devkit/build-angular/node_modules/diff": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
-      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
-      "dev": true,
-      "peer": true,
       "engines": {
-        "node": ">=0.3.1"
+        "node": ">=6.9.0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/helper-define-polyfill-provider": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz",
+      "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==",
       "dev": true,
       "dependencies": {
-        "argparse": "^2.0.1"
+        "@babel/helper-compilation-targets": "^7.22.6",
+        "@babel/helper-plugin-utils": "^7.22.5",
+        "debug": "^4.1.1",
+        "lodash.debounce": "^4.0.8",
+        "resolve": "^1.14.2"
       },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
+      "peerDependencies": {
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.22.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+      "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
       "dev": true,
       "dependencies": {
-        "yallist": "^4.0.0"
+        "@babel/types": "^7.22.5"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=6.9.0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/magic-string": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
-      "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/plugin-proposal-private-property-in-object": {
+      "version": "7.21.0-placeholder-for-preset-env.2",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+      "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
       "dev": true,
-      "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.4.13"
-      },
       "engines": {
-        "node": ">=12"
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/postcss-loader": {
-      "version": "7.2.4",
-      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.2.4.tgz",
-      "integrity": "sha512-F88rpxxNspo5hatIc+orYwZDtHFaVFOSIVAx+fBfJC1GmhWbVmPWtmg2gXKE1OxJbneOSGn8PWdIwsZFcruS+w==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/plugin-transform-async-to-generator": {
+      "version": "7.22.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz",
+      "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==",
       "dev": true,
       "dependencies": {
-        "cosmiconfig": "^8.1.3",
-        "cosmiconfig-typescript-loader": "^4.3.0",
-        "klona": "^2.0.6",
-        "semver": "^7.3.8"
+        "@babel/helper-module-imports": "^7.22.5",
+        "@babel/helper-plugin-utils": "^7.22.5",
+        "@babel/helper-remap-async-to-generator": "^7.22.5"
       },
       "engines": {
-        "node": ">= 14.15.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
+        "node": ">=6.9.0"
       },
       "peerDependencies": {
-        "postcss": "^7.0.0 || ^8.0.1",
-        "ts-node": ">=10",
-        "typescript": ">=4",
-        "webpack": "^5.0.0"
-      },
-      "peerDependenciesMeta": {
-        "ts-node": {
-          "optional": true
-        },
-        "typescript": {
-          "optional": true
-        }
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/postcss-loader/node_modules/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/plugin-transform-runtime": {
+      "version": "7.22.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz",
+      "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==",
       "dev": true,
       "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
-      "bin": {
-        "semver": "bin/semver.js"
+        "@babel/helper-module-imports": "^7.22.5",
+        "@babel/helper-plugin-utils": "^7.22.5",
+        "babel-plugin-polyfill-corejs2": "^0.4.4",
+        "babel-plugin-polyfill-corejs3": "^0.8.2",
+        "babel-plugin-polyfill-regenerator": "^0.5.1",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
-      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
       "dev": true,
-      "dependencies": {
-        "tslib": "^2.1.0"
+      "bin": {
+        "semver": "bin/semver.js"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/ts-node": {
-      "version": "10.9.1",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
-      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/preset-env": {
+      "version": "7.22.9",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz",
+      "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "@cspotcode/source-map-support": "^0.8.0",
-        "@tsconfig/node10": "^1.0.7",
-        "@tsconfig/node12": "^1.0.7",
-        "@tsconfig/node14": "^1.0.0",
-        "@tsconfig/node16": "^1.0.2",
-        "acorn": "^8.4.1",
-        "acorn-walk": "^8.1.1",
-        "arg": "^4.1.0",
-        "create-require": "^1.1.0",
-        "diff": "^4.0.1",
-        "make-error": "^1.1.1",
-        "v8-compile-cache-lib": "^3.0.1",
-        "yn": "3.1.1"
+        "@babel/compat-data": "^7.22.9",
+        "@babel/helper-compilation-targets": "^7.22.9",
+        "@babel/helper-plugin-utils": "^7.22.5",
+        "@babel/helper-validator-option": "^7.22.5",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5",
+        "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-class-properties": "^7.12.13",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+        "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+        "@babel/plugin-syntax-import-assertions": "^7.22.5",
+        "@babel/plugin-syntax-import-attributes": "^7.22.5",
+        "@babel/plugin-syntax-import-meta": "^7.10.4",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+        "@babel/plugin-syntax-top-level-await": "^7.14.5",
+        "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+        "@babel/plugin-transform-arrow-functions": "^7.22.5",
+        "@babel/plugin-transform-async-generator-functions": "^7.22.7",
+        "@babel/plugin-transform-async-to-generator": "^7.22.5",
+        "@babel/plugin-transform-block-scoped-functions": "^7.22.5",
+        "@babel/plugin-transform-block-scoping": "^7.22.5",
+        "@babel/plugin-transform-class-properties": "^7.22.5",
+        "@babel/plugin-transform-class-static-block": "^7.22.5",
+        "@babel/plugin-transform-classes": "^7.22.6",
+        "@babel/plugin-transform-computed-properties": "^7.22.5",
+        "@babel/plugin-transform-destructuring": "^7.22.5",
+        "@babel/plugin-transform-dotall-regex": "^7.22.5",
+        "@babel/plugin-transform-duplicate-keys": "^7.22.5",
+        "@babel/plugin-transform-dynamic-import": "^7.22.5",
+        "@babel/plugin-transform-exponentiation-operator": "^7.22.5",
+        "@babel/plugin-transform-export-namespace-from": "^7.22.5",
+        "@babel/plugin-transform-for-of": "^7.22.5",
+        "@babel/plugin-transform-function-name": "^7.22.5",
+        "@babel/plugin-transform-json-strings": "^7.22.5",
+        "@babel/plugin-transform-literals": "^7.22.5",
+        "@babel/plugin-transform-logical-assignment-operators": "^7.22.5",
+        "@babel/plugin-transform-member-expression-literals": "^7.22.5",
+        "@babel/plugin-transform-modules-amd": "^7.22.5",
+        "@babel/plugin-transform-modules-commonjs": "^7.22.5",
+        "@babel/plugin-transform-modules-systemjs": "^7.22.5",
+        "@babel/plugin-transform-modules-umd": "^7.22.5",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5",
+        "@babel/plugin-transform-new-target": "^7.22.5",
+        "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5",
+        "@babel/plugin-transform-numeric-separator": "^7.22.5",
+        "@babel/plugin-transform-object-rest-spread": "^7.22.5",
+        "@babel/plugin-transform-object-super": "^7.22.5",
+        "@babel/plugin-transform-optional-catch-binding": "^7.22.5",
+        "@babel/plugin-transform-optional-chaining": "^7.22.6",
+        "@babel/plugin-transform-parameters": "^7.22.5",
+        "@babel/plugin-transform-private-methods": "^7.22.5",
+        "@babel/plugin-transform-private-property-in-object": "^7.22.5",
+        "@babel/plugin-transform-property-literals": "^7.22.5",
+        "@babel/plugin-transform-regenerator": "^7.22.5",
+        "@babel/plugin-transform-reserved-words": "^7.22.5",
+        "@babel/plugin-transform-shorthand-properties": "^7.22.5",
+        "@babel/plugin-transform-spread": "^7.22.5",
+        "@babel/plugin-transform-sticky-regex": "^7.22.5",
+        "@babel/plugin-transform-template-literals": "^7.22.5",
+        "@babel/plugin-transform-typeof-symbol": "^7.22.5",
+        "@babel/plugin-transform-unicode-escapes": "^7.22.5",
+        "@babel/plugin-transform-unicode-property-regex": "^7.22.5",
+        "@babel/plugin-transform-unicode-regex": "^7.22.5",
+        "@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
+        "@babel/preset-modules": "^0.1.5",
+        "@babel/types": "^7.22.5",
+        "babel-plugin-polyfill-corejs2": "^0.4.4",
+        "babel-plugin-polyfill-corejs3": "^0.8.2",
+        "babel-plugin-polyfill-regenerator": "^0.5.1",
+        "core-js-compat": "^3.31.0",
+        "semver": "^6.3.1"
       },
-      "bin": {
-        "ts-node": "dist/bin.js",
-        "ts-node-cwd": "dist/bin-cwd.js",
-        "ts-node-esm": "dist/bin-esm.js",
-        "ts-node-script": "dist/bin-script.js",
-        "ts-node-transpile-only": "dist/bin-transpile.js",
-        "ts-script": "dist/bin-script-deprecated.js"
+      "engines": {
+        "node": ">=6.9.0"
       },
       "peerDependencies": {
-        "@swc/core": ">=1.2.50",
-        "@swc/wasm": ">=1.2.50",
-        "@types/node": "*",
-        "typescript": ">=2.7"
-      },
-      "peerDependenciesMeta": {
-        "@swc/core": {
-          "optional": true
-        },
-        "@swc/wasm": {
-          "optional": true
-        }
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@angular-devkit/build-angular/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/preset-env/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
     },
-    "node_modules/@angular-devkit/build-webpack": {
-      "version": "0.1600.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1600.4.tgz",
-      "integrity": "sha512-MBUeVpZOTGGwT088mH2gEUF0w+8vrtZN1vQL1HLKHZoKEiuGBvixv+nQemUcN7dWJdOt1T+Q/UzPo5BD1u7kvw==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/runtime": {
+      "version": "7.22.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
+      "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/architect": "0.1600.4",
-        "rxjs": "7.8.1"
+        "regenerator-runtime": "^0.13.11"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
-      },
-      "peerDependencies": {
-        "webpack": "^5.30.0",
-        "webpack-dev-server": "^4.0.0"
+        "node": ">=6.9.0"
       }
     },
-    "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": {
-      "version": "0.1600.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.4.tgz",
-      "integrity": "sha512-wW35EURt4QQhOTuQoMQ91Y9Jt7Fm4fs7KCUIQpkGisVu3P79TRgWyXUKv5IBXzLMGC3aJDv25zbLRchP+4Y7uA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@babel/template": {
+      "version": "7.22.5",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
+      "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.0.4",
-        "rxjs": "7.8.1"
+        "@babel/code-frame": "^7.22.5",
+        "@babel/parser": "^7.22.5",
+        "@babel/types": "^7.22.5"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": ">=6.9.0"
       }
     },
-    "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.4.tgz",
-      "integrity": "sha512-RK8+W+BVQcD2RHyQcy4iKSIFO1BexVUU2htu3+A9SY7VN/szoMgHIP/sx7Pj+LIJfDrvyve57aIOv0KPWk3WOg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/@esbuild/linux-x64": {
+      "version": "0.18.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz",
+      "integrity": "sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/@ngtools/webpack": {
+      "version": "16.2.11",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.11.tgz",
+      "integrity": "sha512-4ndXJ4s94ZsryVGSDk/waIDrUqXqdGWftoOEn81Zu+nkL9ncI/G1fNUlSJ5OqeKmMLxMFouoy+BuJfvT+gEgnQ==",
       "dev": true,
-      "dependencies": {
-        "ajv": "8.12.0",
-        "ajv-formats": "2.1.1",
-        "jsonc-parser": "3.2.0",
-        "rxjs": "7.8.1",
-        "source-map": "0.7.4"
-      },
       "engines": {
         "node": "^16.14.0 || >=18.10.0",
         "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
         "yarn": ">= 1.13.0"
       },
       "peerDependencies": {
-        "chokidar": "^3.5.2"
-      },
-      "peerDependenciesMeta": {
-        "chokidar": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
-      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.1.0"
+        "@angular/compiler-cli": "^16.0.0",
+        "typescript": ">=4.9.3 <5.2",
+        "webpack": "^5.54.0"
       }
     },
-    "node_modules/@angular-devkit/core": {
-      "version": "15.2.8",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.8.tgz",
-      "integrity": "sha512-Lo4XrbDMtXarKnMrFgWLmQdSX+3QPNAg4otG8cmp/U4jJyjV4dAYKEAsb1sCNGUSM4h4v09EQU/5ugVjDU29lQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
       "dependencies": {
-        "ajv": "8.12.0",
-        "ajv-formats": "2.1.1",
-        "jsonc-parser": "3.2.0",
-        "rxjs": "6.6.7",
-        "source-map": "0.7.4"
+        "color-convert": "^2.0.1"
       },
       "engines": {
-        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
-      },
-      "peerDependencies": {
-        "chokidar": "^3.5.2"
+        "node": ">=8"
       },
-      "peerDependenciesMeta": {
-        "chokidar": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/@angular-devkit/schematics": {
-      "version": "15.2.8",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.8.tgz",
-      "integrity": "sha512-w6EUGC96kVsH9f8sEzajzbONMawezyVBiSo+JYp5r25rQArAz/a+KZntbuETWHQ0rQOEsKmUNKxwmr11BaptSQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/babel-loader": {
+      "version": "9.1.3",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz",
+      "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "15.2.8",
-        "jsonc-parser": "3.2.0",
-        "magic-string": "0.29.0",
-        "ora": "5.4.1",
-        "rxjs": "6.6.7"
+        "find-cache-dir": "^4.0.0",
+        "schema-utils": "^4.0.0"
       },
       "engines": {
-        "node": "^14.20.0 || ^16.13.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": ">= 14.15.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.12.0",
+        "webpack": ">=5"
       }
     },
-    "node_modules/@angular-eslint/builder": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-16.0.3.tgz",
-      "integrity": "sha512-pv/CrnOHHOnBqhyBmqUPsIHKXOHYMJztxYJ83tjxeXL5Moyu5e6CBMIQ58UtqmgWfEIA3n7owYy9KvHTJcemyQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/babel-plugin-polyfill-corejs2": {
+      "version": "0.4.7",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz",
+      "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==",
       "dev": true,
       "dependencies": {
-        "@nx/devkit": "16.2.2",
-        "nx": "16.2.2"
+        "@babel/compat-data": "^7.22.6",
+        "@babel/helper-define-polyfill-provider": "^0.4.4",
+        "semver": "^6.3.1"
       },
       "peerDependencies": {
-        "eslint": "^7.20.0 || ^8.0.0",
-        "typescript": "*"
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
-    "node_modules/@angular-eslint/bundled-angular-compiler": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-16.0.3.tgz",
-      "integrity": "sha512-8zwY6ustiPXBEF3+jELKVwGk6j2HJn7GHbqAhDFR02YiE27iRMSGTHIAWGs6ZI7F1JgfrIsOHrUgzC1x95K6rg==",
-      "dev": true
+    "node_modules/@angular-builders/custom-webpack/node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
     },
-    "node_modules/@angular-eslint/eslint-plugin": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-16.0.3.tgz",
-      "integrity": "sha512-1c+dFytcQDOA2wJ8/rtydMV6UYq1BgVfOcBXOr0WJxC9g8Cad9czcUOkW41WGrTp5kICMliV0ypH5eEaCM2WDQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/babel-plugin-polyfill-corejs3": {
+      "version": "0.8.7",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz",
+      "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==",
       "dev": true,
       "dependencies": {
-        "@angular-eslint/utils": "16.0.3",
-        "@typescript-eslint/utils": "5.59.7"
+        "@babel/helper-define-polyfill-provider": "^0.4.4",
+        "core-js-compat": "^3.33.1"
       },
       "peerDependencies": {
-        "eslint": "^7.20.0 || ^8.0.0",
-        "typescript": "*"
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
-    "node_modules/@angular-eslint/eslint-plugin-template": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-16.0.3.tgz",
-      "integrity": "sha512-OKTMWOjC7F5tdv7gm2tlmgyr/uVyS1RWJZn4X/6D6p0kOpiDXmajtbYHD5tzbshX2Ep62Nt+rg8+1XGHrU0ScA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/babel-plugin-polyfill-regenerator": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz",
+      "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==",
       "dev": true,
       "dependencies": {
-        "@angular-eslint/bundled-angular-compiler": "16.0.3",
-        "@angular-eslint/utils": "16.0.3",
-        "@typescript-eslint/type-utils": "5.59.7",
-        "@typescript-eslint/utils": "5.59.7",
-        "aria-query": "5.1.3",
-        "axobject-query": "3.1.1"
+        "@babel/helper-define-polyfill-provider": "^0.4.4"
       },
       "peerDependencies": {
-        "eslint": "^7.20.0 || ^8.0.0",
-        "typescript": "*"
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
-    "node_modules/@angular-eslint/schematics": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-16.0.3.tgz",
-      "integrity": "sha512-vRdSY0ovE+wfTvYeguPp/QAxvGejLADO8CzJkas0PxdCQiyLuTscKsYE82XcvX2kitMexvH71lNF0ggnGoMRXA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
       "dependencies": {
-        "@angular-eslint/eslint-plugin": "16.0.3",
-        "@angular-eslint/eslint-plugin-template": "16.0.3",
-        "@nx/devkit": "16.2.2",
-        "ignore": "5.2.4",
-        "nx": "16.2.2",
-        "strip-json-comments": "3.1.1",
-        "tmp": "0.2.1"
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
       },
-      "peerDependencies": {
-        "@angular/cli": ">= 16.0.0 < 17.0.0"
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "node_modules/@angular-eslint/template-parser": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-16.0.3.tgz",
-      "integrity": "sha512-IAWdwp/S9QC3EMiVxSS0E3ABy9PSidN3PW0Ll2EtM3mzXMYlpZXmxqd+B1xV/xKWzhk1Mp04QX8hHfG6Vq+qaQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
       "dev": true,
       "dependencies": {
-        "@angular-eslint/bundled-angular-compiler": "16.0.3",
-        "eslint-scope": "^7.0.0"
+        "color-name": "~1.1.4"
       },
-      "peerDependencies": {
-        "eslint": "^7.20.0 || ^8.0.0",
-        "typescript": "*"
+      "engines": {
+        "node": ">=7.0.0"
       }
     },
-    "node_modules/@angular-eslint/utils": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-16.0.3.tgz",
-      "integrity": "sha512-QsbUVHJLk+fE08/D4y3wOyGk1iX2LVSygw+uzilbaAXfjD5/c0Ei5FbVx2mMYPk+aOl4yrvGQW3dmetMiAR0MQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/critters": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz",
+      "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==",
       "dev": true,
       "dependencies": {
-        "@angular-eslint/bundled-angular-compiler": "16.0.3",
-        "@typescript-eslint/utils": "5.59.7"
-      },
-      "peerDependencies": {
-        "eslint": "^7.20.0 || ^8.0.0",
-        "typescript": "*"
+        "chalk": "^4.1.0",
+        "css-select": "^5.1.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.2",
+        "htmlparser2": "^8.0.2",
+        "postcss": "^8.4.23",
+        "pretty-bytes": "^5.3.0"
       }
     },
-    "node_modules/@angular/animations": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.0.4.tgz",
-      "integrity": "sha512-M0C21mav4gtzWrWf6zTAYImL+Vix0bEDM/FF9ktmm04yt3Y8VtcMeDNRQRLj6JTIbth/fyvKZ20HSYFD91ez2g==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/css-loader": {
+      "version": "6.8.1",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz",
+      "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "icss-utils": "^5.1.0",
+        "postcss": "^8.4.21",
+        "postcss-modules-extract-imports": "^3.0.0",
+        "postcss-modules-local-by-default": "^4.0.3",
+        "postcss-modules-scope": "^3.0.0",
+        "postcss-modules-values": "^4.0.0",
+        "postcss-value-parser": "^4.2.0",
+        "semver": "^7.3.8"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "@angular/core": "16.0.4"
+        "webpack": "^5.0.0"
       }
     },
-    "node_modules/@angular/cdk": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.0.3.tgz",
-      "integrity": "sha512-KQCKWWNiHuYmzAVSiEyVs1P+859uhlvnmUH2azoL3qUHUcvdyB+iYqv9rTVRJl4N8nOpOYrj4JP3zmvNZLsI1Q==",
-      "dependencies": {
-        "tslib": "^2.3.0"
-      },
-      "optionalDependencies": {
-        "parse5": "^7.1.2"
-      },
-      "peerDependencies": {
-        "@angular/common": "^16.0.0 || ^17.0.0",
-        "@angular/core": "^16.0.0 || ^17.0.0",
-        "rxjs": "^6.5.3 || ^7.4.0"
+    "node_modules/@angular-builders/custom-webpack/node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
       }
     },
-    "node_modules/@angular/cli": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.0.4.tgz",
-      "integrity": "sha512-6+gULiKzETlB/iTjhDBAnDYY7od34kKyFQE9X/p2DejslMtRweuj7/A8RueVpTR7fIDkWH1aBeZgqIWjj/8eVw==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/esbuild": {
+      "version": "0.18.17",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.17.tgz",
+      "integrity": "sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==",
       "dev": true,
-      "dependencies": {
-        "@angular-devkit/architect": "0.1600.4",
-        "@angular-devkit/core": "16.0.4",
-        "@angular-devkit/schematics": "16.0.4",
-        "@schematics/angular": "16.0.4",
-        "@yarnpkg/lockfile": "1.1.0",
-        "ansi-colors": "4.1.3",
-        "ini": "4.0.0",
-        "inquirer": "8.2.4",
-        "jsonc-parser": "3.2.0",
-        "npm-package-arg": "10.1.0",
-        "npm-pick-manifest": "8.0.1",
-        "open": "8.4.2",
-        "ora": "5.4.1",
-        "pacote": "15.1.3",
-        "resolve": "1.22.2",
-        "semver": "7.4.0",
-        "symbol-observable": "4.0.0",
-        "yargs": "17.7.2"
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
       },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.18.17",
+        "@esbuild/android-arm64": "0.18.17",
+        "@esbuild/android-x64": "0.18.17",
+        "@esbuild/darwin-arm64": "0.18.17",
+        "@esbuild/darwin-x64": "0.18.17",
+        "@esbuild/freebsd-arm64": "0.18.17",
+        "@esbuild/freebsd-x64": "0.18.17",
+        "@esbuild/linux-arm": "0.18.17",
+        "@esbuild/linux-arm64": "0.18.17",
+        "@esbuild/linux-ia32": "0.18.17",
+        "@esbuild/linux-loong64": "0.18.17",
+        "@esbuild/linux-mips64el": "0.18.17",
+        "@esbuild/linux-ppc64": "0.18.17",
+        "@esbuild/linux-riscv64": "0.18.17",
+        "@esbuild/linux-s390x": "0.18.17",
+        "@esbuild/linux-x64": "0.18.17",
+        "@esbuild/netbsd-x64": "0.18.17",
+        "@esbuild/openbsd-x64": "0.18.17",
+        "@esbuild/sunos-x64": "0.18.17",
+        "@esbuild/win32-arm64": "0.18.17",
+        "@esbuild/win32-ia32": "0.18.17",
+        "@esbuild/win32-x64": "0.18.17"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/esbuild-wasm": {
+      "version": "0.18.17",
+      "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.18.17.tgz",
+      "integrity": "sha512-9OHGcuRzy+I8ziF9FzjfKLWAPbvi0e/metACVg9k6bK+SI4FFxeV6PcZsz8RIVaMD4YNehw+qj6UMR3+qj/EuQ==",
+      "dev": true,
       "bin": {
-        "ng": "bin/ng.js"
+        "esbuild": "bin/esbuild"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": ">=12"
       }
     },
-    "node_modules/@angular/cli/node_modules/@angular-devkit/architect": {
-      "version": "0.1600.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.4.tgz",
-      "integrity": "sha512-wW35EURt4QQhOTuQoMQ91Y9Jt7Fm4fs7KCUIQpkGisVu3P79TRgWyXUKv5IBXzLMGC3aJDv25zbLRchP+4Y7uA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.0.4",
-        "rxjs": "7.8.1"
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": ">=8.0.0"
       }
     },
-    "node_modules/@angular/cli/node_modules/@angular-devkit/core": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.4.tgz",
-      "integrity": "sha512-RK8+W+BVQcD2RHyQcy4iKSIFO1BexVUU2htu3+A9SY7VN/szoMgHIP/sx7Pj+LIJfDrvyve57aIOv0KPWk3WOg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/find-cache-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz",
+      "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==",
       "dev": true,
       "dependencies": {
-        "ajv": "8.12.0",
-        "ajv-formats": "2.1.1",
-        "jsonc-parser": "3.2.0",
-        "rxjs": "7.8.1",
-        "source-map": "0.7.4"
+        "common-path-prefix": "^3.0.0",
+        "pkg-dir": "^7.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
-      },
-      "peerDependencies": {
-        "chokidar": "^3.5.2"
+        "node": ">=14.16"
       },
-      "peerDependenciesMeta": {
-        "chokidar": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.4.tgz",
-      "integrity": "sha512-ASr9bGRuTTAAgHsr/ltBBl4CTyZETmuzS/boVMNDVLvjDDFr0aY/F/FW/QRFbJsxgxM2VAJn7NpY64Rl9fQz/g==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/find-up": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz",
+      "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.0.4",
-        "jsonc-parser": "3.2.0",
-        "magic-string": "0.30.0",
-        "ora": "5.4.1",
-        "rxjs": "7.8.1"
+        "locate-path": "^7.1.0",
+        "path-exists": "^5.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@angular/cli/node_modules/@schematics/angular": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.0.4.tgz",
-      "integrity": "sha512-3uu5xq136broqVKCGwYKENZYF8SO4EU15FHRV2EFY/PqU2sU5QBlsGG2Z1JYAVE3aevsk9ORkPb1tRh6yP28Rw==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/ipaddr.js": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz",
+      "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/locate-path": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+      "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
       "dev": true,
       "dependencies": {
-        "@angular-devkit/core": "16.0.4",
-        "@angular-devkit/schematics": "16.0.4",
-        "jsonc-parser": "3.2.0"
+        "p-locate": "^6.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0",
-        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
-        "yarn": ">= 1.13.0"
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@angular/cli/node_modules/magic-string": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
-      "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
       "dev": true,
       "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.4.13"
+        "yallist": "^4.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=10"
       }
     },
-    "node_modules/@angular/cli/node_modules/rxjs": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
-      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/magic-string": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
+      "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==",
       "dev": true,
       "dependencies": {
-        "tslib": "^2.1.0"
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "node_modules/@angular/common": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.0.4.tgz",
-      "integrity": "sha512-EX1u46Lwc0uGcc0jjaxFHcnkuXusytYcjcd9pc+7eNNIi9nvLJDRmd8VyZ12ozc6ynBO6vt0mFSpIpjUVsTheQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/mini-css-extract-plugin": {
+      "version": "2.7.6",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz",
+      "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "schema-utils": "^4.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "@angular/core": "16.0.4",
-        "rxjs": "^6.5.3 || ^7.4.0"
+        "webpack": "^5.0.0"
       }
     },
-    "node_modules/@angular/compiler": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.0.4.tgz",
-      "integrity": "sha512-TTCuvkODBsbm6H1xleUQy60ITaUIdD/b2smm2OHlMw0QPJOfC75DG0uwIjsHA5KjZNlLyRBQKAxrc8cYhMpyoQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/p-limit": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+      "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "yocto-queue": "^1.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
-      },
-      "peerDependencies": {
-        "@angular/core": "16.0.4"
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
       },
-      "peerDependenciesMeta": {
-        "@angular/core": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@angular/compiler-cli": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.0.4.tgz",
-      "integrity": "sha512-sCwB6GVvadrp9CQDC1cm/Hcmv0so2zZ6K1hLiFqi5ZbqelzAtTmvGxhTyQEAX1ZC7hdrpvhWm1Sqbh08OD4cdA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/p-locate": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+      "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
       "dev": true,
       "dependencies": {
-        "@babel/core": "7.21.8",
-        "@jridgewell/sourcemap-codec": "^1.4.14",
-        "chokidar": "^3.0.0",
-        "convert-source-map": "^1.5.1",
-        "reflect-metadata": "^0.1.2",
-        "semver": "^7.0.0",
-        "tslib": "^2.3.0",
-        "yargs": "^17.2.1"
+        "p-limit": "^4.0.0"
       },
-      "bin": {
-        "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
-        "ngc": "bundles/src/bin/ngc.js",
-        "ngcc": "bundles/ngcc/index.js"
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
       },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/path-exists": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+      "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+      "dev": true,
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/piscina": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.0.0.tgz",
+      "integrity": "sha512-641nAmJS4k4iqpNUqfggqUBUMmlw0ZoM5VZKdQkV2e970Inn3Tk9kroCc1wpsYLD07vCwpys5iY0d3xI/9WkTg==",
+      "dev": true,
+      "dependencies": {
+        "eventemitter-asyncresource": "^1.0.0",
+        "hdr-histogram-js": "^2.0.1",
+        "hdr-histogram-percentiles-obj": "^3.0.0"
       },
-      "peerDependencies": {
-        "@angular/compiler": "16.0.4",
-        "typescript": ">=4.9.3 <5.1"
+      "optionalDependencies": {
+        "nice-napi": "^1.0.2"
       }
     },
-    "node_modules/@angular/core": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.0.4.tgz",
-      "integrity": "sha512-Lp/qGJt0NFFTKQHofaMjCdGYfVwGCyhBtcbioaTqi1wA5OwXfMZX5MgHZXl1KlnZs1YVqM8vO/Z/4kOxgYXvNQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/pkg-dir": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz",
+      "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "find-up": "^6.3.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+        "node": ">=14.16"
       },
-      "peerDependencies": {
-        "rxjs": "^6.5.3 || ^7.4.0",
-        "zone.js": "~0.13.0"
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@angular/forms": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.0.4.tgz",
-      "integrity": "sha512-QkOM7xEYd4N0kXr1h2ma/R+TnkNGxNIyHNZNFMcTiWiJpzBkUiDJxurlm0Tdn/h5g/smY3zY0PFflQE5wpohNQ==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/postcss": {
+      "version": "8.4.31",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
       "dependencies": {
-        "tslib": "^2.3.0"
+        "nanoid": "^3.3.6",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
-      },
-      "peerDependencies": {
-        "@angular/common": "16.0.4",
-        "@angular/core": "16.0.4",
-        "@angular/platform-browser": "16.0.4",
-        "rxjs": "^6.5.3 || ^7.4.0"
+        "node": "^10 || ^12 || >=14"
       }
     },
-    "node_modules/@angular/material": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.0.3.tgz",
-      "integrity": "sha512-iK3SpYKE7V2yeN7IfiLKusdQdJyYh+AD7U+0CWlgR3ejbmBiw+FTDxxbjOVAr14qI26vTqrKFf83lOkEm6m+Hg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
       "dependencies": {
-        "@material/animation": "15.0.0-canary.90291f2e2.0",
-        "@material/auto-init": "15.0.0-canary.90291f2e2.0",
-        "@material/banner": "15.0.0-canary.90291f2e2.0",
-        "@material/base": "15.0.0-canary.90291f2e2.0",
-        "@material/button": "15.0.0-canary.90291f2e2.0",
-        "@material/card": "15.0.0-canary.90291f2e2.0",
-        "@material/checkbox": "15.0.0-canary.90291f2e2.0",
-        "@material/chips": "15.0.0-canary.90291f2e2.0",
-        "@material/circular-progress": "15.0.0-canary.90291f2e2.0",
-        "@material/data-table": "15.0.0-canary.90291f2e2.0",
-        "@material/density": "15.0.0-canary.90291f2e2.0",
-        "@material/dialog": "15.0.0-canary.90291f2e2.0",
-        "@material/dom": "15.0.0-canary.90291f2e2.0",
-        "@material/drawer": "15.0.0-canary.90291f2e2.0",
-        "@material/elevation": "15.0.0-canary.90291f2e2.0",
-        "@material/fab": "15.0.0-canary.90291f2e2.0",
-        "@material/feature-targeting": "15.0.0-canary.90291f2e2.0",
-        "@material/floating-label": "15.0.0-canary.90291f2e2.0",
-        "@material/form-field": "15.0.0-canary.90291f2e2.0",
-        "@material/icon-button": "15.0.0-canary.90291f2e2.0",
-        "@material/image-list": "15.0.0-canary.90291f2e2.0",
-        "@material/layout-grid": "15.0.0-canary.90291f2e2.0",
-        "@material/line-ripple": "15.0.0-canary.90291f2e2.0",
-        "@material/linear-progress": "15.0.0-canary.90291f2e2.0",
-        "@material/list": "15.0.0-canary.90291f2e2.0",
-        "@material/menu": "15.0.0-canary.90291f2e2.0",
-        "@material/menu-surface": "15.0.0-canary.90291f2e2.0",
-        "@material/notched-outline": "15.0.0-canary.90291f2e2.0",
-        "@material/radio": "15.0.0-canary.90291f2e2.0",
-        "@material/ripple": "15.0.0-canary.90291f2e2.0",
-        "@material/rtl": "15.0.0-canary.90291f2e2.0",
-        "@material/segmented-button": "15.0.0-canary.90291f2e2.0",
-        "@material/select": "15.0.0-canary.90291f2e2.0",
-        "@material/shape": "15.0.0-canary.90291f2e2.0",
-        "@material/slider": "15.0.0-canary.90291f2e2.0",
-        "@material/snackbar": "15.0.0-canary.90291f2e2.0",
-        "@material/switch": "15.0.0-canary.90291f2e2.0",
-        "@material/tab": "15.0.0-canary.90291f2e2.0",
-        "@material/tab-bar": "15.0.0-canary.90291f2e2.0",
-        "@material/tab-indicator": "15.0.0-canary.90291f2e2.0",
-        "@material/tab-scroller": "15.0.0-canary.90291f2e2.0",
-        "@material/textfield": "15.0.0-canary.90291f2e2.0",
-        "@material/theme": "15.0.0-canary.90291f2e2.0",
-        "@material/tooltip": "15.0.0-canary.90291f2e2.0",
-        "@material/top-app-bar": "15.0.0-canary.90291f2e2.0",
-        "@material/touch-target": "15.0.0-canary.90291f2e2.0",
-        "@material/typography": "15.0.0-canary.90291f2e2.0",
-        "tslib": "^2.3.0"
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-builders/custom-webpack/node_modules/sass": {
+      "version": "1.64.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz",
+      "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
       },
-      "peerDependencies": {
-        "@angular/animations": "^16.0.0 || ^17.0.0",
-        "@angular/cdk": "16.0.3",
-        "@angular/common": "^16.0.0 || ^17.0.0",
-        "@angular/core": "^16.0.0 || ^17.0.0",
-        "@angular/forms": "^16.0.0 || ^17.0.0",
-        "@angular/platform-browser": "^16.0.0 || ^17.0.0",
-        "rxjs": "^6.5.3 || ^7.4.0"
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
       }
     },
-    "node_modules/@angular/platform-browser": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.0.4.tgz",
-      "integrity": "sha512-sU9ZthOtTM8tJCX93MumRGDbkly4wZt0iucmqc7NYhWQfmwPBP+qVBEyXJneVOag6FU7OozyMoQ4e3274ka52w==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/sass-loader": {
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.2.tgz",
+      "integrity": "sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "neo-async": "^2.6.2"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+        "node": ">= 14.15.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "@angular/animations": "16.0.4",
-        "@angular/common": "16.0.4",
-        "@angular/core": "16.0.4"
+        "fibers": ">= 3.1.0",
+        "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
+        "sass": "^1.3.0",
+        "sass-embedded": "*",
+        "webpack": "^5.0.0"
       },
       "peerDependenciesMeta": {
-        "@angular/animations": {
+        "fibers": {
+          "optional": true
+        },
+        "node-sass": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
           "optional": true
         }
       }
     },
-    "node_modules/@angular/platform-browser-dynamic": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.0.4.tgz",
-      "integrity": "sha512-AsyC4tXW2ddIrVNZANqWocdtYP/xMq79ZeDSlQSfxuPt3wvqx+bFOGMDJva9fIW6yBz4forOUKdnpyH4M9aegg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "lru-cache": "^6.0.0"
       },
-      "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+      "bin": {
+        "semver": "bin/semver.js"
       },
-      "peerDependencies": {
-        "@angular/common": "16.0.4",
-        "@angular/compiler": "16.0.4",
-        "@angular/core": "16.0.4",
-        "@angular/platform-browser": "16.0.4"
+      "engines": {
+        "node": ">=10"
       }
     },
-    "node_modules/@angular/platform-server": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-16.0.4.tgz",
-      "integrity": "sha512-0A4Za6eDMgd6LhqMDqLdYhIk6Bp+cT97JvmUMLJus5mI1HpTv7cdUsOFpEf4kykP+u1uLSDmA1Zr/NUkyartIg==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0",
-        "xhr2": "^0.2.0"
+        "has-flag": "^4.0.0"
       },
       "engines": {
-        "node": "^16.14.0 || >=18.10.0"
-      },
-      "peerDependencies": {
-        "@angular/animations": "16.0.4",
-        "@angular/common": "16.0.4",
-        "@angular/compiler": "16.0.4",
-        "@angular/core": "16.0.4",
-        "@angular/platform-browser": "16.0.4"
+        "node": ">=8"
       }
     },
-    "node_modules/@angular/router": {
-      "version": "16.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.0.4.tgz",
-      "integrity": "sha512-RgmY/dH5IB3aEnulqWHWSJ9TlcdOe7ZXOu1Fztmm3hk9brXyX5OB7R9sLG77yrE0eGDhdBJjgkLjmWPfjXurOA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/terser": {
+      "version": "5.19.2",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
+      "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
+      "dev": true,
       "dependencies": {
-        "tslib": "^2.3.0"
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
       },
-      "engines": {
-        "node": "^16.14.0 || >=18.10.0"
+      "bin": {
+        "terser": "bin/terser"
       },
-      "peerDependencies": {
-        "@angular/common": "16.0.4",
-        "@angular/core": "16.0.4",
-        "@angular/platform-browser": "16.0.4",
-        "rxjs": "^6.5.3 || ^7.4.0"
+      "engines": {
+        "node": ">=10"
       }
     },
-    "node_modules/@assemblyscript/loader": {
-      "version": "0.10.1",
-      "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
-      "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==",
-      "dev": true
-    },
-    "node_modules/@babel/code-frame": {
-      "version": "7.22.13",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
-      "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/ts-node": {
+      "version": "10.9.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+      "dev": true,
       "dependencies": {
-        "@babel/highlight": "^7.22.13",
-        "chalk": "^2.4.2"
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
       },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/compat-data": {
-      "version": "7.21.7",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz",
-      "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==",
-      "devOptional": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/core": {
-      "version": "7.21.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz",
-      "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==",
-      "devOptional": true,
-      "dependencies": {
-        "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.21.4",
-        "@babel/generator": "^7.21.5",
-        "@babel/helper-compilation-targets": "^7.21.5",
-        "@babel/helper-module-transforms": "^7.21.5",
-        "@babel/helpers": "^7.21.5",
-        "@babel/parser": "^7.21.8",
-        "@babel/template": "^7.20.7",
-        "@babel/traverse": "^7.21.5",
-        "@babel/types": "^7.21.5",
-        "convert-source-map": "^1.7.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.2",
-        "semver": "^6.3.0"
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
       },
-      "engines": {
-        "node": ">=6.9.0"
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
       },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/babel"
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@babel/core/node_modules/@babel/generator": {
-      "version": "7.22.3",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz",
-      "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/tsconfig-paths": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+      "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.22.3",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
-        "jsesc": "^2.5.1"
+        "json5": "^2.2.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">=6"
       }
     },
-    "node_modules/@babel/core/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "devOptional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
+    "node_modules/@angular-builders/custom-webpack/node_modules/tslib": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
+      "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==",
+      "dev": true
     },
-    "node_modules/@babel/generator": {
-      "version": "7.21.4",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
-      "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
+    "node_modules/@angular-builders/custom-webpack/node_modules/vite": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz",
+      "integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.21.4",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
-        "jsesc": "^2.5.1"
+        "esbuild": "^0.18.10",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
       },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-annotate-as-pure": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
-      "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/types": "^7.18.6"
+      "bin": {
+        "vite": "bin/vite.js"
       },
       "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz",
-      "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/types": "^7.21.5"
+        "node": "^14.18.0 || >=16.0.0"
       },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz",
-      "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/compat-data": "^7.21.5",
-        "@babel/helper-validator-option": "^7.21.0",
-        "browserslist": "^4.21.3",
-        "lru-cache": "^5.1.1",
-        "semver": "^6.3.0"
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
       },
-      "engines": {
-        "node": ">=6.9.0"
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0"
+        "@types/node": ">= 14",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack": {
+      "version": "5.88.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
+      "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/eslint-scope": "^3.7.3",
+        "@types/estree": "^1.0.0",
+        "@webassemblyjs/ast": "^1.11.5",
+        "@webassemblyjs/wasm-edit": "^1.11.5",
+        "@webassemblyjs/wasm-parser": "^1.11.5",
+        "acorn": "^8.7.1",
+        "acorn-import-assertions": "^1.9.0",
+        "browserslist": "^4.14.5",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.15.0",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.9",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.2.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.7",
+        "watchpack": "^2.4.0",
+        "webpack-sources": "^3.2.3"
+      },
       "bin": {
-        "semver": "bin/semver.js"
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@babel/helper-create-class-features-plugin": {
-      "version": "7.21.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz",
-      "integrity": "sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack-dev-middleware": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.1.tgz",
+      "integrity": "sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==",
+      "dev": true,
       "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.18.6",
-        "@babel/helper-environment-visitor": "^7.21.5",
-        "@babel/helper-function-name": "^7.21.0",
-        "@babel/helper-member-expression-to-functions": "^7.21.5",
-        "@babel/helper-optimise-call-expression": "^7.18.6",
-        "@babel/helper-replace-supers": "^7.21.5",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
-        "@babel/helper-split-export-declaration": "^7.18.6",
-        "semver": "^6.3.0"
+        "colorette": "^2.0.10",
+        "memfs": "^3.4.12",
+        "mime-types": "^2.1.31",
+        "range-parser": "^1.2.1",
+        "schema-utils": "^4.0.0"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">= 14.15.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0"
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "devOptional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@babel/helper-create-regexp-features-plugin": {
-      "version": "7.21.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz",
-      "integrity": "sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack-dev-server": {
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
+      "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
+      "dev": true,
       "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.18.6",
-        "regexpu-core": "^5.3.1",
-        "semver": "^6.3.0"
+        "@types/bonjour": "^3.5.9",
+        "@types/connect-history-api-fallback": "^1.3.5",
+        "@types/express": "^4.17.13",
+        "@types/serve-index": "^1.9.1",
+        "@types/serve-static": "^1.13.10",
+        "@types/sockjs": "^0.3.33",
+        "@types/ws": "^8.5.5",
+        "ansi-html-community": "^0.0.8",
+        "bonjour-service": "^1.0.11",
+        "chokidar": "^3.5.3",
+        "colorette": "^2.0.10",
+        "compression": "^1.7.4",
+        "connect-history-api-fallback": "^2.0.0",
+        "default-gateway": "^6.0.3",
+        "express": "^4.17.3",
+        "graceful-fs": "^4.2.6",
+        "html-entities": "^2.3.2",
+        "http-proxy-middleware": "^2.0.3",
+        "ipaddr.js": "^2.0.1",
+        "launch-editor": "^2.6.0",
+        "open": "^8.0.9",
+        "p-retry": "^4.5.0",
+        "rimraf": "^3.0.2",
+        "schema-utils": "^4.0.0",
+        "selfsigned": "^2.1.1",
+        "serve-index": "^1.9.1",
+        "sockjs": "^0.3.24",
+        "spdy": "^4.0.2",
+        "webpack-dev-middleware": "^5.3.1",
+        "ws": "^8.13.0"
+      },
+      "bin": {
+        "webpack-dev-server": "bin/webpack-dev-server.js"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">= 12.13.0"
       },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "devOptional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
-      "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.17.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "debug": "^4.1.1",
-        "lodash.debounce": "^4.0.8",
-        "resolve": "^1.14.2",
-        "semver": "^6.1.2"
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "@babel/core": "^7.4.0-0"
-      }
-    },
-    "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "devOptional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@babel/helper-environment-visitor": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
-      "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
-      "devOptional": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-function-name": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
-      "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/template": "^7.22.15",
-        "@babel/types": "^7.23.0"
+        "webpack": "^4.37.0 || ^5.0.0"
       },
-      "engines": {
-        "node": ">=6.9.0"
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        },
+        "webpack-cli": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@babel/helper-function-name/node_modules/@babel/template": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
-      "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
+      "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
+      "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.22.13",
-        "@babel/parser": "^7.22.15",
-        "@babel/types": "^7.22.15"
+        "colorette": "^2.0.10",
+        "memfs": "^3.4.3",
+        "mime-types": "^2.1.31",
+        "range-parser": "^1.2.1",
+        "schema-utils": "^4.0.0"
       },
       "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-hoist-variables": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
-      "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/types": "^7.22.5"
+        "node": ">= 12.13.0"
       },
-      "engines": {
-        "node": ">=6.9.0"
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^4.0.0 || ^5.0.0"
       }
     },
-    "node_modules/@babel/helper-member-expression-to-functions": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz",
-      "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack-merge": {
+      "version": "5.9.0",
+      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
+      "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.21.5"
+        "clone-deep": "^4.0.1",
+        "wildcard": "^2.0.0"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">=10.0.0"
       }
     },
-    "node_modules/@babel/helper-module-imports": {
-      "version": "7.21.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
-      "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.21.4"
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
       },
-      "engines": {
-        "node": ">=6.9.0"
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
       }
     },
-    "node_modules/@babel/helper-module-transforms": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz",
-      "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/helper-environment-visitor": "^7.21.5",
-        "@babel/helper-module-imports": "^7.21.4",
-        "@babel/helper-simple-access": "^7.21.5",
-        "@babel/helper-split-export-declaration": "^7.18.6",
-        "@babel/helper-validator-identifier": "^7.19.1",
-        "@babel/template": "^7.20.7",
-        "@babel/traverse": "^7.21.5",
-        "@babel/types": "^7.21.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack/node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "peerDependencies": {
+        "ajv": "^6.9.1"
       }
     },
-    "node_modules/@babel/helper-optimise-call-expression": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
-      "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
-      "devOptional": true,
+    "node_modules/@angular-builders/custom-webpack/node_modules/webpack/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.18.6"
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       }
     },
-    "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz",
-      "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==",
-      "devOptional": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
+    "node_modules/@angular-builders/custom-webpack/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
     },
-    "node_modules/@babel/helper-remap-async-to-generator": {
-      "version": "7.18.9",
-      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
-      "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.18.6",
-        "@babel/helper-environment-visitor": "^7.18.9",
-        "@babel/helper-wrap-function": "^7.18.9",
-        "@babel/types": "^7.18.9"
-      },
+    "node_modules/@angular-builders/custom-webpack/node_modules/yocto-queue": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
+      "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
+      "dev": true,
       "engines": {
-        "node": ">=6.9.0"
+        "node": ">=12.20"
       },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@babel/helper-replace-supers": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz",
-      "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==",
-      "devOptional": true,
+    "node_modules/@angular-devkit/architect": {
+      "version": "0.1600.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.6.tgz",
+      "integrity": "sha512-Mk/pRujuer5qRMrgC7DPwLQ88wTAEKhbs0yJ/1prm4cx+VkxX9MMf6Y4AHKRmduKmFmd2LmX21/ACiU65acH8w==",
+      "dev": true,
       "dependencies": {
-        "@babel/helper-environment-visitor": "^7.21.5",
-        "@babel/helper-member-expression-to-functions": "^7.21.5",
-        "@babel/helper-optimise-call-expression": "^7.18.6",
-        "@babel/template": "^7.20.7",
-        "@babel/traverse": "^7.21.5",
-        "@babel/types": "^7.21.5"
+        "@angular-devkit/core": "16.0.6",
+        "rxjs": "7.8.1"
       },
       "engines": {
-        "node": ">=6.9.0"
+        "node": "^16.14.0 || >=18.10.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
       }
     },
-    "node_modules/@babel/helper-simple-access": {
-      "version": "7.21.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz",
-      "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==",
-      "devOptional": true,
+    "node_modules/@angular-devkit/architect/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.21.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
+        "tslib": "^2.1.0"
       }
     },
-    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
-      "version": "7.20.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
-      "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
-      "devOptional": true,
+    "node_modules/@angular-devkit/build-angular": {
+      "version": "16.0.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.0.4.tgz",
+      "integrity": "sha512-avNUNuLFSJOJ52z4Yk/5yyMVlMdpqbhPqW+RdinYrBRjkJZoscndV1bshrLZQHmVvoUpJes0mLXa1u+NRhqd3Q==",
+      "dev": true,
       "dependencies": {
-        "@babel/types": "^7.20.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-split-export-declaration": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
-      "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
-      "devOptional": true,
-      "dependencies": {
-        "@babel/types": "^7.18.6"
+        "@ampproject/remapping": "2.2.1",
+        "@angular-devkit/architect": "0.1600.4",
+        "@angular-devkit/build-webpack": "0.1600.4",
+        "@angular-devkit/core": "16.0.4",
+        "@babel/core": "7.21.4",
+        "@babel/generator": "7.21.4",
+        "@babel/helper-annotate-as-pure": "7.18.6",
+        "@babel/helper-split-export-declaration": "7.18.6",
+        "@babel/plugin-proposal-async-generator-functions": "7.20.7",
+        "@babel/plugin-transform-async-to-generator": "7.20.7",
+        "@babel/plugin-transform-runtime": "7.21.4",
+        "@babel/preset-env": "7.21.4",
+        "@babel/runtime": "7.21.0",
+        "@babel/template": "7.20.7",
+        "@discoveryjs/json-ext": "0.5.7",
+        "@ngtools/webpack": "16.0.4",
+        "@vitejs/plugin-basic-ssl": "1.0.1",
+        "ansi-colors": "4.1.3",
+        "autoprefixer": "10.4.14",
+        "babel-loader": "9.1.2",
+        "babel-plugin-istanbul": "6.1.1",
+        "browserslist": "4.21.5",
+        "cacache": "17.0.6",
+        "chokidar": "3.5.3",
+        "copy-webpack-plugin": "11.0.0",
+        "critters": "0.0.16",
+        "css-loader": "6.7.3",
+        "esbuild-wasm": "0.17.18",
+        "glob": "8.1.0",
+        "https-proxy-agent": "5.0.1",
+        "inquirer": "8.2.4",
+        "jsonc-parser": "3.2.0",
+        "karma-source-map-support": "1.4.0",
+        "less": "4.1.3",
... 49046 lines suppressed ...