From bf127a335964f95fb66df80330589ba5e0f3befb Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:00:19 -0600
Subject: [PATCH 01/49] aws auth fixture

---
 projects/nshmp-apps/cypress/fixtures/aws-auth.json | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/fixtures/aws-auth.json

diff --git a/projects/nshmp-apps/cypress/fixtures/aws-auth.json b/projects/nshmp-apps/cypress/fixtures/aws-auth.json
new file mode 100644
index 000000000..46217f224
--- /dev/null
+++ b/projects/nshmp-apps/cypress/fixtures/aws-auth.json
@@ -0,0 +1,3 @@
+{
+  "isAuthorized": true
+}
-- 
GitLab


From cf7287d29d212c691c2b233288d1f08166429066 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:00:40 -0600
Subject: [PATCH 02/49] Dashboard tests

---
 .../integration/dashboard/dashboard.spec.ts   | 18 +++++++++++--
 .../dev/dashboard/dev-dashboard.spec.ts       | 25 +++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)
 create mode 100644 projects/nshmp-apps/cypress/integration/dev/dashboard/dev-dashboard.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/dashboard/dashboard.spec.ts b/projects/nshmp-apps/cypress/integration/dashboard/dashboard.spec.ts
index b9818827b..ae36596ba 100644
--- a/projects/nshmp-apps/cypress/integration/dashboard/dashboard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/dashboard/dashboard.spec.ts
@@ -1,11 +1,25 @@
+import {navigation} from '../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../utils';
+
 describe('Dashboard', () => {
   beforeEach(() => {
+    cy.viewport('macbook-16');
     cy.visit('/');
   });
 
   describe('Check nshmp-template', () => {
-    it('Has nshmp-template', () => {
-      cy.get('nshmp-template').should('exist');
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Check dashboard', () => {
+    const nav = navigation().map(navItem => {
+      const list = navItem.navigation.filter(app => app.display !== 'Dashboard');
+      return {
+        ...navItem,
+        navigation: list,
+      };
     });
+
+    utils.hasDashboard(nav);
   });
 });
diff --git a/projects/nshmp-apps/cypress/integration/dev/dashboard/dev-dashboard.spec.ts b/projects/nshmp-apps/cypress/integration/dev/dashboard/dev-dashboard.spec.ts
new file mode 100644
index 000000000..39a66feab
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/dev/dashboard/dev-dashboard.spec.ts
@@ -0,0 +1,25 @@
+import {devNavigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+describe('Development Dashboard', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.visit('/dev');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(devNavigation());
+  });
+
+  describe('Check dev dashboard', () => {
+    const nav = devNavigation().map(navItem => {
+      const list = navItem.navigation.filter(app => app.display !== 'Development Dashboard');
+      return {
+        ...navItem,
+        navigation: list,
+      };
+    });
+
+    utils.hasDashboard(nav);
+  });
+});
-- 
GitLab


From 4a8d2ef28c27197c30ef8a97b6e2f9e482a8dfff Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:00:58 -0600
Subject: [PATCH 03/49] Exceedance e2e

---
 .../exceedance-explorer.spec.ts               | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts b/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts
new file mode 100644
index 000000000..c40960f7b
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts
@@ -0,0 +1,76 @@
+import {devNavigation} from '../../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../../utils';
+
+describe('Exceedance Explorer Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.visit('/dev/math/exceedance-explorer');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(devNavigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.changePlotSettings('.exceedance-plot', '.exceedance-settings');
+
+    it('Has default plot', () => {
+      cy.get('nshmp-template-plot-content').find('plotly-plot').should('be.visible');
+    });
+
+    it('Has control panel form fields', () => {
+      cy.get('nshmp-template-control-panel')
+        .find('form')
+        .within(() => {
+          setControlPanel();
+        });
+    });
+
+    it('Adds and removes curve from plot', () => {
+      cy.get('nshmp-template-control-panel')
+        .find('form')
+        .within(() => {
+          setControlPanel();
+        });
+
+      cy.get('nshmp-lib-control-panel-buttons')
+        .find('.submit-button')
+        .find('button')
+        .should('be.enabled');
+
+      cy.get('nshmp-lib-control-panel-buttons')
+        .find('.reset-button')
+        .find('button')
+        .should('not.be.enabled');
+
+      cy.get('nshmp-lib-control-panel-buttons').find('.submit-button').find('button').click();
+
+      cy.get('plotly-plot').find('.scatterlayer');
+
+      cy.get('nshmp-lib-control-panel-buttons')
+        .find('.reset-button')
+        .find('button')
+        .should('be.enabled')
+        .click();
+
+      cy.get('plotly-plot').find('.scatterlayer').should('not.exist');
+    });
+  });
+});
+
+function setControlPanel() {
+  cy.get('.median-input').find('input').clear().type('2.0').should('have.value', '2.0');
+
+  cy.get('.sigma-input').find('input').clear().type('0.5').should('have.value', '0.5');
+
+  cy.get('.rate-input').find('input').clear().type('1').should('have.value', '1');
+
+  cy.get('.truncation-checkbox').find('input').should('be.checked');
+
+  cy.get('.truncation-level-input').find('input').clear().type('3').should('have.value', '3');
+}
-- 
GitLab


From 93508ba1bf1dc9e4c84b078f3eaf2aaf8bce58eb Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:01:22 -0600
Subject: [PATCH 04/49] Fix list

---
 projects/nshmp-apps/src/app/dashboard/app.component.ts     | 4 +---
 projects/nshmp-apps/src/app/dev/dashboard/app.component.ts | 4 +---
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/projects/nshmp-apps/src/app/dashboard/app.component.ts b/projects/nshmp-apps/src/app/dashboard/app.component.ts
index e353e32c9..2a7b8d2a3 100644
--- a/projects/nshmp-apps/src/app/dashboard/app.component.ts
+++ b/projects/nshmp-apps/src/app/dashboard/app.component.ts
@@ -7,9 +7,7 @@ import * as nav from 'projects/nshmp-lib/src/lib/utils/navigation.utils';
   styleUrls: ['./app.component.scss'],
 })
 export class AppComponent {
-  navigationList = nav
-    .navigation()
-    .filter(navList => navList.navigation.find(nav => nav.display !== 'Dashboard'));
+  navigationList = nav.navigation();
 
   gmmApps = nav.gmmApps();
   mainApps = nav.mainApps();
diff --git a/projects/nshmp-apps/src/app/dev/dashboard/app.component.ts b/projects/nshmp-apps/src/app/dev/dashboard/app.component.ts
index 6ce6b5e20..8805cd1bc 100644
--- a/projects/nshmp-apps/src/app/dev/dashboard/app.component.ts
+++ b/projects/nshmp-apps/src/app/dev/dashboard/app.component.ts
@@ -8,9 +8,7 @@ import * as nav from 'projects/nshmp-lib/src/lib/utils/navigation.utils';
   styleUrls: ['./app.component.scss'],
 })
 export class AppComponent implements OnInit {
-  navigationList = nav
-    .devNavigation()
-    .filter(navList => navList.navigation.find(nav => nav.display !== 'Development Dashboard'));
+  navigationList = nav.devNavigation();
 
   awsApps = nav.devAwsApps();
   mainApps = nav.devMainApps();
-- 
GitLab


From 9f994fd2c70f46e2d9d52ed5fdadae8bd3f3eae0 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:01:41 -0600
Subject: [PATCH 05/49] Add classes

---
 .../control-panel/control-panel.component.html     | 14 +++++++++-----
 .../components/plot/plot.component.html            |  1 +
 .../components/settings/settings.component.html    |  3 ++-
 .../control-panel-buttons.component.html           |  6 +++---
 .../plot-settings-axis.component.html              |  6 +++---
 .../plot-settings/plot-settings.component.html     | 14 ++++++++------
 6 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html
index 1b7ae95c9..5b8f6609d 100644
--- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html
@@ -6,7 +6,7 @@
   (submit)="exceedanceFacade.calculateExceedance()"
 >
   <!-- Median -->
-  <mat-form-field class="grid-col-12">
+  <mat-form-field class="grid-col-12 median-input">
     <mat-label> Median (g) </mat-label>
     <input
       type="number"
@@ -20,7 +20,7 @@
   </mat-form-field>
 
   <!-- Sigma -->
-  <mat-form-field class="grid-col-12">
+  <mat-form-field class="grid-col-12 sigma-input">
     <mat-label> Sigma (natural log units) </mat-label>
     <input
       type="number"
@@ -34,7 +34,7 @@
   </mat-form-field>
 
   <!-- Rate -->
-  <mat-form-field class="grid-col-12">
+  <mat-form-field class="grid-col-12 rate-input">
     <mat-label> Rate </mat-label>
     <input
       type="number"
@@ -48,12 +48,16 @@
   </mat-form-field>
 
   <!-- Truncation -->
-  <mat-checkbox color="primary" class="margin-left-1" [ngrxFormControlState]="truncation$ | async">
+  <mat-checkbox
+    color="primary"
+    class="margin-left-1 truncation-checkbox"
+    [ngrxFormControlState]="truncation$ | async"
+  >
     Truncation
   </mat-checkbox>
 
   <!-- Truncation Level -->
-  <mat-form-field class="grid-col-12">
+  <mat-form-field class="grid-col-12 truncation-level-input">
     <mat-label> Truncation Level (n) </mat-label>
     <input
       [disabled]="(truncation$ | async)?.value === false"
diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html
index f9990b49f..2f46fba10 100644
--- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html
+++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html
@@ -2,6 +2,7 @@
 <div class="height-full overflow-auto">
   <div class="grid-container nshmp-plotly-plot">
     <plotly-plot
+      class="exceedance-plot"
       [divId]="(plotData$ | async)?.id"
       [config]="(plotData$ | async)?.config"
       [data]="(plotData$ | async)?.data"
diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html
index eb4ffb7ae..320a5fc43 100644
--- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html
+++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html
@@ -1,5 +1,6 @@
 <!-- Exceedance plot settings -->
-<plot-lib-plot-settings *ngIf="plot$ | async" [plot]="plot$ | async"> </plot-lib-plot-settings>
+<plot-lib-plot-settings class="exceedance-settings" *ngIf="plot$ | async" [plot]="plot$ | async">
+</plot-lib-plot-settings>
 
 <div class="padding-y-3"></div>
 
diff --git a/projects/nshmp-lib/src/lib/components/control-panel-buttons/control-panel-buttons.component.html b/projects/nshmp-lib/src/lib/components/control-panel-buttons/control-panel-buttons.component.html
index dbb036c88..bc51288b6 100644
--- a/projects/nshmp-lib/src/lib/components/control-panel-buttons/control-panel-buttons.component.html
+++ b/projects/nshmp-lib/src/lib/components/control-panel-buttons/control-panel-buttons.component.html
@@ -1,6 +1,6 @@
 <div class="form-buttons form-buttons--left grid-row">
   <!-- Plot Button -->
-  <div class="{{ plotButtonSize }} padding-x-1">
+  <div class="{{ plotButtonSize }} padding-x-1 submit-button">
     <button
       class="grid-col-12"
       color="primary"
@@ -14,7 +14,7 @@
 
   <!-- Service Info Button -->
   <div
-    class="{{ serviceCallButtonSize }} padding-x-1"
+    class="{{ serviceCallButtonSize }} padding-x-1 service-button"
     [ngClass]="{'grid-offset-3': showReset === false}"
     *ngIf="showServiceCallInfo"
   >
@@ -32,7 +32,7 @@
   <div class="grid-col-fill" *ngIf="showServiceCallInfo === false"></div>
 
   <!-- Reset Control Panel Button -->
-  <div class="{{ resetButtonSize }} padding-x-1" *ngIf="showReset">
+  <div class="{{ resetButtonSize }} padding-x-1 reset-button" *ngIf="showReset">
     <button
       class="grid-col-12"
       color="warn"
diff --git a/projects/plot-lib/src/lib/components/plot-settings-axis/plot-settings-axis.component.html b/projects/plot-lib/src/lib/components/plot-settings-axis/plot-settings-axis.component.html
index 26868184c..a3559bb88 100644
--- a/projects/plot-lib/src/lib/components/plot-settings-axis/plot-settings-axis.component.html
+++ b/projects/plot-lib/src/lib/components/plot-settings-axis/plot-settings-axis.component.html
@@ -4,13 +4,13 @@
 
   <div class="settings-subsection--section">
     <!-- Plot Settings: Axis: title text -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 axis-title-input">
       <mat-label>Title</mat-label>
       <input type="text" matInput [ngrxFormControlState]="settings?.title?.controls?.text" />
     </mat-form-field>
 
     <!-- Plot Settings: Axis: title font size -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 axis-title-font-size-input">
       <mat-label>Title Font Size</mat-label>
       <input
         matInput
@@ -20,7 +20,7 @@
     </mat-form-field>
 
     <!-- Plot Settings: Axis: axis type -->
-    <div class="mat-form-field-wrapper">
+    <div class="mat-form-field-wrapper axis-type-toggle">
       <mat-button-toggle-group
         class="grid-col-12"
         [ngrxFormControlState]="settings?.axis?.controls?.type"
diff --git a/projects/plot-lib/src/lib/components/plot-settings/plot-settings.component.html b/projects/plot-lib/src/lib/components/plot-settings/plot-settings.component.html
index dbc9d9ffe..79526209c 100644
--- a/projects/plot-lib/src/lib/components/plot-settings/plot-settings.component.html
+++ b/projects/plot-lib/src/lib/components/plot-settings/plot-settings.component.html
@@ -4,13 +4,13 @@
     <mat-label class="settings-section--label">{{ nshmpPlot.label }}</mat-label>
 
     <!-- Plot Settings: title text -->
-    <mat-form-field class="grid-col-12 padding-top-2">
+    <mat-form-field class="grid-col-12 padding-top-2 title-input">
       <mat-label>Title</mat-label>
       <input type="text" matInput [ngrxFormControlState]="plotSettings?.title?.controls?.text" />
     </mat-form-field>
 
     <!-- Plot Settings: title font size -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 title-font-size-input">
       <mat-label>Title Font Size</mat-label>
       <input
         matInput
@@ -20,7 +20,7 @@
     </mat-form-field>
 
     <!-- Plot Settings: height-->
-    <mat-form-field class="grid-col-12 padding-bottom-2">
+    <mat-form-field class="grid-col-12 padding-bottom-2 plot-height-input">
       <mat-label>Height</mat-label>
       <input
         type="number"
@@ -34,17 +34,19 @@
     <!-- Plot Settings: show legend -->
     <mat-slide-toggle
       color="primary"
-      class="margin-left-1 margin-bottom-2"
+      class="margin-left-1 margin-bottom-2 show-legend-toggle"
       [ngrxFormControlState]="plotSettings?.layout?.controls?.showlegend"
     >
       Legend
     </mat-slide-toggle>
 
     <!-- X-Axis Settings-->
-    <plot-lib-plot-settings-axis [settings]="plotSettings?.xAxis"> </plot-lib-plot-settings-axis>
+    <plot-lib-plot-settings-axis class="x-axis-settings" [settings]="plotSettings?.xAxis">
+    </plot-lib-plot-settings-axis>
 
     <!-- Y-Axis Settings -->
-    <plot-lib-plot-settings-axis [settings]="plotSettings?.yAxis"> </plot-lib-plot-settings-axis>
+    <plot-lib-plot-settings-axis class="y-axis-settings" [settings]="plotSettings?.yAxis">
+    </plot-lib-plot-settings-axis>
 
     <mat-divider class="padding-bottom-2"></mat-divider>
   </form>
-- 
GitLab


From 6cea021ed78288e0e88001b6c1f29d4472a84425 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Thu, 24 Mar 2022 11:02:01 -0600
Subject: [PATCH 06/49] Update template

---
 package-lock.json | 43 ++++++++++++++++++++++---------------------
 package.json      |  3 ++-
 2 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 763082b77..cb7f436da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,7 @@
         "@angular/platform-browser-dynamic": "^13.2.6",
         "@angular/router": "^13.2.6",
         "@ghsc/disagg-d3": "^0.6.0",
-        "@ghsc/nshmp-template": "^13.0.1",
+        "@ghsc/nshmp-template": "^13.0.2",
         "@ghsc/nshmp-utils": "^4.4.0",
         "@mapbox/mapbox-gl-geocoder": "^5.0.0",
         "@ngrx/effects": "^13.0.2",
@@ -95,6 +95,7 @@
         "protractor": "~7.0.0",
         "ts-loader": "^9.2.8",
         "ts-node": "^8.10.2",
+        "tsconfig-paths": "^3.14.1",
         "tsutils": "^3.21.0",
         "typescript": "~4.5.5"
       }
@@ -3821,9 +3822,9 @@
       }
     },
     "node_modules/@ghsc/nshmp-template": {
-      "version": "13.0.1",
-      "resolved": "https://code.usgs.gov/api/v4/projects/1416/packages/npm/@ghsc/nshmp-template/-/@ghsc/nshmp-template-13.0.1.tgz",
-      "integrity": "sha1-j3g0VMEgC5Uxzr09QgtM5NMVXMM=",
+      "version": "13.0.2",
+      "resolved": "https://code.usgs.gov/api/v4/projects/1416/packages/npm/@ghsc/nshmp-template/-/@ghsc/nshmp-template-13.0.2.tgz",
+      "integrity": "sha1-LGJgCC4bEs2UlEKAFY6G+dQph+I=",
       "dependencies": {
         "tslib": "^2.0.0"
       },
@@ -19928,9 +19929,9 @@
       }
     },
     "node_modules/minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+      "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
     },
     "node_modules/minimist-options": {
       "version": "4.1.0",
@@ -26325,14 +26326,14 @@
       }
     },
     "node_modules/tsconfig-paths": {
-      "version": "3.14.0",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz",
-      "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==",
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+      "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
       "dev": true,
       "dependencies": {
         "@types/json5": "^0.0.29",
         "json5": "^1.0.1",
-        "minimist": "^1.2.0",
+        "minimist": "^1.2.6",
         "strip-bom": "^3.0.0"
       }
     },
@@ -30589,9 +30590,9 @@
       }
     },
     "@ghsc/nshmp-template": {
-      "version": "13.0.1",
-      "resolved": "https://code.usgs.gov/api/v4/projects/1416/packages/npm/@ghsc/nshmp-template/-/@ghsc/nshmp-template-13.0.1.tgz",
-      "integrity": "sha1-j3g0VMEgC5Uxzr09QgtM5NMVXMM=",
+      "version": "13.0.2",
+      "resolved": "https://code.usgs.gov/api/v4/projects/1416/packages/npm/@ghsc/nshmp-template/-/@ghsc/nshmp-template-13.0.2.tgz",
+      "integrity": "sha1-LGJgCC4bEs2UlEKAFY6G+dQph+I=",
       "requires": {
         "tslib": "^2.0.0"
       }
@@ -42894,9 +42895,9 @@
       }
     },
     "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+      "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
     },
     "minimist-options": {
       "version": "4.1.0",
@@ -47801,14 +47802,14 @@
       }
     },
     "tsconfig-paths": {
-      "version": "3.14.0",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz",
-      "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==",
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+      "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
       "dev": true,
       "requires": {
         "@types/json5": "^0.0.29",
         "json5": "^1.0.1",
-        "minimist": "^1.2.0",
+        "minimist": "^1.2.6",
         "strip-bom": "^3.0.0"
       },
       "dependencies": {
diff --git a/package.json b/package.json
index bd54503e3..c6ecd246f 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
     "@angular/platform-browser-dynamic": "^13.2.6",
     "@angular/router": "^13.2.6",
     "@ghsc/disagg-d3": "^0.6.0",
-    "@ghsc/nshmp-template": "^13.0.1",
+    "@ghsc/nshmp-template": "^13.0.2",
     "@ghsc/nshmp-utils": "^4.4.0",
     "@mapbox/mapbox-gl-geocoder": "^5.0.0",
     "@ngrx/effects": "^13.0.2",
@@ -105,6 +105,7 @@
     "protractor": "~7.0.0",
     "ts-loader": "^9.2.8",
     "ts-node": "^8.10.2",
+    "tsconfig-paths": "^3.14.1",
     "tsutils": "^3.21.0",
     "typescript": "~4.5.5"
   },
-- 
GitLab


From 5d86c8b2ec72c67732266a17f25bd3c1a11c5ab7 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:16:19 -0600
Subject: [PATCH 07/49] Add dev e2e

---
 .../exceedance-explorer.spec.ts               | 54 ++++++-------------
 1 file changed, 17 insertions(+), 37 deletions(-)

diff --git a/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts b/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts
index c40960f7b..66e242f39 100644
--- a/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/dev/math/exceedance-explorer/exceedance-explorer.spec.ts
@@ -24,53 +24,33 @@ describe('Exceedance Explorer Application', () => {
     });
 
     it('Has control panel form fields', () => {
-      cy.get('nshmp-template-control-panel')
-        .find('form')
-        .within(() => {
-          setControlPanel();
-        });
+      setControlPanel();
     });
 
     it('Adds and removes curve from plot', () => {
-      cy.get('nshmp-template-control-panel')
-        .find('form')
-        .within(() => {
-          setControlPanel();
-        });
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: true,
+        hasServiceCallButton: false,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
 
-      cy.get('nshmp-lib-control-panel-buttons')
-        .find('.submit-button')
-        .find('button')
-        .should('be.enabled');
+      setControlPanel();
 
-      cy.get('nshmp-lib-control-panel-buttons')
-        .find('.reset-button')
-        .find('button')
-        .should('not.be.enabled');
+      utils.submitFormCheckPlot({
+        plotClass: '.exceedance-plot',
+        intercept: false,
+      });
 
-      cy.get('nshmp-lib-control-panel-buttons').find('.submit-button').find('button').click();
-
-      cy.get('plotly-plot').find('.scatterlayer');
-
-      cy.get('nshmp-lib-control-panel-buttons')
-        .find('.reset-button')
-        .find('button')
-        .should('be.enabled')
-        .click();
-
-      cy.get('plotly-plot').find('.scatterlayer').should('not.exist');
+      utils.controlPanelButtonsEnabled(buttons);
     });
   });
 });
 
 function setControlPanel() {
-  cy.get('.median-input').find('input').clear().type('2.0').should('have.value', '2.0');
-
-  cy.get('.sigma-input').find('input').clear().type('0.5').should('have.value', '0.5');
-
-  cy.get('.rate-input').find('input').clear().type('1').should('have.value', '1');
-
+  utils.setInput('.median-input', '2.0');
+  utils.setInput('.sigma-input', '0.5');
+  utils.setInput('.rate-input', '1.0');
   cy.get('.truncation-checkbox').find('input').should('be.checked');
-
-  cy.get('.truncation-level-input').find('input').clear().type('3').should('have.value', '3');
+  utils.setInput('.truncation-level-input', '3.0');
 }
-- 
GitLab


From 646529bdfdf941bef0b3868e3faf007149e756b5 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:16:57 -0600
Subject: [PATCH 08/49] add gmm e2e tests

---
 .../gmm/distance/gmm-distance.spec.ts         |  92 ++++++++++++
 .../gmm/magnitude/gmm-magnitude.spec.ts       | 134 ++++++++++++++++++
 .../gmm/spectra/response-spectra.spec.ts      | 120 ++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
 create mode 100644 projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
 create mode 100644 projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
new file mode 100644
index 000000000..1a6ed1673
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
@@ -0,0 +1,92 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/data/gmm/distance';
+
+describe('Ground Motion vs. Distance Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/gmm/distance');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.changePlotSettings('.distance-plot', '.distance-settings');
+    utils.hasGmmMenu();
+
+    it('Can set GMM menus', () => {
+      setGmmMenus();
+    });
+
+    it('Can set event parameters', () => {
+      setEventParameters();
+    });
+
+    it('Can set geometry parameters', () => {
+      utils.setSourceGeometryParameters();
+    });
+
+    it('Can set site parameters', () => {
+      utils.setSiteParameters();
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+
+      utils.submitFormCheckPlot({
+        plotClass: '.distance-plot',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  setGmmMenus();
+  setEventParameters();
+  utils.setSourceGeometryParameters();
+  utils.setSiteParameters();
+}
+
+function setGmmMenus() {
+  cy.get('nshmp-template-control-panel').find('form').should('be.visible').as('form');
+
+  cy.get('@form').find('.multi-select-parameter-menu').click();
+  utils.selectMatOption('gmm');
+
+  cy.get('@form').find('.imt-select').find('mat-select').click();
+  utils.selectMatOption('default');
+
+  cy.get('@form').find('gmm-lib-gmm-menu').find('select').select([0, 1, 2, 3, 4]);
+
+  cy.get('@form')
+    .find('.imt-select')
+    .find('mat-select')
+    .click()
+    .get('.cdk-overlay-container')
+    .find('mat-option')
+    .should('not.have.value', 'default');
+  utils.selectMatOption('PGA');
+}
+
+function setEventParameters() {
+  utils.setMagnitude();
+}
diff --git a/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
new file mode 100644
index 000000000..52cc01ec7
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
@@ -0,0 +1,134 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/data/gmm/magnitude';
+
+describe('Ground Motion vs. Magnitude', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/gmm/magnitude');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.changePlotSettings('.mean-plot', '.mean-settings');
+    utils.changePlotSettings('.sigma-plot', '.sigma-settings');
+    utils.hasGmmMenu();
+
+    it('Can set GMM menus', () => {
+      setGmmMenus();
+    });
+
+    it('Can set event parameters', () => {
+      setEventParameters();
+    });
+
+    it('Can set geometry parameters', () => {
+      utils.setSourceGeometryParameters();
+    });
+
+    it('Can set site parameters', () => {
+      utils.setSiteParameters();
+    });
+
+    it('Can set path parameters', () => {
+      setPathParameters();
+    });
+
+    it('Can reset control panel inputs', () => {
+      utils.canResetControlPanel(() => setControlPanel());
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+      utils.submitFormCheckPlots({
+        firstPlotClass: '.mean-plot',
+        secondPlotClass: '.sigma-plot',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  setGmmMenus();
+  setEventParameters();
+  utils.setSourceGeometryParameters();
+  setPathParameters();
+  utils.setSiteParameters();
+}
+
+function setGmmMenus() {
+  cy.get('nshmp-template-control-panel').find('form').should('be.visible').as('form');
+
+  cy.get('@form').find('.multi-select-parameter-menu').click();
+  utils.selectMatOption('gmm');
+
+  cy.get('@form').find('.imt-select').find('mat-select').click();
+  utils.selectMatOption('default');
+
+  cy.get('@form').find('gmm-lib-gmm-menu').find('select').select([0, 1, 2, 3, 4]);
+
+  cy.get('@form')
+    .find('.imt-select')
+    .find('mat-select')
+    .click()
+    .get('.cdk-overlay-container')
+    .find('mat-option')
+    .should('not.have.value', 'default');
+  utils.selectMatOption('PGA');
+}
+
+function setPathParameters() {
+  cy.get('nshmp-template-control-panel')
+    .find('.distance-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('20.0')
+    .should('have.value', '20.0');
+}
+
+function setEventParameters() {
+  cy.get('nshmp-template-control-panel')
+    .find('.mmin-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('4.0')
+    .should('have.value', '4.0');
+
+  cy.get('nshmp-template-control-panel')
+    .find('.mmax-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('7.5')
+    .should('have.value', '7.5');
+
+  cy.get('nshmp-template-control-panel')
+    .find('.mstep-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('0.1')
+    .should('have.value', '0.1');
+}
diff --git a/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
new file mode 100644
index 000000000..031fa6947
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
@@ -0,0 +1,120 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/data/gmm/spectra';
+
+describe('Response Spectra', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/gmm/spectra');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.changePlotSettings('.spectra-plot', '.spectra-settings');
+    utils.changePlotSettings('.sigma-plot', '.sigma-settings');
+    utils.hasGmmMenu();
+
+    it('Can set GMM menus', () => {
+      setGmmMenus();
+    });
+
+    it('Can set event parameters', () => {
+      setEventParameters();
+    });
+
+    it('Can set geometry parameters', () => {
+      utils.setSourceGeometryParameters();
+    });
+
+    it('Can set site parameters', () => {
+      utils.setSiteParameters();
+    });
+
+    it('Can set path parameters', () => {
+      setPathParameters();
+    });
+
+    it('Can reset control panel inputs', () => {
+      utils.canResetControlPanel(() => setControlPanel());
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+      utils.submitFormCheckPlots({
+        firstPlotClass: '.spectra-plot',
+        secondPlotClass: '.sigma-plot',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  setGmmMenus();
+  setEventParameters();
+  utils.setSourceGeometryParameters();
+  setPathParameters();
+  utils.setSiteParameters();
+}
+
+function setGmmMenus() {
+  cy.get('nshmp-template-control-panel').find('form').should('be.visible').as('form');
+
+  cy.get('@form').find('.multi-select-parameter-menu').click();
+  utils.selectMatOption('gmm');
+
+  cy.get('@form').find('gmm-lib-gmm-menu').find('select').select([0, 1, 2, 3, 4]);
+}
+
+function setPathParameters() {
+  cy.get('nshmp-template-control-panel')
+    .find('.rx-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('20.0')
+    .should('have.value', '20.0');
+
+  cy.get('nshmp-template-control-panel')
+    .find('.rrup-input')
+    .find('input')
+    .should('be.visible')
+    .should('not.be.enabled');
+
+  cy.get('nshmp-template-control-panel')
+    .find('.rjb-input')
+    .find('input')
+    .should('be.visible')
+    .should('not.be.enabled');
+}
+
+function setEventParameters() {
+  utils.setMagnitude();
+
+  cy.get('nshmp-template-control-panel')
+    .find('.rake-input')
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('90')
+    .should('have.value', '90');
+}
-- 
GitLab


From 51832b6592ff2c39ce06cb7a23261ad109951ab1 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:17:23 -0600
Subject: [PATCH 09/49] add hazard e2e tests

---
 .../integration/hazard/disagg/disagg.spec.ts  | 132 ++++++++++++++++++
 .../hazard/dynamic/dynamic-hazard.spec.ts     |  56 ++++++++
 .../hazard/static/static-hazard.spec.ts       |  56 ++++++++
 3 files changed, 244 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
 create mode 100644 projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
 create mode 100644 projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
new file mode 100644
index 000000000..63aa09420
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
@@ -0,0 +1,132 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/conus-2018/dynamic/disagg';
+
+describe('Disagg Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/hazard/disagg');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.hasHazardModelMenu();
+
+    it('Can set control panel', () => {
+      setControlPanel();
+    });
+
+    it('Downloads data as csv', () => {
+      setControlPanel();
+      utils.submitFormCheckPlot({
+        plotClass: 'hazard-lib-disagg-plot',
+      });
+
+      cy.get('nshmp-template-plot-content').find('.export-button').scrollIntoView().click();
+      utils.hasDownloadedFile('disagg-Total.csv');
+    });
+
+    it('Downloads summary text', () => {
+      setControlPanel();
+      utils.submitFormCheckPlot({
+        plotClass: 'hazard-lib-disagg-plot',
+      });
+
+      cy.get('nshmp-template-plot-content')
+        .find('.summary')
+        .find('.summary-report')
+        .scrollIntoView()
+        .find('button')
+        .click();
+      utils.hasDownloadedFile('disagg-Total.csv');
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+
+      cy.get('.cdk-overlay-container').as('overlay');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.component-select')
+        .find('mat-select')
+        .should('be.visible')
+        .click();
+      cy.get('@overlay').find('mat-option').should('not.exist');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.export-button-loading')
+        .should('be.visible')
+        .should('not.be.enabled');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.summary')
+        .find('app-disagg-summary')
+        .scrollIntoView()
+        .should('not.be.visible');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.summary')
+        .find('app-disagg-contributors')
+        .scrollIntoView()
+        .should('not.be.visible');
+
+      utils.submitFormCheckPlot({
+        plotClass: 'hazard-lib-disagg-plot',
+      });
+
+      cy.get('nshmp-template-plot-content')
+        .find('.component-select')
+        .scrollIntoView()
+        .find('mat-select')
+        .should('be.visible')
+        .click();
+      cy.get('@overlay').find('mat-option').should('have.length.above', 1);
+      utils.selectMatOption('Total');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.export-button')
+        .scrollIntoView()
+        .should('be.visible')
+        .should('be.enabled')
+        .click();
+
+      cy.get('nshmp-template-plot-content')
+        .find('.summary')
+        .find('app-disagg-summary')
+        .scrollIntoView()
+        .should('be.visible');
+
+      cy.get('nshmp-template-plot-content')
+        .find('.summary')
+        .find('app-disagg-contributors')
+        .scrollIntoView()
+        .should('be.visible');
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  utils.setHazardCommonControlPanel();
+
+  cy.get('nshmp-template-control-panel').find('.imt-select').find('mat-select').click();
+  utils.selectMatOption('PGA');
+}
diff --git a/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
new file mode 100644
index 000000000..7ca1abfd9
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
@@ -0,0 +1,56 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/conus-2018/dynamic/hazard';
+
+describe('Dynamic Hazard Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/hazard/dynamic');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.hasHazardModelMenu();
+    utils.changePlotSettings('.hazard-plot', '.hazard-settings');
+    utils.changePlotSettings('.spectra-plot', '.spectra-settings');
+
+    it('Can set control panel', () => {
+      setControlPanel();
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+
+      utils.submitFormCheckPlots({
+        firstPlotClass: '.hazard-plot',
+        secondPlotClass: '.spectra-plot',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  utils.setHazardCommonControlPanel();
+  utils.setHazardTruncate();
+  utils.setHazardMaxDirection();
+}
diff --git a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
new file mode 100644
index 000000000..b31959955
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
@@ -0,0 +1,56 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/conus-2018/static/hazard';
+
+describe('Static Hazard Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/hazard/static');
+    cy.wait('@usage');
+  });
+
+  // describe('Check nshmp-template', () => {
+  //   utils.hasNshmpTemplate(navigation());
+  // });
+
+  describe('Application', () => {
+    // utils.hasControlPanel();
+    // utils.hasPlotContent();
+    // utils.toggleControlPanel();
+    // utils.togglePlotPanel();
+    // utils.toggleSettingPanel();
+    // utils.hasHazardModelMenu();
+    // utils.changePlotSettings('.hazard-plot', '.hazard-settings');
+    // utils.changePlotSettings('.spectra-plot', '.spectra-settings');
+
+    it('Can set control panel', () => {
+      setControlPanel();
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: true,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+
+      utils.submitFormCheckPlots({
+        firstPlotClass: '.hazard-plot',
+        secondPlotClass: '.spectra-plot',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  utils.setHazardCommonControlPanel();
+  utils.setHazardMaxDirection();
+  utils.setHazardTruncate();
+}
-- 
GitLab


From 735e1cb9b2d2b1e843b0776abb3107a7d3eee918 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:17:35 -0600
Subject: [PATCH 10/49] add services e2e tests

---
 .../integration/services/services.spec.ts     | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/integration/services/services.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/services/services.spec.ts b/projects/nshmp-apps/cypress/integration/services/services.spec.ts
new file mode 100644
index 000000000..35af555cd
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/services/services.spec.ts
@@ -0,0 +1,35 @@
+import {navigation} from '../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../utils';
+
+describe('Services Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.visit('/services');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    it('Has service accordians', () => {
+      cy.get('mat-accordion')
+        .should('be.visible')
+        .should('have.length.above', 1)
+        .find('.mat-expansion-panel-content')
+        .should('have.css', 'visibility', 'hidden');
+    });
+
+    it('Can open service accordions', () => {
+      cy.get('mat-accordion')
+        .should('be.visible')
+        .each(accordian => {
+          cy.wrap(accordian)
+            .click()
+            .find('.mat-expansion-panel-content')
+            .scrollIntoView()
+            .should('have.css', 'visibility', 'visible');
+        });
+    });
+  });
+});
-- 
GitLab


From e0f4ca724f8ed527c816465f01d6472ef05ef125 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:17:49 -0600
Subject: [PATCH 11/49] add source-models e2e tests

---
 .../source-models/data/data-mapping.spec.ts   | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/integration/source-models/data/data-mapping.spec.ts

diff --git a/projects/nshmp-apps/cypress/integration/source-models/data/data-mapping.spec.ts b/projects/nshmp-apps/cypress/integration/source-models/data/data-mapping.spec.ts
new file mode 100644
index 000000000..1e6ffd76a
--- /dev/null
+++ b/projects/nshmp-apps/cypress/integration/source-models/data/data-mapping.spec.ts
@@ -0,0 +1,54 @@
+import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
+import * as utils from '../../../utils';
+
+const url = '/ws/nshmp/data/fault-sections';
+
+describe('Data Mapping Application', () => {
+  beforeEach(() => {
+    cy.viewport('macbook-16');
+    cy.intercept(url).as('usage');
+    cy.visit('/source-models/data');
+    cy.wait('@usage');
+  });
+
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
+
+  describe('Application', () => {
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+
+    it('Can set control panel', () => {
+      setControlPanel();
+    });
+
+    it('Calls service and plots response', () => {
+      const buttons: utils.ControlPanelButtons = {
+        hasResetButton: false,
+        hasServiceCallButton: false,
+        hasSubmitButton: true,
+      };
+      utils.controlPanelButtonsNotEnabled(buttons);
+
+      setControlPanel();
+
+      utils.submitFormCheckPlot({
+        plotClass: 'mgl-map',
+      });
+
+      utils.controlPanelButtonsEnabled(buttons);
+    });
+  });
+});
+
+function setControlPanel() {
+  cy.get('nshmp-template-control-panel')
+    .find('.fault-section-select')
+    .should('be.visible')
+    .find('mat-select')
+    .click();
+  utils.selectMatOption('ALL');
+}
-- 
GitLab


From e6060ce1b3dddb0652a3774224f02bec0ddc1b6a Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:18:30 -0600
Subject: [PATCH 12/49] add plugins

---
 projects/nshmp-apps/cypress/plugins/index.js | 28 +++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/projects/nshmp-apps/cypress/plugins/index.js b/projects/nshmp-apps/cypress/plugins/index.js
index 3a3f37893..db8bc0453 100644
--- a/projects/nshmp-apps/cypress/plugins/index.js
+++ b/projects/nshmp-apps/cypress/plugins/index.js
@@ -1,7 +1,33 @@
 // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
 // For more info, visit https://on.cypress.io/plugins-api
+
 const webpackPreprocessor = require('@cypress/webpack-preprocessor');
 
 module.exports = on => {
-  on('file:preprocessor', webpackPreprocessor());
+  on(
+    'file:preprocessor',
+    webpackPreprocessor({
+      webpackOptions: {
+        resolve: {
+          extensions: ['.ts', '.tsx', '.js'],
+        },
+        module: {
+          rules: [
+            {
+              test: /\.tsx?$/,
+              loader: 'ts-loader',
+              options: {transpileOnly: true},
+            },
+          ],
+        },
+      },
+    })
+  );
+
+  on('task', {
+    log(message) {
+      console.log(message);
+      return null;
+    },
+  });
 };
-- 
GitLab


From 61e12cb41f02854951b5f796dec50650c9787bc6 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:19:03 -0600
Subject: [PATCH 13/49] add css classes for testings

---
 .../control-panel.component.html              | 24 ++++-----
 .../components/plots/plots.component.html     |  1 +
 .../settings/settings.component.html          | 32 ++++++-----
 .../control-panel.component.html              |  4 +-
 .../event-parameters.component.html           |  6 +--
 .../path-parameters.component.html            |  2 +-
 .../plots-settings.component.html             | 48 ++++++++++-------
 .../components/plots/plots.component.html     |  2 +
 .../site-parameters.component.html            |  6 +--
 .../source-parameters.component.html          |  6 +--
 .../control-panel.component.html              |  2 +-
 .../event-parameters.component.html           | 10 ++--
 .../path-parameters.component.html            | 10 ++--
 .../plots-settings.component.html             | 48 ++++++++++-------
 .../components/plots/plots.component.html     |  2 +
 .../site-parameters.component.html            |  6 +--
 .../source-parameters.component.html          |  6 +--
 .../control-panel.component.html              |  4 +-
 .../components/plots/plots.component.html     | 14 +++--
 .../components/control/control.component.html |  4 +-
 .../components/plots/plots.component.html     |  2 +
 .../settings/settings.component.html          | 54 +++++++++++--------
 .../control-panel.component.html              |  2 +
 .../components/plots/plots.component.html     |  2 +
 .../settings/settings.component.html          | 54 +++++++++++--------
 .../control-panel.component.html              | 12 +++--
 26 files changed, 219 insertions(+), 144 deletions(-)

diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html
index 5313dd66c..289233f83 100644
--- a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html
@@ -5,7 +5,7 @@
     [ngrxFormState]="form$ | async"
     (submit)="gmmDistanceFacade.callService()"
   >
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 multi-select-parameter-menu">
       <mat-label>Multi-Selectable Parameter</mat-label>
       <mat-select [ngrxFormControlState]="(controls$ | async)?.multiSelectableParam">
         <mat-option value="gmm">Ground Motion Models</mat-option>
@@ -25,7 +25,7 @@
     </gmm-lib-gmm-menu>
 
     <!-- IMT select menu -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 imt-select">
       <mat-label>Intensity Measure Type</mat-label>
       <mat-select #imtSelectEl [ngrxFormControlState]="(controls$ | async)?.imt">
         <mat-option *ngIf="(gmmSelected$ | async) === false" selected="true" value="default">
@@ -38,13 +38,13 @@
     </mat-form-field>
 
     <!-- Event parameters -->
-    <div class="settings-subsection">
+    <div class="settings-subsection event-parameters">
       <mat-label class="settings-subsection--label">Event Parameters</mat-label>
 
       <!-- Event parameters: Mw input -->
       <div class="settings-subsection--section">
         <gmm-lib-gmm-multi-select
-          class="grid-col-6"
+          class="grid-col-6 mw-input"
           label="Mw"
           [multiple]="MwMultiple$ | async"
           [numberControl]="(controls$ | async)?.Mw"
@@ -58,12 +58,12 @@
     </div>
 
     <!-- Source geometry -->
-    <div class="settings-subsection">
+    <div class="settings-subsection geometry-parameters">
       <mat-label class="settings-subsection--label">Source Geometry</mat-label>
 
       <div class="settings-subsection--section grid-row grid-gap-sm">
         <!-- Source geometry: zTop input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 ztop-input">
           <mat-label>Z<sub>TOP</sub> (km)</mat-label>
           <input
             matInput
@@ -82,7 +82,7 @@
         </mat-form-field>
 
         <!-- Source geometry: Dip input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 dip-input">
           <mat-label>Dip (°)</mat-label>
           <input
             matInput
@@ -101,7 +101,7 @@
         </mat-form-field>
 
         <!-- Source geometry: Width input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 width-input">
           <mat-label>Width (km)</mat-label>
           <input
             matInput
@@ -122,13 +122,13 @@
     </div>
 
     <!-- Site & Basin-->
-    <div class="settings-subsection">
+    <div class="settings-subsection site-parameters">
       <mat-label class="settings-subsection--label">Site & Basin</mat-label>
 
       <div class="settings-subsection--section grid-row grid-gap-sm">
         <!-- Site & Basin: Vs30 input -->
         <gmm-lib-gmm-multi-select
-          class="grid-col-6"
+          class="grid-col-6 vs30-input"
           [label]="vs30Label"
           [multiple]="vs30Multiple$ | async"
           [numberControl]="(controls$ | async)?.vs30"
@@ -140,7 +140,7 @@
         </gmm-lib-gmm-multi-select>
 
         <!-- Site & Basin: Z 1.0 input -->
-        <mat-form-field class="grid-col-3">
+        <mat-form-field class="grid-col-3 z1p0-input">
           <mat-label>Z<sub>1.0</sub> (km)</mat-label>
           <input
             matInput
@@ -159,7 +159,7 @@
         </mat-form-field>
 
         <!-- Site & Basin: Z 2.5 input -->
-        <mat-form-field class="grid-col-3">
+        <mat-form-field class="grid-col-3 z2p5-input">
           <mat-label>Z<sub>2.5</sub> (km)</mat-label>
           <input
             matInput
diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html
index 13ac33909..504dfb3bb 100644
--- a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html
@@ -1,6 +1,7 @@
 <!-- Means -->
 <div *ngIf="meanPlotData$ | async" class="nshmp-plotly-plot">
   <plotly-plot
+    class="distance-plot"
     [config]="(meanPlotData$ | async)?.config"
     [className]="(meanPlotData$ | async)?.config?.className"
     [data]="(meanPlotData$ | async)?.data"
diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html
index fa8cdb6bf..2323a7125 100644
--- a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html
+++ b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html
@@ -1,15 +1,23 @@
-<plot-lib-plot-settings *ngIf="meanPlot$ | async" [plot]="meanPlot$ | async">
-</plot-lib-plot-settings>
+<div class="height-full overflow-auto">
+  <plot-lib-plot-settings
+    *ngIf="meanPlot$ | async"
+    class="distance-settings"
+    [plot]="meanPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<div class="padding-y-3"></div>
+  <div class="padding-y-3"></div>
 
-<div class="form-buttons form-buttons--right">
-  <button
-    color="warn"
-    (click)="gmmDistanceFacade.resetPlotSettings()"
-    [disabled]="(meanPlotSettings$ | async)?.isPristine && (meanPlotSettings$ | async)?.isUntouched"
-    mat-raised-button
-  >
-    Reset
-  </button>
+  <div class="form-buttons form-buttons--right">
+    <button
+      color="warn"
+      (click)="gmmDistanceFacade.resetPlotSettings()"
+      [disabled]="
+        (meanPlotSettings$ | async)?.isPristine && (meanPlotSettings$ | async)?.isUntouched
+      "
+      mat-raised-button
+    >
+      Reset
+    </button>
+  </div>
 </div>
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/control-panel/control-panel.component.html
index a5417e5ab..4d1fd5ceb 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/control-panel/control-panel.component.html
@@ -6,7 +6,7 @@
     (submit)="facade.callService()"
     novalidate
   >
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 multi-select-parameter-menu">
       <mat-label>Multi-Selectable Parameter</mat-label>
       <mat-select [ngrxFormControlState]="(controls$ | async)?.multiSelectableParam">
         <mat-option value="gmm">Ground Motion Models</mat-option>
@@ -25,7 +25,7 @@
     </gmm-lib-gmm-menu>
 
     <!-- IMT select menu -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 imt-select">
       <mat-label>Intensity Measure Type</mat-label>
       <mat-select #imtSelectEl [ngrxFormControlState]="(controls$ | async)?.imt">
         <mat-option *ngIf="(gmmSelected$ | async) === false" selected="true" value="default">
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/event-parameters/event-parameters.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/event-parameters/event-parameters.component.html
index 618d7bce3..0dc701e7a 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/event-parameters/event-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/event-parameters/event-parameters.component.html
@@ -5,7 +5,7 @@
 
     <div class="settings-subsection--section grid-row grid-gap-sm">
       <!-- Event parameters: mMin input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 mmin-input">
         <mat-label>Minimum Mw</mat-label>
         <input
           matInput
@@ -25,7 +25,7 @@
       </mat-form-field>
 
       <!-- Event parameters: mMax input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 mmax-input">
         <mat-label>Maximum Mw</mat-label>
         <input
           matInput
@@ -45,7 +45,7 @@
       </mat-form-field>
 
       <!-- Event parameters: Step input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 mstep-input">
         <mat-label>Mw Step</mat-label>
         <input
           matInput
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/path-parameters/path-parameters.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/path-parameters/path-parameters.component.html
index 3bd363b1a..6d65c74e3 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/path-parameters/path-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/path-parameters/path-parameters.component.html
@@ -4,7 +4,7 @@
     <mat-label class="settings-subsection--label">Path Parameters</mat-label>
 
     <!-- Event parameters: Distance input -->
-    <div class="settings-subsection--section">
+    <div class="settings-subsection--section distance-input">
       <mat-form-field class="grid-col-12">
         <mat-label>Distance (km)</mat-label>
         <input
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/plots-settings/plots-settings.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/plots-settings/plots-settings.component.html
index f3675c769..5fdd22ec1 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/plots-settings/plots-settings.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/plots-settings/plots-settings.component.html
@@ -1,23 +1,33 @@
-<plot-lib-plot-settings *ngIf="meanPlot$ | async" [plot]="meanPlot$ | async">
-</plot-lib-plot-settings>
+<div class="height-full overflow-auto">
+  <plot-lib-plot-settings
+    *ngIf="meanPlot$ | async"
+    [plot]="meanPlot$ | async"
+    class="mean-settings"
+  >
+  </plot-lib-plot-settings>
 
-<plot-lib-plot-settings *ngIf="sigmaPlot$ | async" [plot]="sigmaPlot$ | async">
-</plot-lib-plot-settings>
+  <plot-lib-plot-settings
+    *ngIf="sigmaPlot$ | async"
+    [plot]="sigmaPlot$ | async"
+    class="sigma-settings"
+  >
+  </plot-lib-plot-settings>
 
-<div class="padding-y-2"></div>
+  <div class="padding-y-2"></div>
 
-<div class="form-buttons form-buttons--right">
-  <button
-    color="warn"
-    (click)="facade.resetPlotSettings()"
-    [disabled]="
-      (meanPlotSettings$ | async)?.isPristine &&
-      (meanPlotSettings$ | async)?.isUntouched &&
-      (sigmaPlotSettings$ | async)?.isPristine &&
-      (sigmaPlotSettings$ | async)?.isPristine
-    "
-    mat-raised-button
-  >
-    Reset
-  </button>
+  <div class="form-buttons form-buttons--right">
+    <button
+      color="warn"
+      (click)="facade.resetPlotSettings()"
+      [disabled]="
+        (meanPlotSettings$ | async)?.isPristine &&
+        (meanPlotSettings$ | async)?.isUntouched &&
+        (sigmaPlotSettings$ | async)?.isPristine &&
+        (sigmaPlotSettings$ | async)?.isPristine
+      "
+      mat-raised-button
+    >
+      Reset
+    </button>
+  </div>
 </div>
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/plots/plots.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/plots/plots.component.html
index eea68bbc6..3ddca65be 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/plots/plots.component.html
@@ -1,6 +1,7 @@
 <!-- Mean Plot -->
 <div *ngIf="meanPlotData$ | async" class="nshmp-plotly-plot">
   <plotly-plot
+    class="mean-plot"
     [config]="(meanPlotData$ | async)?.config"
     [className]="(meanPlotData$ | async)?.config?.className"
     [data]="(meanPlotData$ | async)?.data"
@@ -21,6 +22,7 @@
 <!-- Sigma Plot -->
 <div *ngIf="sigmaPlotData$ | async" class="nshmp-plotly-plot">
   <plotly-plot
+    class="sigma-plot"
     [config]="(sigmaPlotData$ | async)?.config"
     [className]="(sigmaPlotData$ | async)?.config?.className"
     [data]="(sigmaPlotData$ | async)?.data"
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.html
index 58c58196a..c5de94e45 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.html
@@ -6,7 +6,7 @@
     <div class="settings-subsection--section grid-row grid-gap-sm">
       <!-- Site & Basin: Vs30 input -->
       <gmm-lib-gmm-multi-select
-        class="grid-col-6"
+        class="grid-col-6 vs30-input"
         [label]="vs30Label"
         [multiple]="vs30Multiple$ | async"
         [numberControl]="(controls$ | async)?.vs30"
@@ -18,7 +18,7 @@
       </gmm-lib-gmm-multi-select>
 
       <!-- Site & Basin: Z 1.0 input -->
-      <mat-form-field class="grid-col-3">
+      <mat-form-field class="grid-col-3 z1p0-input">
         <mat-label>Z<sub>1.0</sub> (km)</mat-label>
         <input
           matInput
@@ -37,7 +37,7 @@
       </mat-form-field>
 
       <!-- Site & Basin: Z 2.5 input -->
-      <mat-form-field class="grid-col-3">
+      <mat-form-field class="grid-col-3 z2p5-input">
         <mat-label>Z<sub>2.5</sub> (km)</mat-label>
         <input
           matInput
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/source-parameters/source-parameters.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/source-parameters/source-parameters.component.html
index ed2c98f2c..a9e730a33 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/source-parameters/source-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/source-parameters/source-parameters.component.html
@@ -5,7 +5,7 @@
 
     <div class="settings-subsection--section grid-row grid-gap-sm">
       <!-- Source Parameters: zTop input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 ztop-input">
         <mat-label>Z<sub>TOP</sub> (km)</mat-label>
         <input
           matInput
@@ -24,7 +24,7 @@
       </mat-form-field>
 
       <!-- Source Parameters: Dip input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 dip-input">
         <mat-label>Dip (°)</mat-label>
         <input
           matInput
@@ -43,7 +43,7 @@
       </mat-form-field>
 
       <!-- Source Parameters: Width input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 width-input">
         <mat-label>Width (km)</mat-label>
         <input
           matInput
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html
index 9967cae7d..91a1219f5 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html
@@ -6,7 +6,7 @@
     (submit)="facade.callService()"
     novalidate
   >
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 multi-select-parameter-menu">
       <mat-label>Multi-Selectable Parameter</mat-label>
       <mat-select [ngrxFormControlState]="(controls$ | async)?.multiSelectableParam">
         <mat-option value="gmm">Ground Motion Model</mat-option>
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html
index b0a679736..4976f99fd 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html
@@ -7,7 +7,7 @@
       <div class="grid-row grid-gap-sm">
         <!-- Event parameters: Mw input -->
         <gmm-lib-gmm-multi-select
-          class="grid-col-6"
+          class="grid-col-6 mw-input"
           label="M<sub>W</sub>"
           [multiple]="MwMultiple$ | async"
           [numberControl]="(controls$ | async)?.Mw"
@@ -19,7 +19,7 @@
         </gmm-lib-gmm-multi-select>
 
         <!-- Event Parameters: Rake -->
-        <mat-form-field class="grid-col-6">
+        <mat-form-field class="grid-col-6 rake-input">
           <mat-label>Rake (°)</mat-label>
           <input
             matInput
@@ -41,7 +41,7 @@
 
       <!-- Event Parameters: Rake Buttons -->
       <mat-button-toggle-group
-        class="grid-col-12"
+        class="grid-col-12 rake-buttons"
         [ngrxFormControlState]="(controls$ | async)?.rakeButton"
         [value]="(controls$ | async)?.rake?.value"
       >
@@ -51,7 +51,7 @@
       </mat-button-toggle-group>
 
       <!-- Event Parameters: zHyp -->
-      <div class="grid-row grid-gap-lg">
+      <div class="grid-row grid-gap-lg zhyp-input">
         <mat-form-field class="grid-col-4">
           <mat-label>Z<sub>HYP</sub> (km)</mat-label>
           <input
@@ -74,7 +74,7 @@
         <!-- Event Parameters: centered down dip -->
         <div class="grid-col-8">
           <mat-checkbox
-            class=""
+            class="down-dip-checkbox"
             color="primary"
             [ngrxFormControlState]="(controls$ | async)?.zHypCentered"
           >
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html
index 54aeba508..78bb63561 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html
@@ -6,7 +6,7 @@
     <div class="settings-subsection--section">
       <div class="grid-row grid-gap-sm">
         <!-- Path Parameters: rX input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 rx-input">
           <mat-label>R<sub>X</sub> (km)</mat-label>
           <input
             matInput
@@ -25,7 +25,7 @@
         </mat-form-field>
 
         <!-- Path Parameters: rRup input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 rrup-input">
           <mat-label>R<sub>RUP</sub> (km)</mat-label>
           <input
             matInput
@@ -45,7 +45,7 @@
         </mat-form-field>
 
         <!-- Path Parameters: rJB input -->
-        <mat-form-field class="grid-col-4">
+        <mat-form-field class="grid-col-4 rjb-input">
           <mat-label>R<sub>JB</sub> (km)</mat-label>
           <input
             matInput
@@ -68,7 +68,7 @@
       <!-- Path Parameters: Derive rJB and rRup -->
       <div class="grid-row">
         <mat-checkbox
-          class="grid-col-12"
+          class="grid-col-12 rjb-rrup-checkbox"
           color="primary"
           [ngrxFormControlState]="(controls$ | async)?.derivePathParams"
         >
@@ -78,7 +78,7 @@
 
       <!-- Path Parameters: Hanging wall or footwall -->
       <mat-button-toggle-group
-        class="grid-col-12"
+        class="grid-col-12 hanging-wall-button"
         [ngrxFormControlState]="(controls$ | async)?.hangingWall"
       >
         <mat-button-toggle class="grid-col-6" [value]="true"> Hanging Wall </mat-button-toggle>
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html
index e1b269cfb..f96e46d1b 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html
@@ -1,23 +1,33 @@
-<plot-lib-plot-settings *ngIf="spectraPlot$ | async" [plot]="spectraPlot$ | async">
-</plot-lib-plot-settings>
+<div class="height-full overflow-auto">
+  <plot-lib-plot-settings
+    class="spectra-settings"
+    *ngIf="spectraPlot$ | async"
+    [plot]="spectraPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<plot-lib-plot-settings *ngIf="sigmaPlot$ | async" [plot]="sigmaPlot$ | async">
-</plot-lib-plot-settings>
+  <plot-lib-plot-settings
+    class="sigma-settings"
+    *ngIf="sigmaPlot$ | async"
+    [plot]="sigmaPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<div class="padding-y-3"></div>
+  <div class="padding-y-3"></div>
 
-<div class="form-buttons form-buttons--right">
-  <button
-    color="warn"
-    (click)="facade.resetPlotSettings()"
-    [disabled]="
-      (spectraPlotSettings$ | async)?.isPristine &&
-      (spectraPlotSettings$ | async)?.isUntouched &&
-      (sigmaPlotSettings$ | async)?.isPristine &&
-      (sigmaPlotSettings$ | async)?.isPristine
-    "
-    mat-raised-button
-  >
-    Reset
-  </button>
+  <div class="form-buttons form-buttons--right">
+    <button
+      color="warn"
+      (click)="facade.resetPlotSettings()"
+      [disabled]="
+        (spectraPlotSettings$ | async)?.isPristine &&
+        (spectraPlotSettings$ | async)?.isUntouched &&
+        (sigmaPlotSettings$ | async)?.isPristine &&
+        (sigmaPlotSettings$ | async)?.isPristine
+      "
+      mat-raised-button
+    >
+      Reset
+    </button>
+  </div>
 </div>
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html
index 2d011a93c..b9c69d118 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html
@@ -1,6 +1,7 @@
 <!-- Spectra -->
 <div *ngIf="spectraPlotData$ | async" class="nshmp-plotly-plot">
   <plotly-plot
+    class="spectra-plot"
     [config]="(spectraPlotData$ | async)?.config"
     [className]="(spectraPlotData$ | async)?.config?.className"
     [data]="(spectraPlotData$ | async)?.data"
@@ -21,6 +22,7 @@
 <!-- Sigma Plot -->
 <div *ngIf="sigmaPlotData$ | async" class="nshmp-plotly-plot">
   <plotly-plot
+    class="sigma-plot"
     [config]="(sigmaPlotData$ | async)?.config"
     [className]="(sigmaPlotData$ | async)?.config?.className"
     [data]="(sigmaPlotData$ | async)?.data"
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html
index cb2d863b8..1b2f68b22 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html
@@ -6,7 +6,7 @@
     <div class="settings-subsection--section grid-row grid-gap-sm">
       <!-- Site & Basin: Vs30 input -->
       <gmm-lib-gmm-multi-select
-        class="grid-col-6"
+        class="grid-col-6 vs30-input"
         [label]="vs30Label"
         [multiple]="vs30Multiple$ | async"
         [numberControl]="(controls$ | async)?.vs30"
@@ -18,7 +18,7 @@
       </gmm-lib-gmm-multi-select>
 
       <!-- Site & Basin: Z 1.0 input -->
-      <mat-form-field class="grid-col-3">
+      <mat-form-field class="grid-col-3 z1p0-input">
         <mat-label>Z<sub>1.0</sub> (km)</mat-label>
         <input
           matInput
@@ -37,7 +37,7 @@
       </mat-form-field>
 
       <!-- Site & Basin: Z 2.5 input -->
-      <mat-form-field class="grid-col-3">
+      <mat-form-field class="grid-col-3 z2p5-input">
         <mat-label>Z<sub>2.5</sub> (km)</mat-label>
         <input
           matInput
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html
index ac67f1619..41dca40d4 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html
@@ -5,7 +5,7 @@
 
     <div class="settings-subsection--section grid-row grid-gap-sm">
       <!-- Source Parameters: zTop input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 ztop-input">
         <mat-label>Z<sub>TOR</sub> (km)</mat-label>
         <input
           matInput
@@ -24,7 +24,7 @@
       </mat-form-field>
 
       <!-- Source Parameters: Dip input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 dip-input">
         <mat-label>Dip (°)</mat-label>
         <input
           matInput
@@ -43,7 +43,7 @@
       </mat-form-field>
 
       <!-- Source Parameters: Width input -->
-      <mat-form-field class="grid-col-4">
+      <mat-form-field class="grid-col-4 width-input">
         <mat-label>Width (km)</mat-label>
         <input
           matInput
diff --git a/projects/nshmp-apps/src/app/hazard/disagg/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/hazard/disagg/components/control-panel/control-panel.component.html
index 6f4f075d8..424dc54a2 100644
--- a/projects/nshmp-apps/src/app/hazard/disagg/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/hazard/disagg/components/control-panel/control-panel.component.html
@@ -14,6 +14,7 @@
 
     <!-- Latitude -->
     <hazard-lib-location-form
+      class="latitude-input"
       [controlState]="formState?.controls?.latitude"
       label="Latitude"
       [bounds]="(facade.usage$ | async)?.response?.latitude"
@@ -22,6 +23,7 @@
 
     <!-- Longitude -->
     <hazard-lib-location-form
+      class="longitude-input"
       [controlState]="formState?.controls?.longitude"
       label="Longitude"
       [bounds]="(facade.usage$ | async)?.response?.longitude"
@@ -50,7 +52,7 @@
     </div>
 
     <!-- Imt -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 imt-select">
       <mat-label> Intensity Measure Type </mat-label>
       <mat-select [ngrxFormControlState]="formState?.controls?.imt">
         <mat-option *ngFor="let imt of (sourceModel$ | async)?.imts" [value]="imt?.value">
diff --git a/projects/nshmp-apps/src/app/hazard/disagg/components/plots/plots.component.html b/projects/nshmp-apps/src/app/hazard/disagg/components/plots/plots.component.html
index c13d43d83..613f2b4c9 100644
--- a/projects/nshmp-apps/src/app/hazard/disagg/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/hazard/disagg/components/plots/plots.component.html
@@ -1,7 +1,7 @@
 <div class="plot-container" class="grid-container-widescreen" *ngIf="formState$ | async">
   <div>
     <!-- Select component -->
-    <mat-form-field class="grid-col-12 padding-top-4">
+    <mat-form-field class="grid-col-12 padding-top-4 component-select">
       <mat-label> Component </mat-label>
       <mat-select
         [ngrxFormControlState]="(formState$ | async)?.controls?.disaggComponent"
@@ -16,6 +16,7 @@
     <ng-container *ngIf="componentData$ | async as componentData; else loading">
       <div *ngIf="(formState$ | async)?.value as formValues">
         <button
+          class="export-button"
           mat-raised-button
           color="primary"
           [disabled]="(disaggData$ | async) === null"
@@ -28,7 +29,12 @@
 
     <ng-template #loading>
       <div>
-        <button mat-raised-button color="primary" [disabled]="(disaggData$ | async) === null">
+        <button
+          mat-raised-button
+          class="export-button-loading"
+          color="primary"
+          [disabled]="(disaggData$ | async) === null"
+        >
           Export Data as CSV
         </button>
       </div>
@@ -41,7 +47,7 @@
     ></hazard-lib-disagg-plot>
   </div>
 
-  <div class="padding-bottom-4">
+  <div class="padding-bottom-4 summary">
     <h3>
       Summary statistics for, Disaggregation
       <span *ngIf="componentData$ | async">
@@ -51,6 +57,7 @@
     <mat-accordion multi>
       <!-- Summary report -->
       <mat-expansion-panel
+        class="summary-report"
         [expanded]="disaggData$ | async"
         [disabled]="(disaggData$ | async) === null"
       >
@@ -60,6 +67,7 @@
 
       <!-- Contributions -->
       <mat-expansion-panel
+        class="contributions"
         [expanded]="(componentData$ | async) !== null"
         [disabled]="(componentData$ | async) === null"
       >
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/components/control/control.component.html b/projects/nshmp-apps/src/app/hazard/dynamic/components/control/control.component.html
index d02f3efe5..a2c358daa 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/components/control/control.component.html
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/components/control/control.component.html
@@ -14,6 +14,7 @@
 
     <!-- Latitude -->
     <hazard-lib-location-form
+      class="latitude-input"
       [controlState]="formState?.controls?.latitude"
       label="Latitude"
       [bounds]="(facade.usage$ | async)?.response?.latitude"
@@ -22,6 +23,7 @@
 
     <!-- Longitude -->
     <hazard-lib-location-form
+      class="longitude-input"
       [controlState]="formState?.controls?.longitude"
       label="Longitude"
       [bounds]="(facade.usage$ | async)?.response?.longitude"
@@ -57,7 +59,7 @@
     </hazard-lib-return-period-form>
 
     <!-- Source Type Control -->
-    <mat-form-field class="grid-col-12">
+    <mat-form-field class="grid-col-12 source-type-select">
       <mat-label>Source Type</mat-label>
       <mat-select [ngrxFormControlState]="formState?.controls?.sourceType">
         <mat-option *ngFor="let sourceType of sourceTypes$ | async" [value]="sourceType">
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/components/plots/plots.component.html b/projects/nshmp-apps/src/app/hazard/dynamic/components/plots/plots.component.html
index 17e24e185..6daa44f1d 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/components/plots/plots.component.html
@@ -1,6 +1,7 @@
 <!-- Hazard Curves -->
 <div class="grid-container nshmp-plotly-plot">
   <plotly-plot
+    class="hazard-plot"
     #hazardPlot
     [divId]="(hazardPlotData$ | async)?.id"
     [config]="(hazardPlotData$ | async)?.config"
@@ -22,6 +23,7 @@
 <!-- Response Spectrum -->
 <div class="grid-container nshmp-plotly-plot">
   <plotly-plot
+    class="spectra-plot"
     #spectrumPlot
     [divId]="(spectrumPlotData$ | async)?.id"
     [config]="(spectrumPlotData$ | async)?.config"
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/components/settings/settings.component.html b/projects/nshmp-apps/src/app/hazard/dynamic/components/settings/settings.component.html
index 681d2b523..d4bf28006 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/components/settings/settings.component.html
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/components/settings/settings.component.html
@@ -1,26 +1,36 @@
-<!-- Hazard plot settings -->
-<plot-lib-plot-settings *ngIf="hazardPlot$ | async" [plot]="hazardPlot$ | async">
-</plot-lib-plot-settings>
+<div class="height-full overflow-auto">
+  <!-- Hazard plot settings -->
+  <plot-lib-plot-settings
+    class="hazard-settings"
+    *ngIf="hazardPlot$ | async"
+    [plot]="hazardPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<!-- Response spectrum plot settings -->
-<plot-lib-plot-settings *ngIf="spectrumPlot$ | async" [plot]="spectrumPlot$ | async">
-</plot-lib-plot-settings>
+  <!-- Response spectrum plot settings -->
+  <plot-lib-plot-settings
+    class="spectra-settings"
+    *ngIf="spectrumPlot$ | async"
+    [plot]="spectrumPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<div class="padding-y-3"></div>
+  <div class="padding-y-3"></div>
 
-<!-- Reset button -->
-<div class="form-buttons form-buttons--right">
-  <button
-    mat-raised-button
-    color="warn"
-    [disabled]="
-      (hazardSettings$ | async)?.isPristine &&
-      (hazardSettings$ | async)?.isUntouched &&
-      (spectrumSettings$ | async)?.isPristine &&
-      (spectrumSettings$ | async)?.isUntouched
-    "
-    (click)="facade.resetSettings()"
-  >
-    Reset
-  </button>
+  <!-- Reset button -->
+  <div class="form-buttons form-buttons--right">
+    <button
+      mat-raised-button
+      color="warn"
+      [disabled]="
+        (hazardSettings$ | async)?.isPristine &&
+        (hazardSettings$ | async)?.isUntouched &&
+        (spectrumSettings$ | async)?.isPristine &&
+        (spectrumSettings$ | async)?.isUntouched
+      "
+      (click)="facade.resetSettings()"
+    >
+      Reset
+    </button>
+  </div>
 </div>
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/hazard/static/components/control-panel/control-panel.component.html
index 5bd9b365a..0d9c28663 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/control-panel/control-panel.component.html
@@ -14,6 +14,7 @@
 
     <!-- Latitude -->
     <hazard-lib-location-form
+      class="latitude-input"
       [controlState]="formState?.controls?.latitude"
       label="Latitude"
       [bounds]="(facade.usage$ | async)?.response?.latitude"
@@ -22,6 +23,7 @@
 
     <!-- Longitude -->
     <hazard-lib-location-form
+      class="longitude-input"
       [controlState]="formState?.controls?.longitude"
       label="Longitude"
       [bounds]="(facade.usage$ | async)?.response?.longitude"
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/plots/plots.component.html b/projects/nshmp-apps/src/app/hazard/static/components/plots/plots.component.html
index 0df271989..5193f1187 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/plots/plots.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/plots/plots.component.html
@@ -1,6 +1,7 @@
 <!-- Hazard Curves -->
 <div class="grid-container nshmp-plotly-plot" *ngIf="hazardPlotData$ | async">
   <plotly-plot
+    class="hazard-plot"
     [divId]="(hazardPlotData$ | async)?.id"
     [config]="(hazardPlotData$ | async)?.config"
     [data]="(hazardPlotData$ | async)?.data"
@@ -21,6 +22,7 @@
 <!-- Response Spectrum -->
 <div class="grid-container nshmp-plotly-plot" *ngIf="responseSpectrumPlotData$ | async">
   <plotly-plot
+    class="spectra-plot"
     [divId]="(responseSpectrumPlotData$ | async)?.id"
     [config]="(responseSpectrumPlotData$ | async)?.config"
     [data]="(responseSpectrumPlotData$ | async)?.data"
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/settings/settings.component.html b/projects/nshmp-apps/src/app/hazard/static/components/settings/settings.component.html
index 7ad0fc586..bcaf765f7 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/settings/settings.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/settings/settings.component.html
@@ -1,26 +1,36 @@
-<!-- Hazard plot settings -->
-<plot-lib-plot-settings *ngIf="hazardPlot$ | async" [plot]="hazardPlot$ | async">
-</plot-lib-plot-settings>
+<div class="height-full overflow-auto">
+  <!-- Hazard plot settings -->
+  <plot-lib-plot-settings
+    class="hazard-settings"
+    *ngIf="hazardPlot$ | async"
+    [plot]="hazardPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<!-- Response spectrum plot settings -->
-<plot-lib-plot-settings *ngIf="spectrumPlot$ | async" [plot]="spectrumPlot$ | async">
-</plot-lib-plot-settings>
+  <!-- Response spectrum plot settings -->
+  <plot-lib-plot-settings
+    class="spectra-settings"
+    *ngIf="spectrumPlot$ | async"
+    [plot]="spectrumPlot$ | async"
+  >
+  </plot-lib-plot-settings>
 
-<div class="padding-y-3"></div>
+  <div class="padding-y-3"></div>
 
-<!-- Reset button -->
-<div class="form-buttons form-buttons--right">
-  <button
-    mat-raised-button
-    color="warn"
-    [disabled]="
-      (hazardSettings$ | async)?.isPristine &&
-      (hazardSettings$ | async)?.isUntouched &&
-      (spectrumSettings$ | async)?.isPristine &&
-      (spectrumSettings$ | async)?.isUntouched
-    "
-    (click)="staticHazardFacade.resetSettings()"
-  >
-    Reset
-  </button>
+  <!-- Reset button -->
+  <div class="form-buttons form-buttons--right">
+    <button
+      mat-raised-button
+      color="warn"
+      [disabled]="
+        (hazardSettings$ | async)?.isPristine &&
+        (hazardSettings$ | async)?.isUntouched &&
+        (spectrumSettings$ | async)?.isPristine &&
+        (spectrumSettings$ | async)?.isUntouched
+      "
+      (click)="staticHazardFacade.resetSettings()"
+    >
+      Reset
+    </button>
+  </div>
 </div>
diff --git a/projects/nshmp-apps/src/app/source-models/data/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/source-models/data/components/control-panel/control-panel.component.html
index 39888fd39..dccec8f9a 100644
--- a/projects/nshmp-apps/src/app/source-models/data/components/control-panel/control-panel.component.html
+++ b/projects/nshmp-apps/src/app/source-models/data/components/control-panel/control-panel.component.html
@@ -2,11 +2,12 @@
   [ngrxFormState]="form$ | async"
   *ngIf="(form$ | async) && (faultSectionUsage$ | async)"
   (submit)="facade.callServices()"
+  class="height-full overflow-auto"
 >
   <div class="settings-section">
     <mat-label class="settings-section--label">Fault Sections</mat-label>
 
-    <mat-form-field class="grid-col-12 padding-top-2">
+    <mat-form-field class="grid-col-12 padding-top-2 fault-section-select">
       <mat-label>Group Name</mat-label>
       <mat-select [ngrxFormControlState]="(faultSectionForm$ | async)?.group">
         <mat-optgroup label="">
@@ -43,7 +44,10 @@
     </mat-form-field>
   </div> -->
 
-  <div class="form-buttons form-buttons--left">
-    <button mat-raised-button color="primary" class="plot-button" type="submit">Plot</button>
-  </div>
+  <nshmp-lib-control-panel-buttons
+    [plotDisabled]="(form$ | async)?.isInvalid"
+    [showServiceCallInfo]="false"
+    [showReset]="false"
+  >
+  </nshmp-lib-control-panel-buttons>
 </form>
-- 
GitLab


From a19165e3d95897381c521bb76bf5fe24eb8feefb Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:19:35 -0600
Subject: [PATCH 14/49] add css classes for testing

---
 .../gmm-lib/src/lib/components/gmm-menu/gmm-menu.component.html | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/projects/gmm-lib/src/lib/components/gmm-menu/gmm-menu.component.html b/projects/gmm-lib/src/lib/components/gmm-menu/gmm-menu.component.html
index 48d883e93..ecea5dff4 100644
--- a/projects/gmm-lib/src/lib/components/gmm-menu/gmm-menu.component.html
+++ b/projects/gmm-lib/src/lib/components/gmm-menu/gmm-menu.component.html
@@ -4,6 +4,7 @@
     <mat-button-toggle-group class="float-right" value="gmmGroup">
       <!-- Sort by group -->
       <mat-button-toggle
+        class="gmm-group-toggle"
         aria-label="Sort ground motion models by group"
         [checked]="isGmmGrouped"
         (click)="onListChange(true)"
@@ -16,6 +17,7 @@
 
       <!-- Sort alphabetically-->
       <mat-button-toggle
+        class="gmm-alpha-toggle"
         aria-label="Sort ground motion models alphabetically"
         [checked]="!isGmmGrouped"
         (click)="onListChange(false)"
-- 
GitLab


From e0af8575c4386d425734451afeae1c3c3c36f952 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:19:48 -0600
Subject: [PATCH 15/49] add css classes for testing

---
 .../components/site-class-form/site-class-form.component.html | 4 +++-
 .../components/site-class-form/site-class-form.component.ts   | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.html b/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.html
index 3f4ee9098..35a7fc6f5 100644
--- a/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.html
+++ b/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.html
@@ -1,7 +1,9 @@
 <mat-form-field [class]="formFieldClass" *ngIf="controlState">
   <mat-label> {{ label }} </mat-label>
   <mat-select [ngrxFormControlState]="controlState">
-    <mat-option [value]="siteClassPlaceHolder.value">{{ siteClassPlaceHolder.display }}</mat-option>
+    <mat-option [value]="siteClassPlaceHolder.value" *ngIf="siteClasses?.length === 0">
+      {{ siteClassPlaceHolder.display }}
+    </mat-option>
     <mat-option *ngFor="let siteClass of siteClasses" [value]="siteClass.value">
       {{ siteClass.display }}
     </mat-option>
diff --git a/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.ts b/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.ts
index 85d1d11ea..66b728a8e 100644
--- a/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.ts
+++ b/projects/hazard-lib/src/lib/components/site-class-form/site-class-form.component.ts
@@ -21,7 +21,7 @@ export class SiteClassFormComponent {
   label = 'Site Class';
 
   @Input()
-  siteClasses: nshmpWsUtils.metadata.Parameter[];
+  siteClasses: nshmpWsUtils.metadata.Parameter[] = [];
 
   constructor() {}
 }
-- 
GitLab


From 6b7c06a5b1334a0a5c07fecb96154aa27627a319 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:20:03 -0600
Subject: [PATCH 16/49] change header

---
 projects/nshmp-lib/src/lib/utils/navigation.utils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/projects/nshmp-lib/src/lib/utils/navigation.utils.ts b/projects/nshmp-lib/src/lib/utils/navigation.utils.ts
index 46bb67dce..3498940ba 100644
--- a/projects/nshmp-lib/src/lib/utils/navigation.utils.ts
+++ b/projects/nshmp-lib/src/lib/utils/navigation.utils.ts
@@ -132,7 +132,7 @@ export function devNavigation(): NavigationList[] {
       ],
     },
     {
-      subHeader: 'AWS Apps',
+      subHeader: 'AWS',
       navigation: devAwsApps(),
     },
   ];
-- 
GitLab


From 8ec0ed81a8b8d8605f52fd9cfc88d4c0fa64bd24 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:20:29 -0600
Subject: [PATCH 17/49] add plugin

---
 projects/nshmp-apps/cypress/plugins/index.js | 44 ++++++++++----------
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/projects/nshmp-apps/cypress/plugins/index.js b/projects/nshmp-apps/cypress/plugins/index.js
index db8bc0453..6defa7096 100644
--- a/projects/nshmp-apps/cypress/plugins/index.js
+++ b/projects/nshmp-apps/cypress/plugins/index.js
@@ -1,33 +1,31 @@
 // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
 // For more info, visit https://on.cypress.io/plugins-api
 
-const webpackPreprocessor = require('@cypress/webpack-preprocessor');
+const webpackPreprocessor = require('@cypress/webpack-preprocessor')
 
-module.exports = on => {
-  on(
-    'file:preprocessor',
-    webpackPreprocessor({
-      webpackOptions: {
-        resolve: {
-          extensions: ['.ts', '.tsx', '.js'],
-        },
-        module: {
-          rules: [
-            {
-              test: /\.tsx?$/,
-              loader: 'ts-loader',
-              options: {transpileOnly: true},
-            },
-          ],
-        },
+module.exports = (on) => {
+  on('file:preprocessor', webpackPreprocessor(
+   {
+    webpackOptions: {
+      resolve: {
+        extensions: [".ts", ".tsx", ".js"],
       },
-    })
-  );
+      module: {
+        rules: [
+          {
+            test: /\.tsx?$/,
+            loader: "ts-loader",
+            options: { transpileOnly: true }
+          }
+        ]
+      }
+    },
+  }));
 
   on('task', {
     log(message) {
-      console.log(message);
-      return null;
+      console.log(message)
+      return null
     },
-  });
+  })
 };
-- 
GitLab


From d64ef379cb1d262a24f48fc373e1eab7c9fa9872 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:20:53 -0600
Subject: [PATCH 18/49] Add dashboard utils

---
 .../cypress/utils/dashboard.utils.ts          | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/dashboard.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/dashboard.utils.ts b/projects/nshmp-apps/cypress/utils/dashboard.utils.ts
new file mode 100644
index 000000000..0158cac1f
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/dashboard.utils.ts
@@ -0,0 +1,57 @@
+import {NavigationList} from '@ghsc/nshmp-template';
+
+export function hasDashboard(navList: NavigationList[]) {
+  it('Has nshmp-template main', () => {
+    cy.get('nshmp-template-main').should('be.visible');
+  });
+
+  it('Has background image', () => {
+    cy.get('.dashboard').should(
+      'have.css',
+      'background-image',
+      `url("${Cypress.config().baseUrl}dashboard-background.png")`
+    );
+  });
+
+  it('Has dashboard title', () => {
+    cy.get('.dashboard').find('.dashboard--title').should('be.visible');
+  });
+
+  it('Has dashboard description', () => {
+    cy.get('.dashboard').find('.dashboard--description').should('be.visible');
+  });
+
+  it('Has mat cards for applications', () => {
+    cy.get('.dashboard').find('mat-card').should('have.length.above', 0).should('be.visible');
+  });
+
+  navList.forEach(nav => {
+    if (nav.subHeader) {
+      it(`Has "${nav.subHeader}" application list header`, () => {
+        cy.get('.dashboard').find('.app-section').contains(nav.subHeader);
+      });
+    }
+
+    nav.navigation.forEach(app => {
+      const routerLink = app.routerLink.startsWith('/')
+        ? app.routerLink.substring(1)
+        : app.routerLink;
+
+      it(`Has "${app.display}" application`, () => {
+        if (app.routerLink.includes('/aws/')) {
+          cy.intercept('GET', 'https://earthquake.usgs.gov/ws/nshmp/hazard-runs/auth', {
+            fixture: 'aws-auth.json',
+          });
+        }
+
+        cy.get('.dashboard')
+          .find('mat-card')
+          .contains('mat-card-title', app.display)
+          .parent()
+          .click()
+          .url()
+          .should('contain', `${Cypress.config().baseUrl}${routerLink}`);
+      });
+    });
+  });
+}
-- 
GitLab


From 0345b875cc2a1e2c3ecc362b90f8b5ec2174740b Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:21:26 -0600
Subject: [PATCH 19/49] add gmm utils

---
 .../nshmp-apps/cypress/utils/gmm-lib.utils.ts | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/gmm-lib.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/gmm-lib.utils.ts b/projects/nshmp-apps/cypress/utils/gmm-lib.utils.ts
new file mode 100644
index 000000000..24042d710
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/gmm-lib.utils.ts
@@ -0,0 +1,92 @@
+export function hasGmmMenu() {
+  it('Has GMM select menu', () => {
+    cy.get('nshmp-template-control-panel')
+      .find('gmm-lib-gmm-menu')
+      .scrollIntoView()
+      .should('be.visible')
+      .within(() => {
+        cy.get('.sort-buttons')
+          .find('.gmm-group-toggle')
+          .should('have.attr', 'value', 'gmmGroup')
+          .should('have.class', 'mat-button-toggle-checked');
+
+        cy.get('select').find('optgroup').should('be.visible').should('have.length.above', 1);
+
+        cy.get('.sort-buttons')
+          .find('.gmm-alpha-toggle')
+          .should('have.attr', 'value', 'gmmAlpha')
+          .find('button')
+          .click()
+          .parent()
+          .should('have.class', 'mat-button-toggle-checked');
+
+        cy.get('select').find('optgroup').should('not.exist');
+
+        cy.get('select').find('option').should('have.length.above', 1);
+
+        cy.get('select').scrollTo('bottom').should('be.visible');
+      });
+  });
+}
+
+export function setMagnitude() {
+  cy.get('nshmp-template-control-panel')
+    .find('.mw-input')
+    .scrollIntoView()
+    .find('input')
+    .should('be.visible')
+    .clear()
+    .type('6.0')
+    .should('have.value', '6.0');
+}
+
+export function setSiteParameters() {
+  cy.get('nshmp-template-control-panel').within(() => {
+    cy.get('.vs30-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .clear()
+      .type('760')
+      .should('have.value', '760');
+
+    cy.get('.z1p0-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .should('have.value', '');
+
+    cy.get('.z2p5-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .should('have.value', '');
+  });
+}
+export function setSourceGeometryParameters() {
+  cy.get('nshmp-template-control-panel').within(() => {
+    cy.get('.ztop-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .clear()
+      .type('1.0')
+      .should('have.value', '1.0');
+
+    cy.get('.dip-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .clear()
+      .type('90')
+      .should('have.value', '90');
+
+    cy.get('.width-input')
+      .scrollIntoView()
+      .find('input')
+      .should('be.visible')
+      .clear()
+      .type('14')
+      .should('have.value', '14');
+  });
+}
-- 
GitLab


From 65ae869b7f11ef0ff9d38e307683160d62af25b0 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:22:05 -0600
Subject: [PATCH 20/49] add hazard utils

---
 .../cypress/utils/hazard-lib.utils.ts         | 48 +++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/hazard-lib.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/hazard-lib.utils.ts b/projects/nshmp-apps/cypress/utils/hazard-lib.utils.ts
new file mode 100644
index 000000000..10e6fa00d
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/hazard-lib.utils.ts
@@ -0,0 +1,48 @@
+import * as utils from './utils';
+
+export function hasHazardModelMenu() {
+  it('Has model menu', () => {
+    cy.get('nshmp-template-control-panel')
+      .find('hazard-lib-model-form')
+      .scrollIntoView()
+      .find('mat-select')
+      .should('be.visible');
+  });
+}
+
+export function setHazardCommonControlPanel() {
+  utils.setInput('.latitude-input', '34');
+  utils.setInput('.longitude-input', '-118');
+  cy.get('nshmp-template-control-panel')
+    .find('hazard-lib-site-class-form')
+    .scrollIntoView()
+    .find('mat-select')
+    .click();
+  utils.selectMatOption('BC');
+  utils.setInput('hazard-lib-return-period-form', '2475');
+}
+
+export function setHazardMaxDirection() {
+  cy.get('nshmp-template-control-panel')
+    .find('hazard-lib-max-direction-form')
+    .scrollIntoView()
+    .find('mat-checkbox')
+    .should('not.have.class', 'mat-checkbox-checked')
+    .should('be.visible')
+    .find('label')
+    .click()
+    .parent()
+    .should('have.class', 'mat-checkbox-checked');
+}
+export function setHazardTruncate() {
+  cy.get('nshmp-template-control-panel')
+    .find('hazard-lib-truncation-form')
+    .scrollIntoView()
+    .find('mat-checkbox')
+    .should('not.have.class', 'mat-checkbox-checked')
+    .should('be.visible')
+    .find('label')
+    .click()
+    .parent()
+    .should('have.class', 'mat-checkbox-checked');
+}
-- 
GitLab


From 198a0593dd4b6d26d39b5c69bf857aef4e46c4aa Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:22:43 -0600
Subject: [PATCH 21/49] add nshmp-lib utils

---
 .../cypress/utils/nshmp-lib.utils.ts          | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts b/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts
new file mode 100644
index 000000000..61eaa9d78
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts
@@ -0,0 +1,57 @@
+export interface ControlPanelButtons {
+  hasSubmitButton: boolean;
+  hasServiceCallButton: boolean;
+  hasResetButton: boolean;
+}
+
+export function controlPanelButtonsEnabled(buttons: ControlPanelButtons) {
+  cy.get('nshmp-lib-control-panel-buttons').find('.form-buttons').as('buttons');
+
+  if (buttons.hasResetButton) {
+    cy.get('@buttons')
+      .find('.reset-button')
+      .should('be.visible')
+      .find('button')
+      .should('be.enabled');
+  }
+
+  if (buttons.hasServiceCallButton) {
+    cy.get('@buttons')
+      .find('.service-button')
+      .should('be.visible')
+      .find('button')
+      .should('be.enabled')
+      .click();
+
+    hasServiceCallPopUp();
+    cy.get('body').click();
+  }
+
+  if (buttons.hasSubmitButton) {
+    cy.get('@buttons')
+      .find('.submit-button')
+      .should('be.visible')
+      .find('button')
+      .should('be.enabled');
+  }
+}
+
+export function controlPanelButtonsNotEnabled(buttons: ControlPanelButtons) {
+  cy.get('nshmp-lib-control-panel-buttons').find('.form-buttons').as('buttons');
+
+  if (buttons.hasResetButton) {
+    cy.get('@buttons').find('.reset-button').should('be.visible').should('not.be.enabled');
+  }
+
+  if (buttons.hasServiceCallButton) {
+    cy.get('@buttons').find('.service-button').should('be.visible').should('not.be.enabled');
+  }
+
+  if (buttons.hasSubmitButton) {
+    cy.get('@buttons').find('.submit-button').should('be.visible').should('not.be.enabled');
+  }
+}
+
+export function hasServiceCallPopUp() {
+  cy.get('nshmp-lib-url-bottom-sheet').should('be.visible');
+}
-- 
GitLab


From ec9f824bf8cbabefda68177420a8d8c6f2f234f1 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:23:14 -0600
Subject: [PATCH 22/49] add nshmp-template utils

---
 .../cypress/utils/nshmp-template.utils.ts     | 228 ++++++++++++++++++
 1 file changed, 228 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts b/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts
new file mode 100644
index 000000000..c6dbd8e6f
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts
@@ -0,0 +1,228 @@
+import {NavigationList} from '@ghsc/nshmp-template';
+
+export interface CheckPlot {
+  plotClass: string;
+  intercept?: boolean;
+}
+
+export interface CheckPlots {
+  firstPlotClass: string;
+  secondPlotClass: string;
+
+  intercept?: boolean;
+}
+
+export function canResetControlPanel(setControlPanel: Function) {
+  setControlPanel();
+
+  cy.get('nshmp-lib-control-panel-buttons').find('.reset-button').find('button').click();
+}
+
+export function clickSettingsPanel() {
+  cy.get('nshmp-template-app-controls').find('.settings-toggle').find('button').click();
+}
+
+export function hasControlPanel() {
+  it('Has control panel', () => {
+    cy.get('nshmp-template-control-panel').should('be.visible');
+  });
+}
+
+export function hasNshmpTemplate(navList: NavigationList[]) {
+  it('Has nshmp-template', () => {
+    cy.get('nshmp-template').should('be.visible');
+  });
+
+  it('Has nshmp-template header', () => {
+    cy.get('nshmp-template-header').should('be.visible');
+  });
+
+  it('Has nshmp-template header logo', () => {
+    cy.get('div.nshmp-template-header--logo')
+      .should('be.visible')
+      .find('img')
+      .should('be.visible')
+      .should('have.attr', 'src', 'assets/usgs-logo.svg');
+  });
+
+  it('Return to dashboard when logo clicked', () => {
+    cy.get('div.nshmp-template-header--logo')
+      .should('be.visible')
+      .find('a')
+      .should('be.visible')
+      .click()
+      .url()
+      .should('eq', Cypress.config().baseUrl);
+  });
+
+  describe('Navigation Dropdown', () => {
+    it('Has navigation dropdown', () => {
+      cy.get('nshmp-template-navigation')
+        .find('button')
+        .click()
+        .get('.mat-menu-panel')
+        .should('be.visible')
+        .find('button')
+        .should('have.length.above', 0);
+    });
+
+    navList.forEach(nav => {
+      if (nav.subHeader) {
+        it(`Has "${nav.subHeader}" header`, () => {
+          cy.get('nshmp-template-navigation')
+            .find('button')
+            .click()
+            .get('.mat-menu-panel')
+            .contains(nav.subHeader);
+        });
+      }
+      nav.navigation.forEach(app => {
+        const routerLink = app.routerLink.startsWith('/')
+          ? app.routerLink.substring(1)
+          : app.routerLink;
+
+        it(`Has "${app.display}" listed in navigation menu`, () => {
+          if (app.routerLink.includes('/aws/')) {
+            cy.intercept('GET', 'https://earthquake.usgs.gov/ws/nshmp/hazard-runs/auth', {
+              fixture: 'aws-auth.json',
+            });
+          }
+
+          cy.get('nshmp-template-navigation').find('button').click();
+
+          cy.get('.mat-menu-panel')
+            .then(menu => {
+              const subMenu = menu.find(`button:contains('${nav.subHeader}')`);
+              if (subMenu.length) {
+                cy.get('.mat-menu-panel')
+                  .find(`button:contains('${nav.subHeader}')`)
+                  .trigger('mouseenter');
+              }
+            })
+            .get('.mat-menu-panel')
+            .contains('button', app.display)
+            .click()
+            .url()
+            .should('contain', `${Cypress.config().baseUrl}${routerLink}`);
+        });
+      });
+    });
+  });
+}
+
+export function hasPlotContent() {
+  it('Has plot content', () => {
+    cy.get('nshmp-template-plot-content').should('be.visible');
+  });
+}
+
+export function submitFormCheckPlot(options: CheckPlot) {
+  options.intercept = true ? options.intercept === undefined : options.intercept;
+
+  cy.get('nshmp-template-plot-content')
+    .find(options.plotClass)
+    .then(originalPlot => {
+      if (options.intercept) {
+        cy.intercept('**').as('service-call');
+      }
+
+      cy.get('nshmp-lib-control-panel-buttons').find('.submit-button').find('button').click();
+
+      if (options.intercept) {
+        cy.wait('@service-call');
+      }
+
+      cy.get('nshmp-template-plot-content')
+        .find(options.plotClass)
+        .should(updatedPlot => {
+          expect(originalPlot).to.not.equal(updatedPlot);
+        });
+    });
+}
+
+export function submitFormCheckPlots(options: CheckPlots) {
+  options.intercept = true ? options.intercept === undefined : options.intercept;
+
+  cy.get('nshmp-template-plot-content')
+    .find(options.firstPlotClass)
+    .then(originalFirstPlot => {
+      cy.get('nshmp-template-plot-content')
+        .find(options.secondPlotClass)
+        .then(originalSecondPlot => {
+          if (options.intercept) {
+            cy.task('log', 'intercept');
+            cy.intercept('/ws/nshmp**').as('service-call');
+          }
+
+          cy.get('nshmp-lib-control-panel-buttons').find('.submit-button').find('button').click();
+
+          if (options.intercept) {
+            cy.wait('@service-call', {
+              timeout: 30000,
+            });
+          }
+
+          cy.get('nshmp-template-plot-content')
+            .find(options.firstPlotClass)
+            .should(updatedFirstPlot => {
+              expect(originalFirstPlot).to.not.equal(updatedFirstPlot);
+            });
+          cy.get('nshmp-template-plot-content')
+            .find(options.secondPlotClass)
+            .should(updatedSecondPlot => {
+              expect(originalSecondPlot).to.not.equal(updatedSecondPlot);
+            });
+        });
+    });
+}
+
+export function toggleControlPanel() {
+  it('Toggles control panel', () => {
+    cy.get('nshmp-template-app-controls')
+      .find('.control-panel-toggle')
+      .should('have.class', 'mat-button-toggle-checked')
+      .find('button')
+      .should('be.enabled')
+      .as('control-panel-button');
+
+    cy.get('nshmp-template-control-panel').should('be.visible');
+
+    cy.get('@control-panel-button').click();
+
+    cy.get('nshmp-template-control-panel').should('not.exist');
+  });
+}
+
+export function togglePlotPanel() {
+  it('Toggles plot content', () => {
+    cy.get('nshmp-template-app-controls')
+      .find('.plot-toggle')
+      .should('have.class', 'mat-button-toggle-checked')
+      .find('button')
+      .should('be.enabled')
+      .as('plot-button');
+
+    cy.get('nshmp-template-plot-content').should('be.visible');
+
+    cy.get('@plot-button').click();
+
+    cy.get('nshmp-template-plot-content').should('not.exist');
+  });
+}
+
+export function toggleSettingPanel() {
+  it('Toggles setting panel', () => {
+    cy.get('nshmp-template-app-controls')
+      .find('.settings-toggle')
+      .should('not.have.class', 'mat-button-toggle-checked')
+      .find('button')
+      .should('be.enabled')
+      .as('settings-button');
+
+    cy.get('nshmp-template-settings').should('not.exist');
+
+    cy.get('@settings-button').click();
+
+    cy.get('nshmp-template-settings').find('form').should('be.visible');
+  });
+}
-- 
GitLab


From beb40abad1b2f06d0a643bc985120186d14ae75f Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:23:34 -0600
Subject: [PATCH 23/49] add plot-lib utils

---
 .../cypress/utils/plot-lib.utils.ts           | 154 ++++++++++++++++++
 1 file changed, 154 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/plot-lib.utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/plot-lib.utils.ts b/projects/nshmp-apps/cypress/utils/plot-lib.utils.ts
new file mode 100644
index 000000000..cb0dcf9ac
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/plot-lib.utils.ts
@@ -0,0 +1,154 @@
+import * as utils from './nshmp-template.utils';
+
+const testText = 'Test Text';
+
+type Axis = 'x' | 'y';
+
+export function changePlotSettings(plotClass: string, settingsClass: string) {
+  describe(`Change plot settings [plot ${plotClass}, settings ${settingsClass}]`, () => {
+    beforeEach(() => {
+      utils.clickSettingsPanel();
+      cy.get('nshmp-template-settings').find(settingsClass).scrollIntoView().as('plot-settings');
+      cy.get('nshmp-template-plot-content').find(plotClass).scrollIntoView().as('plot');
+    });
+
+    it('Plot title change', () => {
+      cy.get('@plot-settings')
+        .find('.title-input')
+        .scrollIntoView()
+        .should('be.visible')
+        .find('input')
+        .clear()
+        .type(testText)
+        .should('have.value', testText);
+
+      cy.get('@plot').contains('text', testText).should('be.visible');
+
+      cy.get('@plot-settings')
+        .find('.title-font-size-input')
+        .scrollIntoView()
+        .should('be.visible')
+        .find('input')
+        .clear()
+        .type('20')
+        .should('have.value', '20');
+
+      cy.get('@plot').contains('text', testText).should('have.css', 'font-size', '20px');
+    });
+
+    it('Plot height change', () => {
+      cy.get('@plot-settings')
+        .find('.plot-height-input')
+        .scrollIntoView()
+        .should('be.visible')
+        .find('input')
+        .clear()
+        .type('800')
+        .should('have.value', '800');
+
+      cy.get('@plot').find('svg').should('have.attr', 'height', '800');
+    });
+
+    it('X-Axis title change', () => {
+      axisTitleChange('x');
+    });
+
+    it('X-Axis axis change', () => {
+      axisChange('x');
+    });
+
+    it('Y-Axis title change', () => {
+      axisTitleChange('y');
+    });
+
+    it('Y-Axis axis change', () => {
+      axisChange('y');
+    });
+
+    it('Reset plot settings', () => {
+      cy.get('nshmp-template-settings')
+        .find('.form-buttons')
+        .contains('button', 'Reset')
+        .as('reset-button')
+        .should('be.visible')
+        .should('not.be.enabled');
+
+      cy.get('@plot-settings')
+        .find('.title-input')
+        .find('input')
+        .then(originalInput => {
+          cy.get('@plot-settings')
+            .find('.title-input')
+            .find('input')
+            .clear()
+            .type(testText)
+            .should('have.value', testText);
+
+          cy.get('@reset-button').should('be.enabled').click();
+
+          cy.get('@plot-settings')
+            .find('.title-input')
+            .find('input')
+            .should(resetInput => {
+              expect(originalInput.text()).to.equal(resetInput.text());
+            });
+        });
+    });
+  });
+}
+
+function axisChange(axis: Axis) {
+  const axisClass = axis === 'x' ? '.x-axis-settings' : '.y-axis-settings';
+  const axisLayerClass = axis === 'x' ? '.xaxislayer-above' : '.yaxislayer-above';
+
+  cy.get('@plot-settings')
+    .find(axisClass)
+    .find('mat-button-toggle-group')
+    .scrollIntoView()
+    .as('axis-toggles')
+    .contains('button', 'Log')
+    .should('be.visible')
+    .click()
+    .get('@plot')
+    .find(axisLayerClass)
+    .then(logTicks => {
+      cy.get('@axis-toggles')
+        .contains('button', 'Linear')
+        .scrollIntoView()
+        .should('be.visible')
+        .click()
+        .get('@plot')
+        .find(axisLayerClass)
+        .should(linearTicks => {
+          expect(logTicks).to.not.equal(linearTicks);
+        });
+    });
+}
+
+function axisTitleChange(axis: Axis) {
+  const axisClass = axis === 'x' ? '.x-axis-settings' : '.y-axis-settings';
+
+  cy.get('@plot-settings')
+    .find(axisClass)
+    .scrollIntoView()
+    .should('be.visible')
+    .find('.axis-title-input')
+    .find('input')
+    .clear()
+    .type(testText)
+    .should('have.value', testText);
+
+  cy.get('@plot').contains('text', testText).should('be.visible');
+
+  cy.get('@plot-settings')
+    .find(axisClass)
+    .scrollIntoView()
+    .should('be.visible')
+    .find('.axis-title-font-size-input')
+    .find('input')
+    .clear()
+    .type('20')
+    .should('have.value', '20');
+
+  cy.get('@plot').contains('text', testText).should('have.css', 'font-size', '20px');
+}
-- 
GitLab


From 99532af7f408876502abb759631ac36fccf9c493 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:24:16 -0600
Subject: [PATCH 24/49] add e2e utils

---
 projects/nshmp-apps/cypress/utils/index.ts |  7 ++++++
 projects/nshmp-apps/cypress/utils/utils.ts | 26 ++++++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 projects/nshmp-apps/cypress/utils/index.ts
 create mode 100644 projects/nshmp-apps/cypress/utils/utils.ts

diff --git a/projects/nshmp-apps/cypress/utils/index.ts b/projects/nshmp-apps/cypress/utils/index.ts
new file mode 100644
index 000000000..c556eb19e
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/index.ts
@@ -0,0 +1,7 @@
+export * from './dashboard.utils';
+export * from './gmm-lib.utils';
+export * from './hazard-lib.utils';
+export * from './nshmp-lib.utils';
+export * from './nshmp-template.utils';
+export * from './plot-lib.utils';
+export * from './utils';
diff --git a/projects/nshmp-apps/cypress/utils/utils.ts b/projects/nshmp-apps/cypress/utils/utils.ts
new file mode 100644
index 000000000..cf4eaf1d2
--- /dev/null
+++ b/projects/nshmp-apps/cypress/utils/utils.ts
@@ -0,0 +1,26 @@
+export function hasDownloadedFile(filename: string) {
+  const downloadsFolder = Cypress.config('downloadsFolder');
+  const file = `${downloadsFolder}/${filename}`;
+
+  cy.readFile(file).should('exist').should('have.length.above', 1);
+}
+
+export function setInput(inputClass: string, value: string) {
+  cy.get('nshmp-template-control-panel')
+    .find(inputClass)
+    .find('input')
+    .scrollIntoView()
+    .should('be.visible')
+    .clear()
+    .type(value)
+    .should('have.value', value);
+}
+export function selectMatOption(value: string) {
+  cy.get('.cdk-overlay-container')
+    .find('mat-option')
+    .each(option => {
+      if (option.attr('ng-reflect-value') === value) {
+        cy.wrap(option).click();
+      }
+    });
+}
-- 
GitLab


From 7a018437fb3bb501dda2f3c38a1a74b5a064c28e Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:24:29 -0600
Subject: [PATCH 25/49] Add main config

---
 projects/nshmp-apps/cypress.json | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/projects/nshmp-apps/cypress.json b/projects/nshmp-apps/cypress.json
index 775c1f6f0..1742b1477 100644
--- a/projects/nshmp-apps/cypress.json
+++ b/projects/nshmp-apps/cypress.json
@@ -1,11 +1,13 @@
 {
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../dist/cypress/nshmp-apps/downloads",
   "fileServerFolder": ".",
   "fixturesFolder": "./cypress/fixtures",
-  "integrationFolder": "./cypress/integration",
+  "integrationFolder": "./cypress/integration/source-models",
   "pluginsFile": "./cypress/plugins/index",
-  "supportFile": false,
-  "video": false,
-  "videosFolder": "../../dist/cypress/nshmp-apps/videos",
   "screenshotsFolder": "../../dist/cypress/nshmp-apps/screenshots",
-  "chromeWebSecurity": false
+  "supportFile": false,
+  "video": true,
+  "videoCompression": 15,
+  "videosFolder": "../../dist/cypress/nshmp-apps/videos"
 }
-- 
GitLab


From 274aab5647b0473950b29603becd6ba76cef1ef1 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:24:42 -0600
Subject: [PATCH 26/49] update tsconfig

---
 projects/nshmp-apps/cypress/tsconfig.json | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/projects/nshmp-apps/cypress/tsconfig.json b/projects/nshmp-apps/cypress/tsconfig.json
index ba634f447..7c57a901e 100644
--- a/projects/nshmp-apps/cypress/tsconfig.json
+++ b/projects/nshmp-apps/cypress/tsconfig.json
@@ -2,7 +2,9 @@
   "extends": "../../../tsconfig.json",
   "include": ["**/*.ts", "plugins/index.js"],
   "compilerOptions": {
+    "target": "es5",
+    "lib": ["ES2018", "dom"],
     "sourceMap": false,
-    "types": ["cypress"]
+    "types": ["cypress", "node"]
   }
 }
-- 
GitLab


From e3e3cb9947dc4bac1e7d457bec4e781de6093a9b Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:25:14 -0600
Subject: [PATCH 27/49] add plugin

---
 projects/nshmp-apps/cypress/plugins/index.js | 44 ++++++++++----------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/projects/nshmp-apps/cypress/plugins/index.js b/projects/nshmp-apps/cypress/plugins/index.js
index 6defa7096..db8bc0453 100644
--- a/projects/nshmp-apps/cypress/plugins/index.js
+++ b/projects/nshmp-apps/cypress/plugins/index.js
@@ -1,31 +1,33 @@
 // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
 // For more info, visit https://on.cypress.io/plugins-api
 
-const webpackPreprocessor = require('@cypress/webpack-preprocessor')
+const webpackPreprocessor = require('@cypress/webpack-preprocessor');
 
-module.exports = (on) => {
-  on('file:preprocessor', webpackPreprocessor(
-   {
-    webpackOptions: {
-      resolve: {
-        extensions: [".ts", ".tsx", ".js"],
+module.exports = on => {
+  on(
+    'file:preprocessor',
+    webpackPreprocessor({
+      webpackOptions: {
+        resolve: {
+          extensions: ['.ts', '.tsx', '.js'],
+        },
+        module: {
+          rules: [
+            {
+              test: /\.tsx?$/,
+              loader: 'ts-loader',
+              options: {transpileOnly: true},
+            },
+          ],
+        },
       },
-      module: {
-        rules: [
-          {
-            test: /\.tsx?$/,
-            loader: "ts-loader",
-            options: { transpileOnly: true }
-          }
-        ]
-      }
-    },
-  }));
+    })
+  );
 
   on('task', {
     log(message) {
-      console.log(message)
-      return null
+      console.log(message);
+      return null;
     },
-  })
+  });
 };
-- 
GitLab


From 627a7001c6ac6d01d3204243596e811f5c3aecc6 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Fri, 25 Mar 2022 17:26:14 -0600
Subject: [PATCH 28/49] Update deps

---
 package-lock.json | 14 +++++++-------
 package.json      |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index cb7f436da..6cebd28d3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
         "@angular/router": "^13.2.6",
         "@ghsc/disagg-d3": "^0.6.0",
         "@ghsc/nshmp-template": "^13.0.2",
-        "@ghsc/nshmp-utils": "^4.4.0",
+        "@ghsc/nshmp-utils": "^4.4.1",
         "@mapbox/mapbox-gl-geocoder": "^5.0.0",
         "@ngrx/effects": "^13.0.2",
         "@ngrx/router-store": "^13.0.2",
@@ -3834,9 +3834,9 @@
       }
     },
     "node_modules/@ghsc/nshmp-utils": {
-      "version": "4.4.0",
-      "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils/-/@ghsc/nshmp-utils-4.4.0.tgz",
-      "integrity": "sha1-l5Eas0KsOvGc+Mdc3tAqELXEelY=",
+      "version": "4.4.1",
+      "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils/-/@ghsc/nshmp-utils-4.4.1.tgz",
+      "integrity": "sha1-RuJQwEDMEvLyyWE7NdNe/tliCoA=",
       "dependencies": {
         "@mapbox/geojson-extent": "^1.0.0",
         "change-case": "^4.1.2",
@@ -30598,9 +30598,9 @@
       }
     },
     "@ghsc/nshmp-utils": {
-      "version": "4.4.0",
-      "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils/-/@ghsc/nshmp-utils-4.4.0.tgz",
-      "integrity": "sha1-l5Eas0KsOvGc+Mdc3tAqELXEelY=",
+      "version": "4.4.1",
+      "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils/-/@ghsc/nshmp-utils-4.4.1.tgz",
+      "integrity": "sha1-RuJQwEDMEvLyyWE7NdNe/tliCoA=",
       "requires": {
         "@mapbox/geojson-extent": "^1.0.0",
         "change-case": "^4.1.2",
diff --git a/package.json b/package.json
index c6ecd246f..60dc205b9 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
     "@angular/router": "^13.2.6",
     "@ghsc/disagg-d3": "^0.6.0",
     "@ghsc/nshmp-template": "^13.0.2",
-    "@ghsc/nshmp-utils": "^4.4.0",
+    "@ghsc/nshmp-utils": "^4.4.1",
     "@mapbox/mapbox-gl-geocoder": "^5.0.0",
     "@ngrx/effects": "^13.0.2",
     "@ngrx/router-store": "^13.0.2",
-- 
GitLab


From 48216726ae11e56e2a9706301e0ca21046ecf65b Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 07:33:25 -0600
Subject: [PATCH 29/49] Update to string siteClass

---
 .../hazard-lib/src/lib/models/response-spectra.model.ts    | 2 +-
 .../src/app/hazard/dynamic/utils/response-handler.util.ts  | 7 +------
 .../static/components/curve-data/curve-data.component.ts   | 2 +-
 .../components/spectrum-data/spectrum-data.component.ts    | 2 +-
 .../src/app/hazard/static/models/curve-table.model.ts      | 3 +--
 .../src/app/hazard/static/utils/response.handler.ts        | 2 +-
 6 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/projects/hazard-lib/src/lib/models/response-spectra.model.ts b/projects/hazard-lib/src/lib/models/response-spectra.model.ts
index ee99ed902..5c9b8150a 100644
--- a/projects/hazard-lib/src/lib/models/response-spectra.model.ts
+++ b/projects/hazard-lib/src/lib/models/response-spectra.model.ts
@@ -4,7 +4,7 @@ import {nshmpLib, nshmpWsUtils} from '@ghsc/nshmp-utils';
  * Response spectra wrapper for each site class
  */
 export interface ResponseSpectra {
-  siteClass: nshmpWsUtils.metadata.EnumParameterValues;
+  siteClass: string;
   imts: nshmpLib.Imt[];
   responseSpectrum: ResponseSpectrum[];
 }
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/utils/response-handler.util.ts b/projects/nshmp-apps/src/app/hazard/dynamic/utils/response-handler.util.ts
index 345a4cbb6..2ae560d30 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/utils/response-handler.util.ts
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/utils/response-handler.util.ts
@@ -88,12 +88,7 @@ export const responseSpectra = (
   });
 
   return {
-    siteClass: {
-      display: form.siteClass,
-      displayorder: 0,
-      id: 0,
-      value: form.siteClass,
-    },
+    siteClass: form.siteClass,
     responseSpectrum,
     imts: [...hazardCurves.keys()],
   };
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
index 189d6edd9..5ed979838 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
@@ -69,7 +69,7 @@ export class CurveDataComponent {
         td: xy.xs.map(x => (x ? x.toFixed(this.dataPrecision) : x)),
       };
       if (index === 0) {
-        xData.exportSectionBreakLabel = `Site class - ${response.metadata.siteClass.display}`;
+        xData.exportSectionBreakLabel = `Site class - ${response.metadata.siteClass}`;
       }
       tableData.push(xData);
 
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
index 64aeb0b2a..7f85aef6b 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
@@ -34,7 +34,7 @@ export class SpectrumDataComponent {
     return spectra.map(spectrum => {
       const tableData = responseSpectraToTableData(spectrum).map((data, index) => {
         if (index === 0) {
-          data.exportSectionBreakLabel = `Site Class - ${spectrum.siteClass.display}`;
+          data.exportSectionBreakLabel = `Site Class - ${spectrum.siteClass}`;
         }
         return data;
       });
diff --git a/projects/nshmp-apps/src/app/hazard/static/models/curve-table.model.ts b/projects/nshmp-apps/src/app/hazard/static/models/curve-table.model.ts
index 5c932a8ff..3998b12b5 100644
--- a/projects/nshmp-apps/src/app/hazard/static/models/curve-table.model.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/models/curve-table.model.ts
@@ -1,10 +1,9 @@
-import {nshmpWsUtils} from '@ghsc/nshmp-utils';
 import {TableData} from 'projects/nshmp-lib/src/lib/models/table-data.model';
 
 /**
  * Static hazard curve data table, table per site class
  */
 export interface StaticHazardCurveTable {
-  siteClass: nshmpWsUtils.metadata.EnumParameterValues;
+  siteClass: string;
   tableData: TableData[];
 }
diff --git a/projects/nshmp-apps/src/app/hazard/static/utils/response.handler.ts b/projects/nshmp-apps/src/app/hazard/static/utils/response.handler.ts
index 626932212..14f3f3fc6 100644
--- a/projects/nshmp-apps/src/app/hazard/static/utils/response.handler.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/utils/response.handler.ts
@@ -28,7 +28,7 @@ export const getResponseData = (
   const siteClass = state.form.controls.siteClass.value;
   return state.serviceResponses
     ? state.serviceResponses.response.find(response => {
-        return response.find(data => data.metadata.siteClass.value === siteClass);
+        return response.find(data => data.metadata.siteClass === siteClass);
       })
     : null;
 };
-- 
GitLab


From 08fb3b1ee67e73f091fa6b2c1d7a28480bf8387c Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 07:33:44 -0600
Subject: [PATCH 30/49] uncomment

---
 .../hazard/static/static-hazard.spec.ts       | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
index b31959955..29f544276 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
@@ -11,19 +11,19 @@ describe('Static Hazard Application', () => {
     cy.wait('@usage');
   });
 
-  // describe('Check nshmp-template', () => {
-  //   utils.hasNshmpTemplate(navigation());
-  // });
+  describe('Check nshmp-template', () => {
+    utils.hasNshmpTemplate(navigation());
+  });
 
   describe('Application', () => {
-    // utils.hasControlPanel();
-    // utils.hasPlotContent();
-    // utils.toggleControlPanel();
-    // utils.togglePlotPanel();
-    // utils.toggleSettingPanel();
-    // utils.hasHazardModelMenu();
-    // utils.changePlotSettings('.hazard-plot', '.hazard-settings');
-    // utils.changePlotSettings('.spectra-plot', '.spectra-settings');
+    utils.hasControlPanel();
+    utils.hasPlotContent();
+    utils.toggleControlPanel();
+    utils.togglePlotPanel();
+    utils.toggleSettingPanel();
+    utils.hasHazardModelMenu();
+    utils.changePlotSettings('.hazard-plot', '.hazard-settings');
+    utils.changePlotSettings('.spectra-plot', '.spectra-settings');
 
     it('Can set control panel', () => {
       setControlPanel();
-- 
GitLab


From 373ac73971f8585fff7de3d69cdf8dc32f58d7c8 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 07:34:00 -0600
Subject: [PATCH 31/49] update intercept

---
 projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts b/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts
index c6dbd8e6f..6db8a6217 100644
--- a/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts
+++ b/projects/nshmp-apps/cypress/utils/nshmp-template.utils.ts
@@ -150,8 +150,7 @@ export function submitFormCheckPlots(options: CheckPlots) {
         .find(options.secondPlotClass)
         .then(originalSecondPlot => {
           if (options.intercept) {
-            cy.task('log', 'intercept');
-            cy.intercept('/ws/nshmp**').as('service-call');
+            cy.intercept('**').as('service-call');
           }
 
           cy.get('nshmp-lib-control-panel-buttons').find('.submit-button').find('button').click();
-- 
GitLab


From 8ac3205392a35c82f899eab15a55e0fc6797bb3d Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 07:34:53 -0600
Subject: [PATCH 32/49] Add cypress config

---
 angular.json                                        |  2 +-
 projects/nshmp-apps/cypress.json                    | 13 -------------
 projects/nshmp-apps/cypress/config/cypress-all.json | 13 +++++++++++++
 3 files changed, 14 insertions(+), 14 deletions(-)
 delete mode 100644 projects/nshmp-apps/cypress.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-all.json

diff --git a/angular.json b/angular.json
index fd4bf1668..fb51b308a 100644
--- a/angular.json
+++ b/angular.json
@@ -181,7 +181,7 @@
         "e2e": {
           "builder": "@nrwl/cypress:cypress",
           "options": {
-            "cypressConfig": "projects/nshmp-apps/cypress.json",
+            "cypressConfig": "projects/nshmp-apps/cypress/config/cypress-all.json",
             "tsConfig": "projects/nshmp-apps/cypress/tsconfig.json",
             "devServerTarget": "nshmp-apps:serve"
           },
diff --git a/projects/nshmp-apps/cypress.json b/projects/nshmp-apps/cypress.json
deleted file mode 100644
index 1742b1477..000000000
--- a/projects/nshmp-apps/cypress.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "chromeWebSecurity": false,
-  "downloadsFolder": "../../dist/cypress/nshmp-apps/downloads",
-  "fileServerFolder": ".",
-  "fixturesFolder": "./cypress/fixtures",
-  "integrationFolder": "./cypress/integration/source-models",
-  "pluginsFile": "./cypress/plugins/index",
-  "screenshotsFolder": "../../dist/cypress/nshmp-apps/screenshots",
-  "supportFile": false,
-  "video": true,
-  "videoCompression": 15,
-  "videosFolder": "../../dist/cypress/nshmp-apps/videos"
-}
diff --git a/projects/nshmp-apps/cypress/config/cypress-all.json b/projects/nshmp-apps/cypress/config/cypress-all.json
new file mode 100644
index 000000000..4ab195c07
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-all.json
@@ -0,0 +1,13 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videoCompression": 15,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
-- 
GitLab


From 08399c4c27f969c000b6e3899697fd30017416ca Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 07:39:11 -0600
Subject: [PATCH 33/49] Try parallel

---
 .gitlab-ci.yml | 3 ++-
 package.json   | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3b3be1fac..981122313 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -211,9 +211,10 @@ Lint:
 End to End Tests:
   image: cypress/base:16.14.0
   needs: []
+  parallel: 4
   script:
     - npm ci
-    - npm run cy:run
+    - npm run cy:ci
   stage: test
   tags:
     - build
diff --git a/package.json b/package.json
index 60dc205b9..d15bc1d81 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
   "scripts": {
     "build": "ng build nshmp-apps",
     "build:prod": "ng build nshmp-apps --configuration production",
+    "cy:ci": "ng e2e nshmp-apps --parallel",
     "cy:run": "ng e2e nshmp-apps",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
-- 
GitLab


From 31072261a3a114fb767c7d18dcdaa04de59e54eb Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:07:05 -0600
Subject: [PATCH 34/49] add record flag

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index d15bc1d81..27944003b 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "scripts": {
     "build": "ng build nshmp-apps",
     "build:prod": "ng build nshmp-apps --configuration production",
-    "cy:ci": "ng e2e nshmp-apps --parallel",
+    "cy:ci": "ng e2e nshmp-apps --parallel --record",
     "cy:run": "ng e2e nshmp-apps",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
-- 
GitLab


From 42d24726a1d765fe18653b845174f47689925974 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:38:14 -0600
Subject: [PATCH 35/49] add cypress config

---
 projects/nshmp-apps/cypress/config/cypress-all.json  |  1 -
 .../nshmp-apps/cypress/config/cypress-dashboard.json | 12 ++++++++++++
 projects/nshmp-apps/cypress/config/cypress-dev.json  | 12 ++++++++++++
 projects/nshmp-apps/cypress/config/cypress-gmm.json  | 12 ++++++++++++
 .../nshmp-apps/cypress/config/cypress-hazard.json    | 12 ++++++++++++
 .../nshmp-apps/cypress/config/cypress-services.json  | 12 ++++++++++++
 .../cypress/config/cypress-source-models.json        | 12 ++++++++++++
 7 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-dashboard.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-dev.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-gmm.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-hazard.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-services.json
 create mode 100644 projects/nshmp-apps/cypress/config/cypress-source-models.json

diff --git a/projects/nshmp-apps/cypress/config/cypress-all.json b/projects/nshmp-apps/cypress/config/cypress-all.json
index 4ab195c07..bf2840ebd 100644
--- a/projects/nshmp-apps/cypress/config/cypress-all.json
+++ b/projects/nshmp-apps/cypress/config/cypress-all.json
@@ -8,6 +8,5 @@
   "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
   "supportFile": false,
   "video": true,
-  "videoCompression": 15,
   "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
 }
diff --git a/projects/nshmp-apps/cypress/config/cypress-dashboard.json b/projects/nshmp-apps/cypress/config/cypress-dashboard.json
new file mode 100644
index 000000000..9b4571f9f
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-dashboard.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/dashboard",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
diff --git a/projects/nshmp-apps/cypress/config/cypress-dev.json b/projects/nshmp-apps/cypress/config/cypress-dev.json
new file mode 100644
index 000000000..b868a5658
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-dev.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/dev",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
diff --git a/projects/nshmp-apps/cypress/config/cypress-gmm.json b/projects/nshmp-apps/cypress/config/cypress-gmm.json
new file mode 100644
index 000000000..34ea708d8
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-gmm.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/gmm",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
diff --git a/projects/nshmp-apps/cypress/config/cypress-hazard.json b/projects/nshmp-apps/cypress/config/cypress-hazard.json
new file mode 100644
index 000000000..78d7af41a
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-hazard.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/hazard",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
diff --git a/projects/nshmp-apps/cypress/config/cypress-services.json b/projects/nshmp-apps/cypress/config/cypress-services.json
new file mode 100644
index 000000000..c8aab1ef8
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-services.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/services",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
diff --git a/projects/nshmp-apps/cypress/config/cypress-source-models.json b/projects/nshmp-apps/cypress/config/cypress-source-models.json
new file mode 100644
index 000000000..f912962f6
--- /dev/null
+++ b/projects/nshmp-apps/cypress/config/cypress-source-models.json
@@ -0,0 +1,12 @@
+{
+  "chromeWebSecurity": false,
+  "downloadsFolder": "../../../../dist/cypress/nshmp-apps/downloads",
+  "fileServerFolder": ".",
+  "fixturesFolder": "../fixtures",
+  "integrationFolder": "../integration/source-models",
+  "pluginsFile": "../plugins/index",
+  "screenshotsFolder": "../../../../dist/cypress/nshmp-apps/screenshots",
+  "supportFile": false,
+  "video": true,
+  "videosFolder": "../../../../dist/cypress/nshmp-apps/videos"
+}
-- 
GitLab


From 9df7527845e8c34a363e0d099afb5d92eeb236a3 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:40:57 -0600
Subject: [PATCH 36/49] Setup cypress tests

---
 .gitlab-ci.yml | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 981122313..69dd16e80 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,8 @@ variables:
   BASE_HREF: nshmp
   GITLAB_TOKEN: '${CI_JOB_TOKEN}'
   IMAGE_NAME: ${CODE_REGISTRY_IMAGE}/${CI_PROJECT_NAME}:${ENVIRONMENT}-${CI_COMMIT_SHORT_SHA}
+  npm_config_cache: '$CI_PROJECT_DIR/.npm'
+  CYPRESS_CACHE_FOLDER: '$CI_PROJECT_DIR/cache/Cypress'
 
 # Do not run for merge requests
 workflow:
@@ -9,6 +11,13 @@ workflow:
     - if: $CI_COMMIT_TAG
     - if: $CI_COMMIT_BRANCH
 
+cache:
+  key: ${CI_COMMIT_REF_SLUG}
+  paths:
+    - .npm
+    - cache/Cypress
+    - node_modules
+
 stages:
   - init
   - security
@@ -137,6 +146,7 @@ Init:
   artifacts:
     paths:
       - node_modules
+      -
   image: ${DEVOPS_REGISTRY}usgs/node:16
   script:
     - npm ci
@@ -211,10 +221,17 @@ Lint:
 End to End Tests:
   image: cypress/base:16.14.0
   needs: []
-  parallel: 4
+  parallel:
+    matrix:
+      - CMD: cy:run:dashboard
+      - CMD: cy:run:dev
+      - CMD: cy:run:gmm
+      - CMD: cy:run:hazard
+      - CMD: cy:run:services
+      - CMD: cy:run:source-models
   script:
     - npm ci
-    - npm run cy:ci
+    - npm run ${CMD}
   stage: test
   tags:
     - build
-- 
GitLab


From c38000ed85f8c86d461c4c6ba8332230b0e99e7e Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:41:09 -0600
Subject: [PATCH 37/49] Add cypress commands

---
 package.json | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 27944003b..79ae75d53 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,13 @@
   "scripts": {
     "build": "ng build nshmp-apps",
     "build:prod": "ng build nshmp-apps --configuration production",
-    "cy:ci": "ng e2e nshmp-apps --parallel --record",
     "cy:run": "ng e2e nshmp-apps",
+    "cy:run:dashboard": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-dashboard.json",
+    "cy:run:dev": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-dev.json",
+    "cy:run:gmm": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-gmm.json",
+    "cy:run:hazard": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-hazard.json",
+    "cy:run:services": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-services.json",
+    "cy:run:surce-models": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
     "lint": "ng lint",
-- 
GitLab


From 2478fc817e44d7486e00110468fbadcaa5c101b6 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:41:17 -0600
Subject: [PATCH 38/49] Update url

---
 .../cypress/integration/hazard/static/static-hazard.spec.ts     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
index 29f544276..68867d6e3 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/conus-2018/static/hazard';
+const url = 'https://**/ws/nshmp/*/static/hazard';
 
 describe('Static Hazard Application', () => {
   beforeEach(() => {
-- 
GitLab


From f8aaeaf11b3d852fa94c1e93559aaa1f16c8c506 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:43:17 -0600
Subject: [PATCH 39/49] Update url

---
 .../cypress/integration/gmm/distance/gmm-distance.spec.ts       | 2 +-
 .../cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts     | 2 +-
 .../cypress/integration/gmm/spectra/response-spectra.spec.ts    | 2 +-
 .../nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts | 2 +-
 .../cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts   | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
index 1a6ed1673..7b1d46ad3 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/data/gmm/distance';
+const url = 'https://earthquake.usgs.gov/ws/nshmp/data/gmm/distance';
 
 describe('Ground Motion vs. Distance Application', () => {
   beforeEach(() => {
diff --git a/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
index 52cc01ec7..478200aac 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/data/gmm/magnitude';
+const url = 'https://earthquake.usgs.gov/ws/nshmp/data/gmm/magnitude';
 
 describe('Ground Motion vs. Magnitude', () => {
   beforeEach(() => {
diff --git a/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
index 031fa6947..b0bfe12b4 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/data/gmm/spectra';
+const url = 'https://earthquake.usgs.gov/ws/nshmp/data/gmm/spectra';
 
 describe('Response Spectra', () => {
   beforeEach(() => {
diff --git a/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
index 63aa09420..1f7888f8e 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/disagg/disagg.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/conus-2018/dynamic/disagg';
+const url = 'https://earthquake.usgs.gov/ws/nshmp/*/dynamic/disagg';
 
 describe('Disagg Application', () => {
   beforeEach(() => {
diff --git a/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
index 7ca1abfd9..5e268b570 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
@@ -1,7 +1,7 @@
 import {navigation} from '../../../../../nshmp-lib/src/lib/utils/navigation.utils';
 import * as utils from '../../../utils';
 
-const url = '/ws/nshmp/conus-2018/dynamic/hazard';
+const url = 'https://earthquake.usgs.gov/ws/nshmp/*/dynamic/hazard';
 
 describe('Dynamic Hazard Application', () => {
   beforeEach(() => {
-- 
GitLab


From 046ee7864b18e49fbce83b31b7ab01018a622b97 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 08:45:16 -0600
Subject: [PATCH 40/49] REmove array

---
 .gitlab-ci.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 69dd16e80..55719f5a4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -146,7 +146,6 @@ Init:
   artifacts:
     paths:
       - node_modules
-      -
   image: ${DEVOPS_REGISTRY}usgs/node:16
   script:
     - npm ci
-- 
GitLab


From 87efb8b4740748ca33a917a9b280f9df41e53bcb Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 09:04:08 -0600
Subject: [PATCH 41/49] Fix commands

---
 package.json | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/package.json b/package.json
index 79ae75d53..1ff30721d 100644
--- a/package.json
+++ b/package.json
@@ -5,12 +5,12 @@
     "build": "ng build nshmp-apps",
     "build:prod": "ng build nshmp-apps --configuration production",
     "cy:run": "ng e2e nshmp-apps",
-    "cy:run:dashboard": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-dashboard.json",
-    "cy:run:dev": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-dev.json",
-    "cy:run:gmm": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-gmm.json",
-    "cy:run:hazard": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-hazard.json",
-    "cy:run:services": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-services.json",
-    "cy:run:surce-models": "cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
+    "cy:run:dashboard": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-dashboard.json",
+    "cy:run:dev": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-dev.json",
+    "cy:run:gmm": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-gmm.json",
+    "cy:run:hazard": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-hazard.json",
+    "cy:run:services": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-services.json",
+    "cy:run:surce-models": "npm run -- cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
     "lint": "ng lint",
-- 
GitLab


From 9a5ac7c3668e96e49c4e7d26f6be20a12d923105 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 09:04:50 -0600
Subject: [PATCH 42/49] Use artifacts

---
 .gitlab-ci.yml | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 55719f5a4..fa67922f4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,13 +11,6 @@ workflow:
     - if: $CI_COMMIT_TAG
     - if: $CI_COMMIT_BRANCH
 
-cache:
-  key: ${CI_COMMIT_REF_SLUG}
-  paths:
-    - .npm
-    - cache/Cypress
-    - node_modules
-
 stages:
   - init
   - security
@@ -145,6 +138,8 @@ stages:
 Init:
   artifacts:
     paths:
+      - .npm
+      - cache/Cypress
       - node_modules
   image: ${DEVOPS_REGISTRY}usgs/node:16
   script:
@@ -219,7 +214,8 @@ Lint:
 
 End to End Tests:
   image: cypress/base:16.14.0
-  needs: []
+  needs:
+    - Init
   parallel:
     matrix:
       - CMD: cy:run:dashboard
-- 
GitLab


From b607f689dd90e4c5e284468230b703375e52c9be Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 10:39:46 -0600
Subject: [PATCH 43/49] use rebuild

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fa67922f4..a0df95c41 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -225,7 +225,7 @@ End to End Tests:
       - CMD: cy:run:services
       - CMD: cy:run:source-models
   script:
-    - npm ci
+    - npm rebuild
     - npm run ${CMD}
   stage: test
   tags:
-- 
GitLab


From 7238b2a49001ef3036b09147f18e0c80dc3b6152 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 10:40:10 -0600
Subject: [PATCH 44/49] test tabs

---
 .../gmm/distance/gmm-distance.spec.ts         |  34 +++++
 .../gmm/magnitude/gmm-magnitude.spec.ts       |  56 +++++++
 .../gmm/spectra/response-spectra.spec.ts      | 142 ++++++++++++------
 .../hazard/dynamic/dynamic-hazard.spec.ts     |  56 +++++++
 .../hazard/static/static-hazard.spec.ts       |  56 +++++++
 .../cypress/utils/nshmp-lib.utils.ts          |  20 +++
 projects/nshmp-apps/cypress/utils/utils.ts    |   8 +
 .../components/content/content.component.html |   4 +-
 .../components/content/content.component.html |   6 +-
 .../components/content/content.component.html |   6 +-
 .../components/content/content.component.html |   6 +-
 .../components/content/content.component.ts   |   2 +-
 .../components/content/content.component.html |   6 +-
 .../curve-data/curve-data.component.html      |   4 +-
 .../curve-data/curve-data.component.ts        |   2 +-
 .../spectrum-data.component.html              |   4 +-
 .../spectrum-data/spectrum-data.component.ts  |   2 +-
 17 files changed, 350 insertions(+), 64 deletions(-)

diff --git a/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
index 7b1d46ad3..a393697ed 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/distance/gmm-distance.spec.ts
@@ -56,6 +56,40 @@ describe('Ground Motion vs. Distance Application', () => {
 
       utils.controlPanelButtonsEnabled(buttons);
     });
+
+    describe('Application Tabs', () => {
+      it('Has plot tab', () => {
+        cy.get('plotly-plot');
+
+        utils.hasApplicationTab('.medians-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasApplicationTab('.plots-tab');
+
+        cy.get('plotly-plot');
+      });
+
+      it('Has medians tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlot({
+          plotClass: '.distance-plot',
+        });
+
+        utils.hasApplicationTab('.medians-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('gmm-distance-means.csv');
+        utils.hasDataTable();
+      });
+    });
   });
 });
 
diff --git a/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
index 478200aac..c28bbad1a 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/magnitude/gmm-magnitude.spec.ts
@@ -65,6 +65,62 @@ describe('Ground Motion vs. Magnitude', () => {
 
       utils.controlPanelButtonsEnabled(buttons);
     });
+
+    describe('Application Tabs', () => {
+      it('Has plot tab', () => {
+        cy.get('plotly-plot');
+
+        utils.hasApplicationTab('.medians-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasApplicationTab('.plots-tab');
+
+        cy.get('plotly-plot');
+      });
+
+      it('Has medians tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.mean-plot',
+          secondPlotClass: '.sigma-plot',
+        });
+
+        utils.hasApplicationTab('.medians-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('gmm-magnitude-means.csv');
+        utils.hasDataTable();
+      });
+
+      it('Has sigmas tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.mean-plot',
+          secondPlotClass: '.sigma-plot',
+        });
+
+        utils.hasApplicationTab('.sigmas-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('gmm-magnitude-sigmas.csv');
+        utils.hasDataTable();
+      });
+    });
   });
 });
 
diff --git a/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
index b0bfe12b4..2c316034f 100644
--- a/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/gmm/spectra/response-spectra.spec.ts
@@ -11,59 +11,115 @@ describe('Response Spectra', () => {
     cy.wait('@usage');
   });
 
-  describe('Check nshmp-template', () => {
-    utils.hasNshmpTemplate(navigation());
-  });
+  // describe('Check nshmp-template', () => {
+  //   utils.hasNshmpTemplate(navigation());
+  // });
 
   describe('Application', () => {
-    utils.hasControlPanel();
-    utils.hasPlotContent();
-    utils.toggleControlPanel();
-    utils.togglePlotPanel();
-    utils.toggleSettingPanel();
-    utils.changePlotSettings('.spectra-plot', '.spectra-settings');
-    utils.changePlotSettings('.sigma-plot', '.sigma-settings');
-    utils.hasGmmMenu();
-
-    it('Can set GMM menus', () => {
-      setGmmMenus();
-    });
+    //   utils.hasControlPanel();
+    //   utils.hasPlotContent();
+    //   utils.toggleControlPanel();
+    //   utils.togglePlotPanel();
+    //   utils.toggleSettingPanel();
+    //   utils.changePlotSettings('.spectra-plot', '.spectra-settings');
+    //   utils.changePlotSettings('.sigma-plot', '.sigma-settings');
+    //   utils.hasGmmMenu();
+
+    //   it('Can set GMM menus', () => {
+    //     setGmmMenus();
+    //   });
+
+    //   it('Can set event parameters', () => {
+    //     setEventParameters();
+    //   });
+
+    //   it('Can set geometry parameters', () => {
+    //     utils.setSourceGeometryParameters();
+    //   });
+
+    //   it('Can set site parameters', () => {
+    //     utils.setSiteParameters();
+    //   });
+
+    //   it('Can set path parameters', () => {
+    //     setPathParameters();
+    //   });
+
+    //   it('Can reset control panel inputs', () => {
+    //     utils.canResetControlPanel(() => setControlPanel());
+    //   });
+
+    //   it('Calls service and plots response', () => {
+    //     const buttons: utils.ControlPanelButtons = {
+    //       hasResetButton: false,
+    //       hasServiceCallButton: true,
+    //       hasSubmitButton: true,
+    //     };
+    //     utils.controlPanelButtonsNotEnabled(buttons);
+
+    //     setControlPanel();
+    //     utils.submitFormCheckPlots({
+    //       firstPlotClass: '.spectra-plot',
+    //       secondPlotClass: '.sigma-plot',
+    //     });
+
+    //     utils.controlPanelButtonsEnabled(buttons);
+    //   });
+
+    describe('Application Tabs', () => {
+      it('Has plot tab', () => {
+        cy.get('plotly-plot');
+
+        utils.hasApplicationTab('.medians-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasApplicationTab('.plots-tab');
+
+        cy.get('plotly-plot');
+      });
 
-    it('Can set event parameters', () => {
-      setEventParameters();
-    });
+      it('Has medians tab', () => {
+        cy.get('plotly-plot');
 
-    it('Can set geometry parameters', () => {
-      utils.setSourceGeometryParameters();
-    });
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
 
-    it('Can set site parameters', () => {
-      utils.setSiteParameters();
-    });
+        setControlPanel();
 
-    it('Can set path parameters', () => {
-      setPathParameters();
-    });
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.spectra-plot',
+          secondPlotClass: '.sigma-plot',
+        });
 
-    it('Can reset control panel inputs', () => {
-      utils.canResetControlPanel(() => setControlPanel());
-    });
+        utils.hasApplicationTab('.medians-tab');
 
-    it('Calls service and plots response', () => {
-      const buttons: utils.ControlPanelButtons = {
-        hasResetButton: false,
-        hasServiceCallButton: true,
-        hasSubmitButton: true,
-      };
-      utils.controlPanelButtonsNotEnabled(buttons);
-
-      setControlPanel();
-      utils.submitFormCheckPlots({
-        firstPlotClass: '.spectra-plot',
-        secondPlotClass: '.sigma-plot',
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('spectra-medians.csv');
+        utils.hasDataTable();
       });
 
-      utils.controlPanelButtonsEnabled(buttons);
+      it('Has sigmas tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.spectra-plot',
+          secondPlotClass: '.sigma-plot',
+        });
+
+        utils.hasApplicationTab('.sigmas-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('spectra-sigmas.csv');
+        utils.hasDataTable();
+      });
     });
   });
 });
diff --git a/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
index 5e268b570..032ed0c67 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/dynamic/dynamic-hazard.spec.ts
@@ -46,6 +46,62 @@ describe('Dynamic Hazard Application', () => {
 
       utils.controlPanelButtonsEnabled(buttons);
     });
+
+    describe('Application Tabs', () => {
+      it('Has plot tab', () => {
+        cy.get('plotly-plot');
+
+        utils.hasApplicationTab('.hazard-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasApplicationTab('.plots-tab');
+
+        cy.get('plotly-plot');
+      });
+
+      it('Has medians tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.hazard-plot',
+          secondPlotClass: '.spectra-plot',
+        });
+
+        utils.hasApplicationTab('.hazard-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('hazard-curves-total-760.csv');
+        utils.hasDataTable();
+      });
+
+      it('Has spectra tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.hazard-plot',
+          secondPlotClass: '.spectra-plot',
+        });
+
+        utils.hasApplicationTab('.spectra-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('response-spectra-total-760.csv');
+        utils.hasDataTable();
+      });
+    });
   });
 });
 
diff --git a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
index 68867d6e3..84352a15b 100644
--- a/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
+++ b/projects/nshmp-apps/cypress/integration/hazard/static/static-hazard.spec.ts
@@ -46,6 +46,62 @@ describe('Static Hazard Application', () => {
 
       utils.controlPanelButtonsEnabled(buttons);
     });
+
+    describe('Application Tabs', () => {
+      it('Has plot tab', () => {
+        cy.get('plotly-plot');
+
+        utils.hasApplicationTab('.hazard-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasApplicationTab('.plots-tab');
+
+        cy.get('plotly-plot');
+      });
+
+      it('Has medians tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.hazard-plot',
+          secondPlotClass: '.spectra-plot',
+        });
+
+        utils.hasApplicationTab('.hazard-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('static-hazard-curves-CONUS_2018-(-118,34).csv');
+        utils.hasDataTable();
+      });
+
+      it('Has spectra tab', () => {
+        cy.get('plotly-plot');
+
+        utils.exportDataTableNotExist();
+        utils.dataTableNotExist();
+
+        setControlPanel();
+
+        utils.submitFormCheckPlots({
+          firstPlotClass: '.hazard-plot',
+          secondPlotClass: '.spectra-plot',
+        });
+
+        utils.hasApplicationTab('.spectra-tab');
+
+        cy.get('plotly-plot').should('not.exist');
+
+        utils.hasExportDataTable('static-hazard-spectra-CONUS_2018-(-118,34).csv');
+        utils.hasDataTable();
+      });
+    });
   });
 });
 
diff --git a/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts b/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts
index 61eaa9d78..3c3e93278 100644
--- a/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts
+++ b/projects/nshmp-apps/cypress/utils/nshmp-lib.utils.ts
@@ -1,3 +1,5 @@
+import {hasDownloadedFile} from './utils';
+
 export interface ControlPanelButtons {
   hasSubmitButton: boolean;
   hasServiceCallButton: boolean;
@@ -52,6 +54,24 @@ export function controlPanelButtonsNotEnabled(buttons: ControlPanelButtons) {
   }
 }
 
+export function exportDataTableNotExist() {
+  cy.get('nshmp-lib-export-data-table').should('not.exist');
+}
+
+export function dataTableNotExist() {
+  cy.get('nshmp-lib-data-table').should('not.exist');
+}
+
+export function hasExportDataTable(filename: string) {
+  cy.get('nshmp-lib-export-data-table').find('button').should('be.enabled').click();
+
+  hasDownloadedFile(filename);
+}
+
+export function hasDataTable() {
+  cy.get('nshmp-lib-data-table').find('table').find('td').should('have.length.greaterThan', 1);
+}
+
 export function hasServiceCallPopUp() {
   cy.get('nshmp-lib-url-bottom-sheet').should('be.visible');
 }
diff --git a/projects/nshmp-apps/cypress/utils/utils.ts b/projects/nshmp-apps/cypress/utils/utils.ts
index cf4eaf1d2..c72f495e1 100644
--- a/projects/nshmp-apps/cypress/utils/utils.ts
+++ b/projects/nshmp-apps/cypress/utils/utils.ts
@@ -1,3 +1,11 @@
+export function hasApplicationTab(tabClass: string) {
+  cy.get('nshmp-template-plot-content')
+    .find('mat-tab-group')
+    .find(tabClass)
+    .should('be.visible')
+    .click();
+}
+
 export function hasDownloadedFile(filename: string) {
   const downloadsFolder = Cypress.config('downloadsFolder');
   const file = `${downloadsFolder}/${filename}`;
diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html
index 75c68e3fd..f39932be3 100644
--- a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html
+++ b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html
@@ -1,11 +1,11 @@
 <div class="height-full overflow-auto">
   <mat-tab-group dynamicHeight (selectedTabChange)="gmmDistanceFacade.plotRedraw()">
-    <mat-tab label="Plots">
+    <mat-tab labelClass="plots-tab" label="Plots">
       <div>
         <app-plots></app-plots>
       </div>
     </mat-tab>
-    <mat-tab label="Medians">
+    <mat-tab labelClass="medians-tab" label="Medians">
       <div>
         <nshmp-lib-export-data-table
           [table]="table$ | async"
diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/content/content.component.html b/projects/nshmp-apps/src/app/gmm/magnitude/components/content/content.component.html
index 251d6b465..5d1cd287b 100644
--- a/projects/nshmp-apps/src/app/gmm/magnitude/components/content/content.component.html
+++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/content/content.component.html
@@ -1,10 +1,10 @@
 <mat-tab-group class="height-full" (selectedTabChange)="facade.plotRedraw()">
-  <mat-tab label="Plots">
+  <mat-tab labelClass="plots-tab" label="Plots">
     <div>
       <app-plots></app-plots>
     </div>
   </mat-tab>
-  <mat-tab label="Medians">
+  <mat-tab labelClass="medians-tab" label="Medians">
     <div>
       <nshmp-lib-export-data-table
         [table]="meanTable$ | async"
@@ -15,7 +15,7 @@
       <nshmp-lib-data-table [table]="meanTable$ | async"> </nshmp-lib-data-table>
     </div>
   </mat-tab>
-  <mat-tab label="Sigmas">
+  <mat-tab labelClass="sigmas-tab" label="Sigmas">
     <div>
       <nshmp-lib-export-data-table
         [table]="sigmaTable$ | async"
diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html
index 0cae82f07..f71515a47 100644
--- a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html
+++ b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html
@@ -1,10 +1,10 @@
 <mat-tab-group class="height-full" (selectedTabChange)="facade.plotRedraw()">
-  <mat-tab label="Plots">
+  <mat-tab labelClass="plots-tab" label="Plots">
     <div>
       <app-plots></app-plots>
     </div>
   </mat-tab>
-  <mat-tab label="Medians">
+  <mat-tab labelClass="medians-tab" label="Medians">
     <div>
       <nshmp-lib-export-data-table
         [table]="spectraTable$ | async"
@@ -14,7 +14,7 @@
       <nshmp-lib-data-table [table]="spectraTable$ | async"></nshmp-lib-data-table>
     </div>
   </mat-tab>
-  <mat-tab label="Sigmas">
+  <mat-tab labelClass="sigmas-tab" label="Sigmas">
     <div>
       <nshmp-lib-export-data-table
         [table]="sigmaTable$ | async"
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.html b/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.html
index 307099403..c6ef854db 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.html
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.html
@@ -1,10 +1,10 @@
 <mat-tab-group class="height-full" (selectedTabChange)="facade.plotRedraw()">
-  <mat-tab label="Plots">
+  <mat-tab labelClass="plots-tab" label="Plots">
     <div>
       <app-plots></app-plots>
     </div>
   </mat-tab>
-  <mat-tab label="Hazard Curve Data">
+  <mat-tab labelClass="hazard-tab" label="Hazard Curve Data">
     <div>
       <nshmp-lib-export-data-table
         [table]="hazardTableData$ | async"
@@ -15,7 +15,7 @@
       <nshmp-lib-data-table [table]="hazardTableData$ | async"> </nshmp-lib-data-table>
     </div>
   </mat-tab>
-  <mat-tab label="Response Spectrum Data">
+  <mat-tab labelClass="spectra-tab" label="Response Spectrum Data">
     <div>
       <nshmp-lib-export-data-table
         [table]="spectraTableData$ | async"
diff --git a/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.ts b/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.ts
index 089d8dfcb..9a54bd9cf 100644
--- a/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.ts
+++ b/projects/nshmp-apps/src/app/hazard/dynamic/components/content/content.component.ts
@@ -44,7 +44,7 @@ export class ContentComponent {
   );
 
   spectraFileName$ = this.facade.form$.pipe(
-    map(form => `response-spectra-${form.value.sourceType}-${form.value.vs30}`)
+    map(form => `response-spectra-${form.value.sourceType}-${form.value.vs30}.csv`)
   );
 
   constructor(public facade: DynamicHazardAppFacade) {}
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/content/content.component.html b/projects/nshmp-apps/src/app/hazard/static/components/content/content.component.html
index c14d3ae9a..db31a1ddd 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/content/content.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/content/content.component.html
@@ -1,15 +1,15 @@
 <mat-tab-group class="height-full" (selectedTabChange)="staticHazardFacade.plotRedraw()">
-  <mat-tab label="Plots">
+  <mat-tab labelClass="plots-tab" label="Plots">
     <div>
       <app-plots></app-plots>
     </div>
   </mat-tab>
-  <mat-tab label="Hazard Curve Data">
+  <mat-tab labelClass="hazard-tab" label="Hazard Curve Data">
     <div>
       <app-curve-data></app-curve-data>
     </div>
   </mat-tab>
-  <mat-tab label="Response Spectrum Data">
+  <mat-tab labelClass="spectra-tab" label="Response Spectrum Data">
     <div>
       <app-spectrum-data></app-spectrum-data>
     </div>
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.html b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.html
index 96021a771..8b39b1ce2 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.html
@@ -9,10 +9,10 @@
 <mat-accordion *ngIf="serviceResponse$ | async">
   <mat-expansion-panel
     *ngFor="let table of tables$ | async"
-    [expanded]="(siteClass$ | async) === table.siteClass.value"
+    [expanded]="(siteClass$ | async) === table.siteClass"
   >
     <mat-expansion-panel-header>
-      {{ table.siteClass.display }}
+      {{ table.siteClass }}
     </mat-expansion-panel-header>
 
     <ng-template matExpansionPanelContent>
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
index 5ed979838..3653bdc62 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/components/curve-data/curve-data.component.ts
@@ -20,7 +20,7 @@ export class CurveDataComponent {
   hazardFilename$ = this.facade.form$.pipe(
     map(form => {
       const values = form.value;
-      return `static-hazard-curves-${values.model}-(${values.longitude},${values.latitude})`;
+      return `static-hazard-curves-${values.model}-(${values.longitude},${values.latitude}).csv`;
     })
   );
   serviceResponse$ = this.facade.serviceResponse$;
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.html b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.html
index cc1f7da0b..754fae9c0 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.html
+++ b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.html
@@ -9,10 +9,10 @@
 <mat-accordion *ngIf="responseSpectra$ | async">
   <mat-expansion-panel
     *ngFor="let table of tables$ | async"
-    [expanded]="(siteClass$ | async) === table.siteClass.value"
+    [expanded]="(siteClass$ | async) === table.siteClass"
   >
     <mat-expansion-panel-header>
-      {{ table.siteClass.display }}
+      {{ table.siteClass }}
     </mat-expansion-panel-header>
 
     <nshmp-lib-data-table [hideDownloadButton]="true" [table]="table.tableData">
diff --git a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
index 7f85aef6b..9569e82b4 100644
--- a/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
+++ b/projects/nshmp-apps/src/app/hazard/static/components/spectrum-data/spectrum-data.component.ts
@@ -21,7 +21,7 @@ export class SpectrumDataComponent {
   spectraFilename$ = this.facade.form$.pipe(
     map(form => {
       const values = form.value;
-      return `static-hazard-spectra-${values.model}-(${values.longitude},${values.latitude})`;
+      return `static-hazard-spectra-${values.model}-(${values.longitude},${values.latitude}).csv`;
     })
   );
   tables$ = this.facade.responseSpectra$.pipe(map(spectra => this.spectraToTableData(spectra)));
-- 
GitLab


From cb0a5ec52f0ff88afc5a7afad1b1c6fdf3b368ed Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 11:00:28 -0600
Subject: [PATCH 45/49] Add artifacts

---
 .gitlab-ci.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a0df95c41..ab28408f2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -167,6 +167,9 @@ NPM Audit:
 ####
 
 Build Project:
+  artifacts:
+    paths:
+      - .angular
   image: ${DEVOPS_REGISTRY}usgs/node:16
   needs:
     - Init
@@ -216,6 +219,7 @@ End to End Tests:
   image: cypress/base:16.14.0
   needs:
     - Init
+    - Build Project
   parallel:
     matrix:
       - CMD: cy:run:dashboard
-- 
GitLab


From 19b76c512c7da08c729515754d1b142d76d9ff0d Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 11:51:56 -0600
Subject: [PATCH 46/49] Cache all environment

---
 angular.json | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/angular.json b/angular.json
index fb51b308a..86e831e8b 100644
--- a/angular.json
+++ b/angular.json
@@ -405,6 +405,9 @@
   "defaultProject": "nshmp-apps",
   "cli": {
     "analytics": false,
-    "defaultCollection": "@ngrx/schematics"
+    "defaultCollection": "@ngrx/schematics",
+    "cache": {
+      "environment": "all"
+    }
   }
 }
-- 
GitLab


From 9d4595caee76970efa0a54cd7ac491e67caa825e Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 11:52:20 -0600
Subject: [PATCH 47/49] cache dist

---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ab28408f2..828c72a42 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -170,6 +170,7 @@ Build Project:
   artifacts:
     paths:
       - .angular
+      - dist
   image: ${DEVOPS_REGISTRY}usgs/node:16
   needs:
     - Init
-- 
GitLab


From 0c52c6b8c9abecd5b2bd9e88a849c6733f509814 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 12:17:24 -0600
Subject: [PATCH 48/49] fix spelling

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 1ff30721d..9a11baf02 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
     "cy:run:gmm": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-gmm.json",
     "cy:run:hazard": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-hazard.json",
     "cy:run:services": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-services.json",
-    "cy:run:surce-models": "npm run -- cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
+    "cy:run:source-models": "npm run -- cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
     "lint": "ng lint",
-- 
GitLab


From bacb864b1f46799279d09b55b2e45433e4643ea9 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Mon, 28 Mar 2022 12:19:40 -0600
Subject: [PATCH 49/49] fix script

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 9a11baf02..3a48b8be2 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
     "cy:run:gmm": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-gmm.json",
     "cy:run:hazard": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-hazard.json",
     "cy:run:services": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-services.json",
-    "cy:run:source-models": "npm run -- cy:run --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
+    "cy:run:source-models": "npm run cy:run -- --cypress-config=projects/nshmp-apps/cypress/config/cypress-source-models.json",
     "cy:verify": "cypress verify",
     "e2e": "ng e2e",
     "lint": "ng lint",
-- 
GitLab