]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Update cypress dashboard e2e tests
authorAfreen Misbah <afreen@ibm.com>
Mon, 27 Apr 2026 19:50:41 +0000 (01:20 +0530)
committerAfreen Misbah <afreen@ibm.com>
Tue, 12 May 2026 09:09:48 +0000 (14:39 +0530)
- removed dashboard v3 tests
-fixed login, navigation, mirroring, language, osd, page header e2e tests

Signed-off-by: Afreen Misbah <afreen@ibm.com>
27 files changed:
PendingReleaseNotes
src/pybind/mgr/dashboard/frontend/cypress.config.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/a11y/overview.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/orchestrator/04-osds.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.e2e-spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts [deleted file]
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/language.po.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/login.po.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/navigation.po.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/notification.po.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/overview.po.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/page-header.po.ts
src/pybind/mgr/dashboard/frontend/cypress/support/commands.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/health-card/overview-health-card.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/overview/overview.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/about/about.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/storage-overview.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/storage-overview.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/empty-state/empty-state.component.scss
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/local-storage-enum.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.ts

index f7f8d7b136f5a47819e03e8249afcc6acec50f6f..a5aa832074d4853b360900909337281facef3ea6 100644 (file)
@@ -21,7 +21,7 @@
     it must be explicitly loaded in the configuration file or code (see https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md).
 * RGW: Fixed bucket notification events so the 'x_amz_request_id' in NotificationEvent now matches the 'x_amz_request_id' returned by the corresponding S3 operation.
 
-
+* DASHBOARD: Introduces a new landing page - "Overview". This revamps UX and adds more information in the landing page - overall cluster health, health checks, resilency panel (showing active/clean Pgs status), total and used raw capacity, alerts, capacity breakdown by object, file and block and performance charts - throughput, latency and IOPS. This renames teh landing page from "Dashboard" to "Overview"
 * DASHBOARD: Removed the older landing page which was deprecated in Quincy.
   Admins can no longer enable the older, deprecated landing page layout by
   adjusting FEATURE_TOGGLE_DASHBOARD.
index 3f8f743efaf3c37264b0e2a9f029acb05de0f802..63b236078c3c32608b66a61e1b204c80055b906d 100644 (file)
@@ -23,7 +23,7 @@ export default defineConfig({
   env: {
     LOGIN_USER: 'admin',
     LOGIN_PWD: 'admin',
-    CEPH2_URL: 'https://localhost:11002/'
+    CEPH2_URL: 'https://localhost:4202/'
   },
 
   chromeWebSecurity: false,
@@ -55,7 +55,7 @@ export default defineConfig({
       )
       return require('./cypress/plugins/index.js')(on, config);
     },
-    baseUrl: 'https://localhost:11000/',
+    baseUrl: 'https://localhost:4200/',
     excludeSpecPattern: ['*.po.ts', '**/orchestrator/**'],
     experimentalSessionAndOrigin: true,
     specPattern: 'cypress/e2e/**/*-spec.{js,jsx,ts,tsx,feature}'
index 720718f0d6c635654f4b2a04f2790b94103fc483..04732f07412c61294b4abf6097ffeb801772f4b2 100644 (file)
@@ -1,7 +1,7 @@
-import { OvevriewPagehelper } from '../ui/dashboard-v3.po';
+import { OverviewPagehelper } from '../ui/overview.po';
 
 describe('Overview Page', { retries: 0 }, () => {
-  const overview = new OvevriewPagehelper();
+  const overview = new OverviewPagehelper();
 
   beforeEach(() => {
     cy.intercept('GET', '**/api/prometheus/data*', {
index 5302e4e3ce9e68fa091ae3ae5fe40efeabf85fc1..306c3e078900f58fbee999a3bd0cdcb2fd446b74 100644 (file)
@@ -61,8 +61,19 @@ describe('Mirroring page', () => {
           cy.get('[type=submit]').click();
 
           cy.get('[data-testid="pool-name"]').clear().type(name);
-          cy.get('[data-testid="pool-type-select"]').select('replicated');
-          cy.get('[data-testid="pool-type-select"] option:checked').contains('replicated');
+
+          cy.get(
+            '[data-testid="pool-type-select"] cds-radio input[type="radio"][value="replicated"]'
+          ).check({ force: true });
+
+          cy.get('cds-combo-box[id="applications"] input.cds--text-input').click({ force: true });
+          cy.get('.cds--list-box__menu.cds--multi-select').should('be.visible');
+          cy.get('.cds--list-box__menu.cds--multi-select .cds--checkbox-label')
+            .contains('.cds--checkbox-label-text', 'rbd', { matchCase: false })
+            .parent()
+            .click({ force: true });
+          cy.get('body').type('{esc}');
+
           cy.get('cd-submit-button').click();
           // Wait for form submission navigation to complete
           cy.url().should('include', '/pool');
index e526fd6157437011c47e9c354b0b7b59a4f73f57..183cbaf441cea66bccb6cf7c490081e1b49a58ea 100644 (file)
@@ -1,9 +1,9 @@
 import { OSDsPageHelper } from '../cluster/osds.po';
-import { OvevriewPagehelper } from '../ui/dashboard-v3.po';
+import { OverviewPagehelper } from '../ui/overview.po';
 
 describe('OSDs page', () => {
   const osds = new OSDsPageHelper();
-  const overview = new OvevriewPagehelper();
+  const overview = new OverviewPagehelper();
 
   before(() => {
     cy.login();
@@ -29,7 +29,11 @@ describe('OSDs page', () => {
 
           // landing page is easier to check OSD status
           overview.navigateTo();
-          overview.cardRow('OSD').should('contain.text', `${expectedCount} OSDs`);
+          overview.clickSystemsTab();
+          cy.get(`[data-test-id="OSD-value"]`).should(
+            'contain.text',
+            `${expectedCount}/${expectedCount} in/up`
+          );
 
           cy.wait(30000);
           expect(Number(newCount)).to.be.gte(2);
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.e2e-spec.ts
deleted file mode 100644 (file)
index 0a4666d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-import { OvevriewPagehelper } from './dashboard-v3.po';
-
-describe('Dashboard-v3 Main Page', () => {
-  const overview = new OvevriewPagehelper();
-
-  before(() => {
-    cy.login();
-  });
-
-  beforeEach(() => {
-    cy.login();
-    overview.navigateTo();
-  });
-
-  describe('Check that all hyperlinks on inventory card lead to the correct page and fields exist', () => {
-    it('should ensure that all linked pages in the inventory card lead to correct page', () => {
-      const expectationMap = {
-        Host: 'Hosts',
-        Monitor: 'Monitors',
-        OSDs: 'OSDs',
-        Pool: 'Pools',
-        'Object Gateway': 'Gateways'
-      };
-
-      for (const [linkText, breadcrumbText] of Object.entries(expectationMap)) {
-        cy.location('hash').should('eq', '#/overview');
-        overview.clickInventoryCardLink(linkText);
-        overview.expectBreadcrumbText(breadcrumbText);
-        overview.navigateBack();
-      }
-    });
-
-    it('should verify that cards exist on overview in proper order', () => {
-      // Ensures that cards are all displayed on the overview tab while being in the proper
-      // order, checks for card title and position via indexing into a list of all cards.
-      const order = ['Details', 'Inventory', 'Status', 'Capacity', 'Cluster Utilization'];
-
-      for (let i = 0; i < order.length; i++) {
-        overview.card(i).should('contain.text', order[i]);
-      }
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/dashboard-v3.po.ts
deleted file mode 100644 (file)
index 059938b..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import { PageHelper } from '../page-helper.po';
-
-export class OvevriewPagehelper extends PageHelper {
-  pages = { index: { url: '#/overview', id: 'cd-overview' } };
-
-  cardTitle(index: number) {
-    return cy.get('.card-title').its(index).text();
-  }
-
-  clickInventoryCardLink(link: string) {
-    console.log(link);
-    cy.get(`cd-card[cardTitle="Inventory"]`).contains('a', link).click();
-  }
-
-  card(indexOrTitle: number) {
-    cy.get('cd-card').as('cards');
-
-    return cy.get('@cards').its(indexOrTitle);
-  }
-
-  cardRow(rowName: string) {
-    return cy.get(`[data-testid=${rowName}]`);
-  }
-}
index 72987b699b71d2622e11659aec968035cefa16a5..2e92c8e4e7e44f7777a76b03f2792b077d431f96 100644 (file)
@@ -2,7 +2,7 @@ import { PageHelper } from '../page-helper.po';
 
 export class LanguagePageHelper extends PageHelper {
   pages = {
-    index: { url: '#/overview', id: 'cd-dashboard' }
+    index: { url: '#/overview', id: 'cd-overview' }
   };
 
   getLanguageBtn() {
index 7746ac1a0c3182f818f00d5c582ad62f248d07fc..b9b4d3064516ba28fe60e3d258f714ed1cf4c6d7 100644 (file)
@@ -3,14 +3,14 @@ import { PageHelper } from '../page-helper.po';
 export class LoginPageHelper extends PageHelper {
   pages = {
     index: { url: '#/login', id: 'cd-login' },
-    overview: { url: '#/overview', id: 'cd-dashboard' }
+    overview: { url: '#/overview', id: 'cd-overview' }
   };
 
   doLogin() {
     cy.get('[name=username]').type('admin');
     cy.get('#password').type('admin');
     cy.get('[type=submit]').click();
-    cy.get('cd-dashboard').should('exist');
+    cy.get('cd-overview').should('exist');
   }
 
   doLogout() {
index 70ba694efa91ba47c302fc3928f3c8f7ff9bda9e..90da5fdca7c25a6f223f25280f2548f0a3698287 100644 (file)
@@ -88,7 +88,7 @@ export class NavigationPageHelper extends PageHelper {
     navs.forEach((nav: any) => {
       cy.get('cds-sidenav-item').each(($link) => {
         if ($link.text().trim() === nav.menu.trim()) {
-          cy.wrap($link).click();
+          cy.wrap($link).click({ force: true });
         }
       });
       if (nav.submenus) {
index 6775d682d7a25fe83a0baccd57d9da450bac407e..897e11153f57b79a591277180a6465e7a8f0179e 100644 (file)
@@ -2,7 +2,7 @@ import { PageHelper } from '../page-helper.po';
 
 export class NotificationSidebarPageHelper extends PageHelper {
   getNotificationIcon() {
-    return cy.get('cd-notifications a');
+    return cy.get(`[data-testid='header-notification-icon']`);
   }
 
   getPanel() {
@@ -32,7 +32,7 @@ export class NotificationSidebarPageHelper extends PageHelper {
   }
 
   open() {
-    this.getNotificationIcon().click();
+    this.getNotificationIcon().click({ force: true });
     this.getPanel().should('exist');
     this.getSidebar().should('exist');
   }
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/overview.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/ui/overview.po.ts
new file mode 100644 (file)
index 0000000..947b032
--- /dev/null
@@ -0,0 +1,28 @@
+import { PageHelper } from '../page-helper.po';
+
+export class OverviewPagehelper extends PageHelper {
+  pages = { index: { url: '#/overview', id: 'cd-overview' } };
+
+  cardTitle(index: number) {
+    return cy.get('.card-title').its(index).text();
+  }
+
+  clickInventoryCardLink(link: string) {
+    console.log(link);
+    cy.get(`cd-card[cardTitle="Inventory"]`).contains('a', link).click();
+  }
+
+  card(indexOrTitle: number) {
+    cy.get('cd-card').as('cards');
+
+    return cy.get('@cards').its(indexOrTitle);
+  }
+
+  clickSystemsTab() {
+    cy.get(`[data-test-id="systems-tab"]`).click();
+  }
+
+  cardRow(rowName: string) {
+    return cy.get(`[data-testid=${rowName}]`);
+  }
+}
index b04b98f1ec7820b9f0850e2d5adf973858ed414a..41f63bb437a7a2712f0e81c30260ece1627ff112 100644 (file)
@@ -1,7 +1,7 @@
 import { PageHelper } from '../page-helper.po';
 
 const pages = {
-  cephfsMirroring: { url: '#/cephfs/mirroring', id: 'cd-cephfs-mirroring-list' }
+  cephfsMirroring: { url: '#/cephfs/mirroring', id: 'cd-cephfs-mirroring-error' }
 };
 
 export class PageHeaderPageHelper extends PageHelper {
index 2f54487ab0a83b20097c0c47692802376cb6d3a0..1fe3476c72cb4619139c142fc30e8ae2670750aa 100644 (file)
@@ -23,7 +23,7 @@ import { LocalStorage } from '../../src/app/shared/enum/local-storage-enum';
 let auth: any;
 
 const fillAuth = () => {
-  window.localStorage.setItem(LocalStorage.DASHBOARD_USRENAME, auth.username);
+  window.localStorage.setItem(LocalStorage.DASHBOARD_USERNAME, auth.username);
   window.localStorage.setItem('dashboard_permissions', auth.permissions);
   window.localStorage.setItem('user_pwd_expiration_date', auth.pwdExpirationDate);
   window.localStorage.setItem('user_pwd_update_required', auth.pwdUpdateRequired);
index b7ea0637cccd10a35878cce6d554a21e72ef7ca3..a3ea1f11c9ffc2df1e8a7c66fd31fa3d8dc3c52e 100644 (file)
         class="cds-ml-2"
         [caret]="true"
         (click)="toggleSection('system')"
+        data-test-id="systems-tab"
         description=""
         i18n-description>
         <span
                [class.border-subtle-right]="!isLast">
             <span class="overview-health-card-icon-and-text">
               <cd-icon [type]="vm?.[item.key]?.severity"></cd-icon>
-              <span class="cds--type-body-compact-01">
+              <span
+                class="cds--type-body-compact-01"
+                [data-test-id]="item.label">
                 {{ item.label }}
               </span>
             </span>
-            <p class="cds--type-label-01 cds-mt-3 cds-mb-0 overview-health-card-secondary-text">
+            <p
+              class="cds--type-label-01 cds-mt-3 cds-mb-0 overview-health-card-secondary-text"
+              [data-test-id]="item.label+'-value'">
               {{ vm?.[item.key]?.value }}
             </p>
           </div>
index ce95fb1f5753f02cef17f58e5ee95a6cc3206d85..992f68855d759a79b3175fef46f3488b6dd80d8f 100644 (file)
@@ -1,4 +1,10 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import {
+  ComponentFixture,
+  discardPeriodicTasks,
+  fakeAsync,
+  TestBed,
+  tick
+} from '@angular/core/testing';
 import { of, Subject, throwError } from 'rxjs';
 
 import { OverviewComponent } from './overview.component';
@@ -47,12 +53,12 @@ describe('OverviewComponent', () => {
   };
 
   let mockPrometheusService: {
-    isPrometheusUsable: jest.Mock;
+    refreshPrometheusUsable: jest.Mock;
   };
 
   beforeEach(async () => {
     mockPrometheusService = {
-      isPrometheusUsable: jest.fn().mockReturnValue(of(true))
+      refreshPrometheusUsable: jest.fn().mockReturnValue(of(true))
     };
 
     mockHealthService = { getHealthSnapshot: jest.fn() };
@@ -247,7 +253,7 @@ describe('OverviewComponent', () => {
     mockRefreshIntervalService.intervalData$.complete();
   });
 
-  it('storageCardVm$ should emit storage view model with mapped fields', (done) => {
+  it('storageCardVm$ should emit storage view model with mapped fields', fakeAsync((done) => {
     const mockData: HealthSnapshotMap = {
       fsid: 'fsid-storage',
       health: { status: 'HEALTH_OK', checks: {} },
@@ -302,8 +308,10 @@ describe('OverviewComponent', () => {
       done();
     });
 
+    tick(0);
     mockRefreshIntervalService.intervalData$.next();
-  });
+    discardPeriodicTasks();
+  }));
 
   it('storageCardVm$ should emit safe defaults before storage side streams resolve', (done) => {
     const mockData: HealthSnapshotMap = {
@@ -327,7 +335,7 @@ describe('OverviewComponent', () => {
     } as any;
 
     mockHealthService.getHealthSnapshot.mockReturnValue(of(mockData));
-    mockOverviewStorageService.getStorageBreakdown.mockReturnValue(of(null));
+    mockOverviewStorageService.getStorageBreakdown.mockReturnValue(of({ result: [] }));
 
     const sub = component.storageCardVm$.subscribe((vm) => {
       expect(vm.totalCapacity).toBe(1000);
index 9b61a099522b22eb13980879a1cff101a61a351a..42a2f2a5b68344e7971e872b4610bbfb736e9fd5 100644 (file)
@@ -7,8 +7,16 @@ import {
   ViewEncapsulation
 } from '@angular/core';
 import { GridModule, LayoutModule, TilesModule } from 'carbon-components-angular';
-import { combineLatest, EMPTY, Observable } from 'rxjs';
-import { catchError, exhaustMap, map, shareReplay, startWith, switchMap } from 'rxjs/operators';
+import { combineLatest, EMPTY, Observable, timer } from 'rxjs';
+import {
+  catchError,
+  distinctUntilChanged,
+  exhaustMap,
+  map,
+  shareReplay,
+  startWith,
+  switchMap
+} from 'rxjs/operators';
 
 import { HealthService } from '~/app/shared/api/health.service';
 import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service';
@@ -34,6 +42,7 @@ import { PrometheusService } from '~/app/shared/api/prometheus.service';
 const SECONDS_PER_HOUR = 3600;
 const SECONDS_PER_DAY = 86400;
 const TREND_DAYS = 7;
+const PROMETHUES_CONFIG_POLL_INTERVAL = 60000;
 
 @Component({
   selector: 'cd-overview',
@@ -83,9 +92,11 @@ export class OverviewComponent {
   );
 
   /* EMPTY STATE DATA */
-  readonly isPrometheusUsable$ = this.prometheusService
-    .isPrometheusUsable()
-    .pipe(shareReplay({ bufferSize: 1, refCount: true }));
+  readonly isPromethuesConfigured$ = timer(0, PROMETHUES_CONFIG_POLL_INTERVAL).pipe(
+    switchMap(() => this.prometheusService.refreshPrometheusUsable()),
+    distinctUntilChanged(),
+    shareReplay({ bufferSize: 1, refCount: true })
+  );
 
   readonly hasNoOSDs$ = this.healthData$.pipe(
     map((data: HealthSnapshotMap) => (data?.osdmap?.num_osds ?? 0) === 0),
@@ -95,16 +106,16 @@ export class OverviewComponent {
   readonly storageEmptyState$ = this.hasNoOSDs$.pipe(startWith(false)).pipe(
     map((hasNoOSDs) => {
       if (hasNoOSDs) {
-        return $localize`You must have storage configured to access this capability.`;
+        return $localize`You can view capacity usage and related metrics here once you add storage.`;
       }
       return '';
     }),
     shareReplay({ bufferSize: 1, refCount: true })
   );
 
-  readonly prometheusEmptyState$ = this.isPrometheusUsable$.pipe(
-    map((isPrometheusUsable) =>
-      isPrometheusUsable
+  readonly prometheusEmptyState$ = this.isPromethuesConfigured$.pipe(
+    map((isPromethuesConfigured) =>
+      isPromethuesConfigured
         ? ''
         : $localize`You must have Prometheus configured to access this capability.`
     ),
@@ -120,34 +131,36 @@ export class OverviewComponent {
     shareReplay({ bufferSize: 1, refCount: true })
   );
 
-  readonly averageConsumption$ = this.isPrometheusUsable$.pipe(
-    switchMap((usable) =>
-      usable
+  readonly averageConsumption$ = this.isPromethuesConfigured$.pipe(
+    switchMap((isConfigured) =>
+      isConfigured
         ? this.refreshIntervalObs(() => this.overviewStorageService.getAverageConsumption())
         : EMPTY
     ),
     shareReplay({ bufferSize: 1, refCount: true })
   );
 
-  readonly timeUntilFull$ = this.isPrometheusUsable$.pipe(
-    switchMap((usable) =>
-      usable ? this.refreshIntervalObs(() => this.overviewStorageService.getTimeUntilFull()) : EMPTY
+  readonly timeUntilFull$ = this.isPromethuesConfigured$.pipe(
+    switchMap((isConfigured) =>
+      isConfigured
+        ? this.refreshIntervalObs(() => this.overviewStorageService.getTimeUntilFull())
+        : EMPTY
     ),
     shareReplay({ bufferSize: 1, refCount: true })
   );
 
-  readonly breakdownRawData$ = this.isPrometheusUsable$.pipe(
-    switchMap((usable) =>
-      usable
+  readonly breakdownRawData$ = this.isPromethuesConfigured$.pipe(
+    switchMap((isConfigured) =>
+      isConfigured
         ? this.refreshIntervalObs(() => this.overviewStorageService.getStorageBreakdown())
         : EMPTY
     ),
     shareReplay({ bufferSize: 1, refCount: true })
   );
 
-  readonly capacityThresholds$ = this.isPrometheusUsable$.pipe(
-    switchMap((usable) =>
-      usable
+  readonly capacityThresholds$ = this.isPromethuesConfigured$.pipe(
+    switchMap((isConfigured) =>
+      isConfigured
         ? this.refreshIntervalObs(() => this.overviewStorageService.getRawCapacityThresholds())
         : EMPTY
     ),
@@ -156,9 +169,9 @@ export class OverviewComponent {
 
   // getTrendData() is already a polling stream through getRangeQueriesData()
   // hence no refresh needed.
-  readonly trendData$ = this.isPrometheusUsable$.pipe(
-    switchMap((usable) =>
-      usable
+  readonly trendData$ = this.isPromethuesConfigured$.pipe(
+    switchMap((isConfigured) =>
+      isConfigured
         ? this.overviewStorageService.getTrendData(
             Math.floor(Date.now() / 1000) - TREND_DAYS * SECONDS_PER_DAY,
             Math.floor(Date.now() / 1000),
index 9e5017ca7bd1c333b90ec29a772ed48eca30f71e..0be836e56ff1608e9cf3c58b5377b18782a551fe 100644 (file)
@@ -55,7 +55,7 @@ export class AboutComponent extends BaseModal implements OnInit, OnDestroy {
   setVariables() {
     const NOT_AVAILABLE = $localize`Not available`;
     const project = {} as any;
-    project.user = localStorage.getItem(LocalStorage.DASHBOARD_USRENAME);
+    project.user = localStorage.getItem(LocalStorage.DASHBOARD_USERNAME);
     project.role = USER;
     if (this.userPermission.read) {
       this.userService.get(project.user).subscribe((data: any) => {
index 9074d0c129a23db76d7b07027291f0a32325f9c1..ba995dc16c313b90ceef0c8104156dcae9ea6acc 100644 (file)
@@ -35,6 +35,7 @@
         <cd-language-selector class="d-flex"></cd-language-selector>
       </cds-header-navigation>
       <div class="cds--btn cds--btn--icon-only cds--header__action"
+           data-testid="header-notification-icon"
            (click)="onNotificationSelected($event)">
         <cd-notifications></cd-notifications>
       </div>
index 5204064a8f2fe3511cfdb347286da885359350bb..2acb2f27bcbcb4734f0e1e3bea0eb0487e7bb6b6 100644 (file)
@@ -41,7 +41,7 @@ describe('AuthService', () => {
     expect(req.request.body).toEqual(fakeCredentials);
     req.flush(fakeResponse);
     tick();
-    expect(localStorage.getItem(LocalStorage.DASHBOARD_USRENAME)).toBe('foo');
+    expect(localStorage.getItem(LocalStorage.DASHBOARD_USERNAME)).toBe('foo');
   }));
 
   it('should logout and remove the user', () => {
@@ -52,7 +52,7 @@ describe('AuthService', () => {
     const req = httpTesting.expectOne('api/auth/logout');
     expect(req.request.method).toBe('POST');
     req.flush({ redirect_url: '#/login' });
-    expect(localStorage.getItem(LocalStorage.DASHBOARD_USRENAME)).toBe(null);
+    expect(localStorage.getItem(LocalStorage.DASHBOARD_USERNAME)).toBe(null);
     expect(router.navigate).toBeCalledTimes(1);
   });
 });
index 0b93d171696a269615cede5bf11def5552b0a539..ae5e5db06548f7586d47fb8c36845b78ca40fb15 100644 (file)
@@ -91,14 +91,17 @@ export class PrometheusService {
   }
 
   isPrometheusUsable(): Observable<boolean> {
-    return this.isPrometheusModuleEnabled().pipe(
-      switchMap((enabled) =>
-        enabled ? this.isSettingConfigured(this.settingsKey.prometheus) : of(false)
-      ),
+    return this.isSettingConfigured(this.settingsKey.prometheus).pipe(
+      map((isConfigured) => isConfigured),
       catchError(() => of(false))
     );
   }
 
+  refreshPrometheusUsable(): Observable<boolean> {
+    delete this.settings[this.settingsKey.prometheus];
+    return this.isPrometheusUsable();
+  }
+
   isAlertmanagerUsable(): Observable<boolean> {
     return this.isPrometheusModuleEnabled().pipe(
       switchMap((enabled) =>
index 3a256176f21666256b792db6a26bf9ffafb712d5..10a4bbd3271a2cee08d0868918f6b820906c9ac6 100644 (file)
@@ -275,17 +275,16 @@ describe('OverviewStorageService', () => {
   });
 
   describe('getStorageBreakdown', () => {
-    it('should call getPrometheusQueryData with storage breakdown query', () => {
+    it('should call getGaugeQueryData with storage breakdown query', () => {
       const promSpy = jest
-        .spyOn(service['prom'], 'getPrometheusQueryData')
+        .spyOn(service['prom'], 'getGaugeQueryData')
         .mockReturnValue(of({}) as any);
 
       service.getStorageBreakdown().subscribe();
 
-      expect(promSpy).toHaveBeenCalledWith({
-        params:
-          'sum by (application) (ceph_pool_bytes_used * on(pool_id) group_left(instance, name, application) ceph_pool_metadata{application=~"(.*Block.*)|(.*Filesystem.*)|(.*Object.*)|(..*)"})'
-      });
+      expect(promSpy).toHaveBeenCalledWith(
+        'sum by (application) (ceph_pool_bytes_used * on(pool_id) group_left(instance, name, application) ceph_pool_metadata{application=~"(.*Block.*)|(.*Filesystem.*)|(.*Object.*)|(..*)"})'
+      );
     });
   });
 
index a5afd945270873de1dff58deb75f536db43938c0..a8ed8e319bc98d9bff0b2b0ef4fb93647d462487 100644 (file)
@@ -121,7 +121,7 @@ export class OverviewStorageService {
   }
 
   getStorageBreakdown(): Observable<PromqlGuageMetric> {
-    return this.prom.getPrometheusQueryData({ params: this.RAW_USED_BY_STORAGE_TYPE_QUERY });
+    return this.prom.getGaugeQueryData(this.RAW_USED_BY_STORAGE_TYPE_QUERY);
   }
 
   getThresholdStatus(total, used, nearfull, full): CapacityThreshold {
index 28079273da46c0ee392962cc29d2b9516c9af5eb..0d8ceda8d69c0bf541cfde598140a1186d4939ec 100644 (file)
@@ -1,18 +1,21 @@
 @use '@carbon/colors';
 
-empty-state {
+.empty-state {
   display: flex;
   flex-direction: column;
   justify-content: flex-end;
   gap: var(--cds-spacing-05);
-  height: 350px;
+  margin-top: 283px;
+  padding: var(--cds-spacing-05) var(--cds-spacing-05) var(--cds-spacing-07) var(--cds-spacing-05);
+  width: 264px;
+  margin-left: var(--cds-spacing-05);
 
-  p {
-    font-size: 12px !important;
+  img {
+    width: 80px !important;
+    height: 80px !important;
   }
 
-  img {
-    width: 100px !important;
-    height: 100px !important;
+  span {
+    color: var(--cds-text-secondary);
   }
 }
index 611a8bcffdb66c6e00510759ffed4c3a4d14de33..b8c17b18de396791414d038de5286e07678d94d6 100644 (file)
@@ -1,3 +1,3 @@
 export enum LocalStorage {
-  DASHBOARD_USRENAME = 'dashboard_username'
+  DASHBOARD_USERNAME = 'dashboard_username'
 }
index 76e3290ed908032822bacb878cb8d88357586cd0..2b2965441309c403a1d5a1d86108aa816abd5691 100644 (file)
@@ -15,13 +15,13 @@ describe('AuthStorageService', () => {
 
   it('should store username', () => {
     service.set(username, '');
-    expect(localStorage.getItem(LocalStorage.DASHBOARD_USRENAME)).toBe(username);
+    expect(localStorage.getItem(LocalStorage.DASHBOARD_USERNAME)).toBe(username);
   });
 
   it('should remove username', () => {
     service.set(username, '');
     service.remove();
-    expect(localStorage.getItem(LocalStorage.DASHBOARD_USRENAME)).toBe(null);
+    expect(localStorage.getItem(LocalStorage.DASHBOARD_USERNAME)).toBe(null);
   });
 
   it('should be loggedIn', () => {
index 02c4feede3bc95aa38d53a35ee280a12ddffd5e1..d6dbedf110af61ea345ce8b034e9365e8e76d31c 100644 (file)
@@ -16,7 +16,7 @@ export class AuthStorageService {
     pwdExpirationDate: number = null,
     pwdUpdateRequired: boolean = false
   ) {
-    localStorage.setItem(LocalStorage.DASHBOARD_USRENAME, username);
+    localStorage.setItem(LocalStorage.DASHBOARD_USERNAME, username);
     localStorage.setItem('dashboard_permissions', JSON.stringify(new Permissions(permissions)));
     localStorage.setItem('user_pwd_expiration_date', String(pwdExpirationDate));
     localStorage.setItem('user_pwd_update_required', String(pwdUpdateRequired));
@@ -24,17 +24,17 @@ export class AuthStorageService {
   }
 
   remove() {
-    localStorage.removeItem(LocalStorage.DASHBOARD_USRENAME);
+    localStorage.removeItem(LocalStorage.DASHBOARD_USERNAME);
     localStorage.removeItem('user_pwd_expiration_data');
     localStorage.removeItem('user_pwd_update_required');
   }
 
   isLoggedIn() {
-    return localStorage.getItem(LocalStorage.DASHBOARD_USRENAME) !== null;
+    return localStorage.getItem(LocalStorage.DASHBOARD_USERNAME) !== null;
   }
 
   getUsername() {
-    return localStorage.getItem(LocalStorage.DASHBOARD_USRENAME);
+    return localStorage.getItem(LocalStorage.DASHBOARD_USERNAME);
   }
 
   getPermissions(): Permissions {