From bbeac96949b8de2d95315ceb26389fb98d6c42fb Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Tue, 28 Jul 2026 10:42:25 -0300 Subject: [PATCH 1/9] acm alerting and perses fixes --- web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts | 36 ++- web/cypress/e2e/coo/02.acm_perses.cy.ts | 37 +++ .../regression/01.reg_alerts_admin.cy.ts | 13 +- .../e2e/perses/00.coo_bvt_perses_admin.cy.ts | 5 +- .../e2e/perses/01.coo_list_perses_admin.cy.ts | 8 +- .../e2e/perses/02.coo_edit_perses_admin.cy.ts | 7 +- .../perses/03.coo_create_perses_admin.cy.ts | 3 +- .../perses/04.coo_import_perses_admin.cy.ts | 3 +- .../e2e/perses/99.coo_rbac_perses_user1.cy.ts | 1 + .../e2e/perses/99.coo_rbac_perses_user2.cy.ts | 1 + .../e2e/perses/99.coo_rbac_perses_user3.cy.ts | 1 + .../e2e/perses/99.coo_rbac_perses_user4.cy.ts | 1 + .../e2e/perses/99.coo_rbac_perses_user5.cy.ts | 1 + .../fixtures/coo/acm-alerrule-test.yaml | 1 + web/cypress/fixtures/coo/acm-install.sh | 5 + web/cypress/fixtures/coo/acm-uiplugin.yaml | 4 + web/cypress/fixtures/monitoring/constants.ts | 6 + web/cypress/support/commands/auth-commands.ts | 8 +- .../support/commands/operator-commands.ts | 5 - .../support/commands/utility-commands.ts | 13 +- .../support/monitoring/01.reg_alerts.cy.ts | 291 ++++++++++++------ .../monitoring/04.reg_alerts_namespace.cy.ts | 3 +- .../perses/00.coo_bvt_perses_admin.cy.ts | 24 +- .../perses/01.coo_list_perses_admin.cy.ts | 3 +- .../01.coo_list_perses_admin_namespace.cy.ts | 13 +- .../perses/02.coo_edit_perses_admin.cy.ts | 27 +- .../perses/02.coo_edit_perses_admin_1.cy.ts | 13 +- .../perses/03.coo_create_perses_admin.cy.ts | 9 +- .../perses/04.coo_import_perses_admin.cy.ts | 21 +- .../05.coo_create_import_perses_admin.cy.ts | 7 +- .../perses/99.coo_rbac_perses_user1.cy.ts | 7 +- .../perses/99.coo_rbac_perses_user2.cy.ts | 13 +- .../perses/99.coo_rbac_perses_user3.cy.ts | 5 +- .../perses/99.coo_rbac_perses_user4.cy.ts | 1 + .../perses/99.coo_rbac_perses_user5.cy.ts | 1 + .../perses/99.coo_rbac_perses_user6.cy.ts | 1 + web/cypress/views/alerting-rule-list-page.ts | 6 +- web/cypress/views/incidents-page.ts | 10 +- web/cypress/views/list-page.ts | 15 +- web/cypress/views/nav.ts | 7 +- .../views/perses-dashboards-edit-variables.ts | 3 +- .../perses-dashboards-list-dashboards.ts | 22 +- web/cypress/views/silence-alert-page.ts | 1 + web/cypress/views/silences-list-page.ts | 1 - web/src/shared/constants/data-test.ts | 1 - 45 files changed, 444 insertions(+), 220 deletions(-) create mode 100644 web/cypress/e2e/coo/02.acm_perses.cy.ts diff --git a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts index 6324b404a..530193ef8 100644 --- a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts +++ b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts @@ -4,6 +4,9 @@ import '../../support/commands/auth-commands'; import { commonPages } from '../../views/common'; import { nav } from '../../views/nav'; import { acmAlertingPage } from '../../views/acm-alerting-page'; +import { troubleshootingPanelPage } from 'cypress/views/troubleshooting-panel'; +import { incidentsPage } from 'cypress/views/incidents-page'; +import { runAllRegressionFleetManagementAlertsTests } from 'cypress/support/monitoring/01.reg_alerts.cy'; const MCP = { namespace: Cypress.env('COO_NAMESPACE'), @@ -25,25 +28,19 @@ describe('ACM Alerting UI', { tags: ['@coo', '@alerts'] }, () => { cy.beforeBlockACM(MCP, MP); }); - it('Navigate to Fleet Management > local-cluster > Observe > Alerting', () => { + it('Navigate to Fleet Management > Observe > Alerting', () => { // check monitoring-plugin UI is not been affected cy.switchPerspective('Core platform'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); commonPages.titleShouldHaveText('Alerting'); + nav.tabs.switchTab('Incidents'); nav.sidenav.clickNavLink(['Observe', 'Metrics']); commonPages.titleShouldHaveText('Metrics'); // switch to Fleet Management page - cy.switchPerspective('Fleet Management'); + cy.switchPerspective('Fleet management'); // close pop-up window cy.closeOnboardingModalIfPresent(); - // click “local-cluster” when visible - cy.log('Waiting for local-cluster link to appear...'); - cy.contains('local-cluster', { timeout: 120000 }) - .should('exist') - .should('be.visible') - .then(($el) => { - cy.wrap($el).click({ force: true }); - }); + commonPages.titleShouldHaveText('Clusters'); // click side menu -> Observe -> Alerting nav.sidenav.clickNavLink(['Observe', 'Alerting']); // confirm Alerting page loading completed @@ -57,4 +54,23 @@ describe('ACM Alerting UI', { tags: ['@coo', '@alerts'] }, () => { cy.log('Verified all expected alerts are visible on the Alerting page'); cy.log('ACM Alerting UI test completed successfully'); }); + + it('Visual validation for features that should not be displayed under Fleet management perspective', () => { + // check Incidents page is not displayed under Fleet management perspective + cy.log('Incidents page should not be displayed under Fleet management perspective'); + cy.switchPerspective('Fleet management'); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + commonPages.titleShouldHaveText('Alerting'); + incidentsPage.incidentsPageShouldNotBeVisible(); + + // check Signal correlation option is not displayed under Fleet management perspective + cy.log('Signal correlation option should not be displayed under Fleet management perspective'); + troubleshootingPanelPage.signalCorrelationShouldNotBeVisible(); + }); + + runAllRegressionFleetManagementAlertsTests({ + name: 'Fleet management', + alertName: 'Watchdog-spoke', + alertNamespace: `${MP.namespace}`, + }); }); diff --git a/web/cypress/e2e/coo/02.acm_perses.cy.ts b/web/cypress/e2e/coo/02.acm_perses.cy.ts new file mode 100644 index 000000000..ff2136dce --- /dev/null +++ b/web/cypress/e2e/coo/02.acm_perses.cy.ts @@ -0,0 +1,37 @@ +// 02.acm_perses.cy.ts +// E2E test for validating ACM Perses integration with Cluster Observability Operator (COO) +import { nav } from 'cypress/views/nav'; +import '../../support/commands/auth-commands'; +import { runBVTCOOPersesTests1 } from 'cypress/support/perses/00.coo_bvt_perses_admin.cy'; + +const MCP = { + namespace: Cypress.env('COO_NAMESPACE'), + packageName: 'cluster-observability-operator', + operatorName: 'Cluster Observability Operator', + config: { + kind: 'UIPlugin', + name: 'monitoring', + }, +}; +const MP = { + namespace: 'openshift-monitoring', + operatorName: 'Cluster Monitoring Operator', +}; + +describe('ACM - Perses', { tags: ['@coo', '@perses'] }, () => { + before(() => { + cy.beforeBlockACM(MCP, MP); + cy.switchPerspective('Core platform'); + cy.cleanupPersesTestDashboardsBeforeTests(); + }); + + beforeEach(() => { + cy.switchPerspective('Fleet management'); + nav.sidenav.clickNavLink(['Observe', 'Dashboards']); + }); + + runBVTCOOPersesTests1({ + name: 'Fleet management', + dashboardsPageName: 'Dashboards', + }); +}); diff --git a/web/cypress/e2e/monitoring/regression/01.reg_alerts_admin.cy.ts b/web/cypress/e2e/monitoring/regression/01.reg_alerts_admin.cy.ts index 091775529..832eba1dd 100644 --- a/web/cypress/e2e/monitoring/regression/01.reg_alerts_admin.cy.ts +++ b/web/cypress/e2e/monitoring/regression/01.reg_alerts_admin.cy.ts @@ -1,5 +1,5 @@ import { alerts } from '../../../fixtures/monitoring/alert'; -import { runAllRegressionAlertsTests } from '../../../support/monitoring/01.reg_alerts.cy'; +import { runAllRegressionCorePlatformAlertsTests } from '../../../support/monitoring/01.reg_alerts.cy'; import { commonPages } from '../../../views/common'; import { nav } from '../../../views/nav'; @@ -8,13 +8,14 @@ const MP = { operatorName: 'Cluster Monitoring Operator', }; -// Test suite for Administrator perspective +// Test suite for Core platform perspective describe( - 'Regression: Monitoring - Alerts (Administrator)', + 'Regression: Monitoring - Alerts (Core platform)', { tags: ['@monitoring', '@alerts'] }, () => { before(() => { cy.beforeBlock(MP); + cy.switchPerspective('Core platform'); }); beforeEach(() => { @@ -27,9 +28,9 @@ describe( alerts.getWatchdogAlert(); }); - // Run tests in Administrator perspective - runAllRegressionAlertsTests({ - name: 'Administrator', + // Run tests in Core platform perspective + runAllRegressionCorePlatformAlertsTests({ + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/00.coo_bvt_perses_admin.cy.ts b/web/cypress/e2e/perses/00.coo_bvt_perses_admin.cy.ts index 0968e050c..78c6a05f0 100644 --- a/web/cypress/e2e/perses/00.coo_bvt_perses_admin.cy.ts +++ b/web/cypress/e2e/perses/00.coo_bvt_perses_admin.cy.ts @@ -20,11 +20,12 @@ const MP = { //TODO: change tag to @smoke, @dashboards, @perses when customizable-dashboards gets merged describe( - 'BVT: COO - Dashboards (Perses) - Administrator perspective', + 'BVT: COO - Dashboards (Perses) - Core platform perspective', { tags: ['@smoke', '@dashboards', '@perses'] }, () => { before(() => { cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); }); @@ -34,7 +35,7 @@ describe( //TODO: rename after customizable-dashboards gets merged runBVTCOOPersesTests1({ - name: 'Administrator', + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/01.coo_list_perses_admin.cy.ts b/web/cypress/e2e/perses/01.coo_list_perses_admin.cy.ts index b74ea4d62..66c49ac27 100644 --- a/web/cypress/e2e/perses/01.coo_list_perses_admin.cy.ts +++ b/web/cypress/e2e/perses/01.coo_list_perses_admin.cy.ts @@ -28,6 +28,7 @@ describe( () => { before(() => { cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); }); @@ -38,11 +39,11 @@ describe( }); runCOOListPersesTests({ - name: 'Administrator', + name: 'Core platform', }); runCOOListPersesDuplicateDashboardTests({ - name: 'Administrator', + name: 'Core platform', }); }, ); @@ -54,6 +55,7 @@ describe( () => { before(() => { cy.beforeBlockCOO(MCP, MP); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); }); @@ -64,7 +66,7 @@ describe( }); runCOOListPersesTestsNamespace({ - name: 'Administrator', + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/02.coo_edit_perses_admin.cy.ts b/web/cypress/e2e/perses/02.coo_edit_perses_admin.cy.ts index e553d3116..dda15c4a1 100644 --- a/web/cypress/e2e/perses/02.coo_edit_perses_admin.cy.ts +++ b/web/cypress/e2e/perses/02.coo_edit_perses_admin.cy.ts @@ -25,8 +25,9 @@ describe( { tags: ['@perses', '@dashboards'] }, () => { before(() => { - operatorAuthUtils.loginAndAuth(); // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); + operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); }); @@ -37,11 +38,11 @@ describe( }); runCOOEditPersesTests({ - name: 'Administrator', + name: 'Core platform', }); runCOOEditPersesTests1({ - name: 'Administrator', + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/03.coo_create_perses_admin.cy.ts b/web/cypress/e2e/perses/03.coo_create_perses_admin.cy.ts index 3ca5bf5e3..d4f10a03e 100644 --- a/web/cypress/e2e/perses/03.coo_create_perses_admin.cy.ts +++ b/web/cypress/e2e/perses/03.coo_create_perses_admin.cy.ts @@ -26,6 +26,7 @@ describe( before(() => { // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); }); @@ -41,7 +42,7 @@ describe( }); runCOOCreatePersesTests({ - name: 'Administrator', + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/04.coo_import_perses_admin.cy.ts b/web/cypress/e2e/perses/04.coo_import_perses_admin.cy.ts index bf80c0da8..a571e5b16 100644 --- a/web/cypress/e2e/perses/04.coo_import_perses_admin.cy.ts +++ b/web/cypress/e2e/perses/04.coo_import_perses_admin.cy.ts @@ -26,6 +26,7 @@ describe( before(() => { // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); }); @@ -43,7 +44,7 @@ describe( }); runCOOImportPersesTests({ - name: 'Administrator', + name: 'Core platform', }); }, ); diff --git a/web/cypress/e2e/perses/99.coo_rbac_perses_user1.cy.ts b/web/cypress/e2e/perses/99.coo_rbac_perses_user1.cy.ts index 8b6fcf518..8e3f7da79 100644 --- a/web/cypress/e2e/perses/99.coo_rbac_perses_user1.cy.ts +++ b/web/cypress/e2e/perses/99.coo_rbac_perses_user1.cy.ts @@ -24,6 +24,7 @@ describe( before(() => { // Setup COO and Perses dashboards (requires admin privileges) cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); diff --git a/web/cypress/e2e/perses/99.coo_rbac_perses_user2.cy.ts b/web/cypress/e2e/perses/99.coo_rbac_perses_user2.cy.ts index 224a0d7e7..1159e59df 100644 --- a/web/cypress/e2e/perses/99.coo_rbac_perses_user2.cy.ts +++ b/web/cypress/e2e/perses/99.coo_rbac_perses_user2.cy.ts @@ -34,6 +34,7 @@ describe( // Step 2: Setup COO and Perses dashboards (requires admin privileges) // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); diff --git a/web/cypress/e2e/perses/99.coo_rbac_perses_user3.cy.ts b/web/cypress/e2e/perses/99.coo_rbac_perses_user3.cy.ts index 00f80cf59..51e0ec226 100644 --- a/web/cypress/e2e/perses/99.coo_rbac_perses_user3.cy.ts +++ b/web/cypress/e2e/perses/99.coo_rbac_perses_user3.cy.ts @@ -34,6 +34,7 @@ describe( // Step 2: Setup COO and Perses dashboards (requires admin privileges) // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); diff --git a/web/cypress/e2e/perses/99.coo_rbac_perses_user4.cy.ts b/web/cypress/e2e/perses/99.coo_rbac_perses_user4.cy.ts index c9286b70f..8ceb2c2b6 100644 --- a/web/cypress/e2e/perses/99.coo_rbac_perses_user4.cy.ts +++ b/web/cypress/e2e/perses/99.coo_rbac_perses_user4.cy.ts @@ -34,6 +34,7 @@ describe( // Step 2: Setup COO and Perses dashboards (requires admin privileges) // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); diff --git a/web/cypress/e2e/perses/99.coo_rbac_perses_user5.cy.ts b/web/cypress/e2e/perses/99.coo_rbac_perses_user5.cy.ts index 22be8c5e3..261fb7ab0 100644 --- a/web/cypress/e2e/perses/99.coo_rbac_perses_user5.cy.ts +++ b/web/cypress/e2e/perses/99.coo_rbac_perses_user5.cy.ts @@ -34,6 +34,7 @@ describe( // Step 2: Setup COO and Perses dashboards (requires admin privileges) // cy.beforeBlockCOO(MCP, MP, { dashboards: true, troubleshootingPanel: false }); operatorAuthUtils.loginAndAuth(); + cy.switchPerspective('Core platform'); cy.cleanupPersesTestDashboardsBeforeTests(); cy.setupPersesRBACandExtraDashboards(); diff --git a/web/cypress/fixtures/coo/acm-alerrule-test.yaml b/web/cypress/fixtures/coo/acm-alerrule-test.yaml index 5f2b564a1..68b69775d 100644 --- a/web/cypress/fixtures/coo/acm-alerrule-test.yaml +++ b/web/cypress/fixtures/coo/acm-alerrule-test.yaml @@ -28,6 +28,7 @@ data: cluster: "spoke" clusterID: "22222222" severity: warn + namespace: openshift-monitoring - name: cluster-health rules: - alert: ClusterCPUHealth-jb diff --git a/web/cypress/fixtures/coo/acm-install.sh b/web/cypress/fixtures/coo/acm-install.sh index 893e9ed0d..55d1e1b8e 100755 --- a/web/cypress/fixtures/coo/acm-install.sh +++ b/web/cypress/fixtures/coo/acm-install.sh @@ -193,6 +193,10 @@ metadata: name: monitoring spec: monitoring: + incidents: + enabled: true + perses: + enabled: true acm: enabled: true alertmanager: @@ -233,6 +237,7 @@ data: cluster: "spoke" clusterID: "22222222" severity: warn + namespace: openshift-monitoring - name: cluster-health rules: - alert: ClusterCPUHealth-jb diff --git a/web/cypress/fixtures/coo/acm-uiplugin.yaml b/web/cypress/fixtures/coo/acm-uiplugin.yaml index cda8194f8..37dac00a1 100644 --- a/web/cypress/fixtures/coo/acm-uiplugin.yaml +++ b/web/cypress/fixtures/coo/acm-uiplugin.yaml @@ -4,6 +4,10 @@ metadata: name: monitoring spec: monitoring: + incidents: + enabled: true + perses: + enabled: true acm: enabled: true alertmanager: diff --git a/web/cypress/fixtures/monitoring/constants.ts b/web/cypress/fixtures/monitoring/constants.ts index 1d37415ec..816a0b3a0 100644 --- a/web/cypress/fixtures/monitoring/constants.ts +++ b/web/cypress/fixtures/monitoring/constants.ts @@ -28,6 +28,12 @@ export enum Severity { NONE = 'None', } +export enum Cluster { + LOCAL = 'local', + LOCAL_CLUSTER = 'local-cluster', + SPOKE = 'spoke', +} + export enum SilenceState { ACTIVE = 'Active', PENDING = 'Pending', diff --git a/web/cypress/support/commands/auth-commands.ts b/web/cypress/support/commands/auth-commands.ts index 7a5ae88ad..c05930213 100644 --- a/web/cypress/support/commands/auth-commands.ts +++ b/web/cypress/support/commands/auth-commands.ts @@ -277,9 +277,15 @@ Cypress.Commands.add( ); Cypress.Commands.add('switchPerspective', (...perspectives: string[]) => { + cy.log('switchPerspective - ' + perspectives.join(', ')); /* If side bar is collapsed then expand it before switching perspecting */ - cy.wait(2000); + cy.waitUntil( + () => { + return cy.byTestID('username', { timeout: 120000 }).should('be.visible'); + }, + { timeout: 120000 }, + ); cy.get('body').then((body) => { if (body.find('.pf-m-collapsed').length > 0) { cy.get('#nav-toggle').click(); diff --git a/web/cypress/support/commands/operator-commands.ts b/web/cypress/support/commands/operator-commands.ts index ca70f5479..e05168320 100644 --- a/web/cypress/support/commands/operator-commands.ts +++ b/web/cypress/support/commands/operator-commands.ts @@ -67,11 +67,6 @@ function collectDebugInfo(MP: { namespace: string }, MCP?: { namespace: string } cy.log('DEBUG not set. Skipping operator debug information collection.'); return; } - // TODO: Remove once DataViewFilters incompatibility with COO 1.5.0 is resolved - if (Cypress.env('COO_1_5_0')) { - cy.log('COO_1_5_0 set: skipping collectDebugInfo (DataViewFilters incompatibility)'); - return; - } cy.aboutModal(); cy.podImage('monitoring-plugin', MP.namespace); if (MCP && MCP.namespace) { diff --git a/web/cypress/support/commands/utility-commands.ts b/web/cypress/support/commands/utility-commands.ts index e1c5cfe4a..184996e69 100644 --- a/web/cypress/support/commands/utility-commands.ts +++ b/web/cypress/support/commands/utility-commands.ts @@ -171,13 +171,16 @@ Cypress.Commands.add('podImage', (pod: string, namespace: string) => { cy.switchPerspective('Core platform', 'Administrator'); cy.clickNavLink(['Workloads', 'Pods']); cy.byTestID('page-heading').contains('Pods').should('be.visible'); - // Wait for the pod table to load to not compromise the namespace change - cy.get('table tbody tr, [data-ouia-component-id="DataViewTable"] tbody tr', { - timeout: 30000, - }).should('have.length.greaterThan', 0); + cy.get( + 'table tbody tr, [data-ouia-component-id="DataViewTableBasic"], [data-test="empty-box-body"]', + { timeout: 30000 }, + ).should('have.length.greaterThan', 0); cy.changeNamespace(namespace); // Wait for the pod table to load after namespace change so the page stabilizes - cy.get('table tbody tr, [data-ouia-component-id="DataViewTable"] tbody tr', { + cy.get('table tbody tr, [data-ouia-component-id="DataViewTableBasic"] tbody tr', { + timeout: 30000, + }).should('have.length.greaterThan', 0); + cy.get('[data-ouia-component-id="DataViewFilters"]', { timeout: 30000, }).should('have.length.greaterThan', 0); // Re-check for DataViewFilters after the table has stabilized diff --git a/web/cypress/support/monitoring/01.reg_alerts.cy.ts b/web/cypress/support/monitoring/01.reg_alerts.cy.ts index b4eccbaae..607590e07 100644 --- a/web/cypress/support/monitoring/01.reg_alerts.cy.ts +++ b/web/cypress/support/monitoring/01.reg_alerts.cy.ts @@ -9,6 +9,7 @@ import { alertingRuleListPage } from '../../views/alerting-rule-list-page'; import { alertingRuleDetailsPage } from '../../views/alerting-rule-details-page'; import { AlertingRulesAlertState, + Cluster, MainTagState, Severity, SilenceComment, @@ -21,13 +22,99 @@ import { FilterOUIAIDs } from '@/shared/constants/data-test'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + alertName?: string; + alertNamespace?: string; } export function runAllRegressionAlertsTests(perspective: PerspectiveConfig) { testAlertsRegression(perspective); } -export function testAlertsRegression(perspective: PerspectiveConfig) { +export function runAllRegressionCorePlatformAlertsTests(perspective: PerspectiveConfig) { + testAlertsCorePlatformHeaderRegression(perspective); + testAlertsRegression(perspective); +} + +export function runAllRegressionFleetManagementAlertsTests(perspective: PerspectiveConfig) { + testAlertsFleetManagementRegression(perspective); + testAlertsRegression(perspective); +} + +export function testAlertsFleetManagementRegression(perspective: PerspectiveConfig) { + const alertName = perspective.alertName ?? WatchdogAlert.ALERTNAME; + + it(`${perspective.name} perspective - Alerting > Alerts page - Filtering`, () => { + cy.log('1.1 Header components'); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.PENDING); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.SILENCED); + listPage.filter.selectFilterOption('Severity', Severity.CRITICAL); + listPage.filter.selectFilterOption('Severity', Severity.WARNING); + listPage.filter.selectFilterOption('Severity', Severity.INFO); + listPage.filter.selectFilterOption('Severity', Severity.NONE); + listPage.filter.selectFilterOption('Cluster', Cluster.LOCAL); + listPage.filter.selectFilterOption('Cluster', Cluster.LOCAL_CLUSTER); + listPage.filter.selectFilterOption('Cluster', Cluster.SPOKE); + listPage.filter.removeIndividualTag(Cluster.LOCAL); + listPage.filter.removeIndividualTag(Cluster.LOCAL_CLUSTER); + listPage.filter.removeIndividualTag(Cluster.SPOKE); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.FIRING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.PENDING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.SILENCED); + listPage.filter.clearAllFilters(); + + listPage.exportAsCSV(true, /openshift.csv/); + + listPage.filter.byLabel('alertname=' + alertName); + listPage.filter.removeIndividualTag('alertname=' + alertName); + listPage.filter.byLabel('alertname=' + alertName); + listPage.filter.removeIndividualTag('alertname=' + alertName); + }); + + it(`${perspective.name} perspective - Alerting > Alerting Rules page - Filtering`, () => { + cy.log('2.1 use sidebar nav to go to Observe > Alerting'); + nav.tabs.switchTab('Alerting rules'); + alertingRuleListPage.shouldBeLoaded(); + + cy.log('2.2 clear all filters, verify filters and tags'); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.FIRING); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.PENDING); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.SILENCED); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.NOT_FIRING); + listPage.filter.selectFilterOption('Severity', Severity.CRITICAL); + listPage.filter.selectFilterOption('Severity', Severity.WARNING); + listPage.filter.selectFilterOption('Severity', Severity.INFO); + listPage.filter.selectFilterOption('Severity', Severity.NONE); + listPage.filter.selectFilterOption('Source', Source.PLATFORM); + listPage.filter.selectFilterOption('Source', Source.USER); + + listPage.filter.clickOn1more(MainTagState.ALERT_STATE); + listPage.filter.clickOn1more(MainTagState.SEVERITY); + + listPage.filter.clickOnShowLess(MainTagState.ALERT_STATE); + listPage.filter.clickOnShowLess(MainTagState.SEVERITY); + + listPage.filter.clickOn1more(MainTagState.ALERT_STATE); + listPage.filter.clickOn1more(MainTagState.SEVERITY); + + listPage.filter.removeIndividualTag(AlertingRulesAlertState.FIRING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.PENDING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.SILENCED); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.NOT_FIRING); + + listPage.filter.removeIndividualTag(Severity.CRITICAL); + listPage.filter.removeIndividualTag(Severity.WARNING); + listPage.filter.removeIndividualTag(Severity.INFO); + listPage.filter.removeIndividualTag(Severity.NONE); + listPage.filter.removeIndividualTag(Source.PLATFORM); + listPage.filter.removeIndividualTag(Source.USER); + + alertingRuleListPage.filter.assertNoClearAllFilters(); + }); +} + +export function testAlertsCorePlatformHeaderRegression(perspective: PerspectiveConfig) { + const alertName = perspective.alertName ?? WatchdogAlert.ALERTNAME; + it(`${perspective.name} perspective - Alerting > Alerts page - Filtering`, () => { cy.log('1.1 Header components'); listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.PENDING); @@ -46,14 +133,61 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { listPage.exportAsCSV(true, /openshift.csv/); - listPage.filter.byLabel('alertname=' + `${WatchdogAlert.ALERTNAME}`); - listPage.filter.removeIndividualTag('alertname=' + `${WatchdogAlert.ALERTNAME}`); - listPage.filter.byLabel('alertname=' + `${WatchdogAlert.ALERTNAME}`); - listPage.filter.removeIndividualTag('alertname=' + `${WatchdogAlert.ALERTNAME}`); + listPage.filter.byLabel('alertname=' + alertName); + listPage.filter.removeIndividualTag('alertname=' + alertName); + listPage.filter.byLabel('alertname=' + alertName); + listPage.filter.removeIndividualTag('alertname=' + alertName); }); - it(`${perspective.name} perspective - Alerting > Silences page > Create silence`, () => { + it(`${perspective.name} perspective - Alerting > Alerting Rules page - Filtering`, () => { cy.log('2.1 use sidebar nav to go to Observe > Alerting'); + nav.tabs.switchTab('Alerting rules'); + alertingRuleListPage.shouldBeLoaded(); + listPage.filter.removeIndividualTag(Source.PLATFORM); + + cy.log('2.2 clear all filters, verify filters and tags'); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.FIRING); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.PENDING); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.SILENCED); + listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.NOT_FIRING); + listPage.filter.selectFilterOption('Severity', Severity.CRITICAL); + listPage.filter.selectFilterOption('Severity', Severity.WARNING); + listPage.filter.selectFilterOption('Severity', Severity.INFO); + listPage.filter.selectFilterOption('Severity', Severity.NONE); + listPage.filter.selectFilterOption('Source', Source.PLATFORM); + listPage.filter.selectFilterOption('Source', Source.USER); + + listPage.filter.clickOn1more(MainTagState.ALERT_STATE); + listPage.filter.clickOn1more(MainTagState.SEVERITY); + + listPage.filter.clickOnShowLess(MainTagState.ALERT_STATE); + listPage.filter.clickOnShowLess(MainTagState.SEVERITY); + + listPage.filter.clickOn1more(MainTagState.ALERT_STATE); + listPage.filter.clickOn1more(MainTagState.SEVERITY); + + listPage.filter.removeIndividualTag(AlertingRulesAlertState.FIRING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.PENDING); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.SILENCED); + listPage.filter.removeIndividualTag(AlertingRulesAlertState.NOT_FIRING); + + listPage.filter.removeIndividualTag(Severity.CRITICAL); + listPage.filter.removeIndividualTag(Severity.WARNING); + listPage.filter.removeIndividualTag(Severity.INFO); + listPage.filter.removeIndividualTag(Severity.NONE); + listPage.filter.removeIndividualTag(Source.PLATFORM); + listPage.filter.removeIndividualTag(Source.USER); + + alertingRuleListPage.filter.assertNoClearAllFilters(); + }); +} + +export function testAlertsRegression(perspective: PerspectiveConfig) { + const alertName = perspective.alertName ?? WatchdogAlert.ALERTNAME; + const alertNamespace = perspective.alertNamespace ?? WatchdogAlert.NAMESPACE; + + it(`${perspective.name} perspective - Alerting > Silences page > Create silence`, () => { + cy.log('3.1 use sidebar nav to go to Observe > Alerting'); nav.tabs.switchTab('Silences'); silencesListPage.createSilence(); silenceAlertPage.assertCommentNoError(); @@ -74,56 +208,56 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { it( `${perspective.name} perspective - ` + - 'Alerting > Alerts / Silences > Kebab icon on List and Details', + `Alerting > Alerts / Silences > Kebab icon on List and Details`, () => { - cy.log('3.1 use sidebar nav to go to Observe > Alerting'); - - cy.log('3.2 filter to Watchdog alert'); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + cy.log('4.1 use sidebar nav to go to Observe > Alerting'); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + cy.log('4.2 filter to Watchdog alert'); + listPage.filter.byName(`${alertName}`); listPage.ARRows.countShouldBe(1); - cy.log('3.3 silence alert'); + cy.log('4.3 silence alert'); listPage.ARRows.expandRow(); listPage.ARRows.silenceAlert(); silenceAlertPage.addComment(SilenceComment.SILENCE_COMMENT); silenceAlertPage.clickSubmit(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); - cy.log('3.4 Assert Kebab on Alert Details page'); + cy.log('4.4 Assert Kebab on Alert Details page'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); commonPages.titleShouldHaveText('Alerting'); listPage.filter.clearAllFilters(); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + listPage.filter.byName(`${alertName}`); listPage.ARRows.countShouldBe(1); listPage.ARRows.expandRow(); listPage.ARRows.assertNoKebab(); listPage.ARRows.clickAlert(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); detailsPage.assertSilencedAlert(); - cy.log('3.5 Assert Kebab on Silence List page for Silenced alert'); + cy.log('4.5 Assert Kebab on Silence List page for Silenced alert'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Silences'); silencesListPage.shouldBeLoaded(); listPage.filter.removeIndividualTag(SilenceState.ACTIVE); listPage.filter.removeIndividualTag(SilenceState.PENDING); - silencesListPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + silencesListPage.filter.byName(`${alertName}`); listPage.filter.selectFilterOption('Silence State', SilenceState.ACTIVE); silencesListPage.rows.assertSilencedAlertKebab(); - cy.log('3.6 Click on Silenced alert and Assert Actions button'); - silencesListPage.rows.clickSilencedAlert(`${WatchdogAlert.ALERTNAME}`); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + cy.log('4.6 Click on Silenced alert and Assert Actions button'); + silencesListPage.rows.clickSilencedAlert(`${alertName}`); + commonPages.titleShouldHaveText(`${alertName}`); silenceDetailsPage.assertActionsSilencedAlert(); - cy.log('3.7 Expire silence'); + cy.log('4.7 Expire silence'); silenceDetailsPage.expireSilence(false, true); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Silences'); - cy.log('3.8 Assert Kebab on Silence List page for Expired alert'); - silencesListPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + cy.log('4.8 Assert Kebab on Silence List page for Expired alert'); + silencesListPage.filter.byName(`${alertName}`); silencesListPage.emptyState(); listPage.filter.removeIndividualTag(SilenceState.ACTIVE); listPage.filter.removeIndividualTag(SilenceState.PENDING); @@ -132,12 +266,12 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { listPage.filter.selectFilterOption('Silence State', SilenceState.PENDING); silencesListPage.rows.assertExpiredAlertKebab('0'); - cy.log('3.9 Click on Expired alert and Assert Actions button'); - silencesListPage.rows.clickSilencedAlert(`${WatchdogAlert.ALERTNAME}`); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + cy.log('4.9 Click on Expired alert and Assert Actions button'); + silencesListPage.rows.clickSilencedAlert(`${alertName}`); + commonPages.titleShouldHaveText(`${alertName}`); silenceDetailsPage.assertActionsExpiredAlert(); - cy.log('3.10 Recreate silence'); + cy.log('4.10 Recreate silence'); silenceDetailsPage.recreateSilence(false); commonPages.titleShouldHaveText('Recreate silence'); silenceAlertPage.silenceAlertSectionDefault(); @@ -145,7 +279,7 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { silenceAlertPage.alertLabelsSectionDefault(); silenceAlertPage.assertLabelNameLabelValueRegExNegMatcher( 'alertname', - `${WatchdogAlert.ALERTNAME}`, + `${alertName}`, false, false, ); @@ -157,7 +291,7 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { ); silenceAlertPage.assertLabelNameLabelValueRegExNegMatcher( 'namespace', - `${WatchdogAlert.NAMESPACE}`, + `${alertNamespace}`, false, false, ); @@ -168,14 +302,14 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { false, ); silenceAlertPage.clickSubmit(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); - cy.log('3.11 Edit silence'); + cy.log('4.11 Edit silence'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Silences'); silencesListPage.shouldBeLoaded(); listPage.filter.removeIndividualTag(SilenceState.PENDING); - silencesListPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + silencesListPage.filter.byName(`${alertName}`); silencesListPage.rows.editSilence(); commonPages.titleShouldHaveText('Edit silence'); silenceAlertPage.silenceAlertSectionDefault(); @@ -184,7 +318,7 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { silenceAlertPage.alertLabelsSectionDefault(); silenceAlertPage.assertLabelNameLabelValueRegExNegMatcher( 'alertname', - `${WatchdogAlert.ALERTNAME}`, + `${alertName}`, false, false, ); @@ -196,7 +330,7 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { ); silenceAlertPage.assertLabelNameLabelValueRegExNegMatcher( 'namespace', - `${WatchdogAlert.NAMESPACE}`, + `${alertNamespace}`, false, false, ); @@ -207,111 +341,74 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { false, ); silenceAlertPage.clickSubmit(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); - cy.log('3.12 Expire silence'); + cy.log('4.12 Expire silence'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Silences'); silencesListPage.shouldBeLoaded(); listPage.filter.removeIndividualTag(SilenceState.ACTIVE); listPage.filter.removeIndividualTag(SilenceState.PENDING); - silencesListPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + silencesListPage.filter.byName(`${alertName}`); listPage.filter.selectFilterOption('Silence State', SilenceState.ACTIVE); silencesListPage.rows.expireSilence(true); - cy.log('3.13 Alert Details > Silence alert button > Cancel'); + cy.log('4.13 Alert Details > Silence alert button > Cancel'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + listPage.filter.byName(`${alertName}`); listPage.ARRows.countShouldBe(1); listPage.ARRows.expandRow(); listPage.ARRows.clickAlert(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); detailsPage.clickSilenceAlertButton(); silenceAlertPage.addComment(SilenceComment.SILENCE_COMMENT); silenceAlertPage.clickCancelButton(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); nav.sidenav.clickNavLink(['Observe', 'Alerting']); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + listPage.filter.byName(`${alertName}`); listPage.ARRows.countShouldBe(1); }, ); it(`${perspective.name} perspective - Alerting > Alerting Rules`, () => { - cy.log('4.1 use sidebar nav to go to Observe > Alerting'); + cy.log('5.1 Search by Name'); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Alerting rules'); - alertingRuleListPage.shouldBeLoaded(); - - cy.log('4.2 clear all filters, verify filters and tags'); - listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.FIRING); - listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.PENDING); - listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.SILENCED); - listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.NOT_FIRING); - listPage.filter.selectFilterOption('Severity', Severity.CRITICAL); - listPage.filter.selectFilterOption('Severity', Severity.WARNING); - listPage.filter.selectFilterOption('Severity', Severity.INFO); - listPage.filter.selectFilterOption('Severity', Severity.NONE); - listPage.filter.selectFilterOption('Source', Source.PLATFORM); - listPage.filter.selectFilterOption('Source', Source.USER); - - listPage.filter.clickOn1more(MainTagState.ALERT_STATE); - listPage.filter.clickOn1more(MainTagState.SEVERITY); - - listPage.filter.clickOnShowLess(MainTagState.ALERT_STATE); - listPage.filter.clickOnShowLess(MainTagState.SEVERITY); - - listPage.filter.clickOn1more(MainTagState.ALERT_STATE); - listPage.filter.clickOn1more(MainTagState.SEVERITY); - - listPage.filter.removeIndividualTag(AlertingRulesAlertState.FIRING); - listPage.filter.removeIndividualTag(AlertingRulesAlertState.PENDING); - listPage.filter.removeIndividualTag(AlertingRulesAlertState.SILENCED); - listPage.filter.removeIndividualTag(AlertingRulesAlertState.NOT_FIRING); - - listPage.filter.removeIndividualTag(Severity.CRITICAL); - listPage.filter.removeIndividualTag(Severity.WARNING); - listPage.filter.removeIndividualTag(Severity.INFO); - listPage.filter.removeIndividualTag(Severity.NONE); - listPage.filter.removeIndividualTag(Source.PLATFORM); - listPage.filter.removeIndividualTag(Source.USER); - - alertingRuleListPage.filter.assertNoClearAllFilters(); - - cy.log('4.3 Search by Name'); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`, FilterOUIAIDs.RuleNameFilter); + listPage.filter.byName(`${alertName}`, FilterOUIAIDs.RuleNameFilter); alertingRuleListPage.countShouldBe(1); listPage.filter.clearAllFilters(); - cy.log('4.4 Search by Label'); - listPage.filter.byLabel(`namespace=${WatchdogAlert.NAMESPACE}`); + cy.log('5.2 Search by Label'); + listPage.filter.byLabel(`namespace=${alertNamespace}`); listPage.filter.clearAllFilters(); - cy.log('4.5 Search by Name and see details'); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`, FilterOUIAIDs.RuleNameFilter); + cy.log('5.3 Search by Name and see details'); + listPage.filter.byName(`${alertName}`, FilterOUIAIDs.RuleNameFilter); alertingRuleListPage.countShouldBe(1); - alertingRuleListPage.clickAlertingRule(`${WatchdogAlert.ALERTNAME}`); - alertingRuleDetailsPage.assertAlertingRuleDetailsPage(`${WatchdogAlert.ALERTNAME}`); + alertingRuleListPage.clickAlertingRule(`${alertName}`); + alertingRuleDetailsPage.assertAlertingRuleDetailsPage(`${alertName}`); - cy.log('4.6 Alerting rule details > Silence alert'); + cy.log('5.4 Alerting rule details > Silence alert'); alertingRuleDetailsPage.clickOnKebabSilenceAlert(); silenceAlertPage.addComment(SilenceComment.SILENCE_COMMENT); silenceAlertPage.clickSubmit(); - commonPages.titleShouldHaveText(`${WatchdogAlert.ALERTNAME}`); + commonPages.titleShouldHaveText(`${alertName}`); - cy.log('4.7 Alerting rule details > Assert Kebab'); + cy.log('5.5 Alerting rule details > Assert Kebab'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Alerting rules'); listPage.filter.clearAllFilters(); - listPage.filter.byName(`${WatchdogAlert.ALERTNAME}`, FilterOUIAIDs.RuleNameFilter); - alertingRuleListPage.clickAlertingRule(`${WatchdogAlert.ALERTNAME}`); + listPage.filter.byName(`${alertName}`, FilterOUIAIDs.RuleNameFilter); + alertingRuleListPage.clickAlertingRule(`${alertName}`); alertingRuleDetailsPage.assertNoKebab(); - cy.log('4.8 Expire silence'); + cy.log('5.6 Expire silence'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Silences'); silencesListPage.shouldBeLoaded(); listPage.filter.removeIndividualTag(SilenceState.ACTIVE); listPage.filter.removeIndividualTag(SilenceState.PENDING); - silencesListPage.filter.byName(`${WatchdogAlert.ALERTNAME}`); + silencesListPage.filter.byName(`${alertName}`); listPage.filter.selectFilterOption('Silence State', SilenceState.ACTIVE); silencesListPage.rows.expireSilence(true); }); diff --git a/web/cypress/support/monitoring/04.reg_alerts_namespace.cy.ts b/web/cypress/support/monitoring/04.reg_alerts_namespace.cy.ts index 77b118c00..fa132a0ae 100644 --- a/web/cypress/support/monitoring/04.reg_alerts_namespace.cy.ts +++ b/web/cypress/support/monitoring/04.reg_alerts_namespace.cy.ts @@ -240,6 +240,7 @@ export function testAlertsRegressionNamespace(perspective: PerspectiveConfig) { cy.log('4.1 use sidebar nav to go to Observe > Alerting'); nav.tabs.switchTab('Alerting rules'); alertingRuleListPage.shouldBeLoaded(); + listPage.filter.removeIndividualTag(Source.PLATFORM); cy.log('4.2 clear all filters, verify filters and tags'); listPage.filter.selectFilterOption('Alert State', AlertingRulesAlertState.FIRING); @@ -322,7 +323,5 @@ export function testAlertsRegressionNamespace(perspective: PerspectiveConfig) { listPage.emptyState(); nav.tabs.switchTab('Silences'); silencesListPage.firstTimeEmptyState(); - nav.tabs.switchTab('Alerting rules'); - alertingRuleListPage.emptyState(); }); } diff --git a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts index 2c0206fb1..d9487232d 100644 --- a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts +++ b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts @@ -20,6 +20,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runBVTCOOPersesTests1(perspective: PerspectiveConfig) { @@ -30,7 +31,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { it(`1.${perspective.name} perspective - Dashboards (Perses) page`, () => { cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); cy.changeNamespace('All Projects'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); listPersesDashboardsPage.clickDashboard( persesDashboardsDashboardDropdownCOO.ACCELERATORS_COMMON_METRICS[0], ); @@ -43,7 +44,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { `Accelerators common metrics dashboard`, ); cy.changeNamespace('openshift-cluster-observability-operator'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); listPersesDashboardsPage.clickDashboard( persesDashboardsDashboardDropdownCOO.ACCELERATORS_COMMON_METRICS[0], ); @@ -120,6 +121,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { persesDashboardsDashboardDropdownCOO.ACCELERATORS_COMMON_METRICS[2], 'openshift-cluster-observability-operator', ); + persesDashboardsPage.backToListPersesDashboardsPage(); }); it( @@ -128,7 +130,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { () => { cy.log(`5.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`5.2. Change namespace to perses-dev`); cy.changeNamespace('perses-dev'); @@ -170,8 +172,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { listPersesDashboardsPage.renameDashboardRenameButton(); cy.log(`5.5. Click on the Kebab icon - Delete`); - nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + listPersesDashboardsPage.clearAllFilters(); listPersesDashboardsPage.filter.byName( persesDashboardsDashboardDropdownPersesDev.PERSES_DASHBOARD_SAMPLE[0] + ' - Renamed', ); @@ -185,9 +186,8 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { listPersesDashboardsPage.countDashboards('0'); cy.log(`5.7. Search for the renamed dashboard`); - nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); cy.changeNamespace('All Projects'); + listPersesDashboardsPage.clearAllFilters(); listPersesDashboardsPage.filter.byName( persesDashboardsDashboardDropdownPersesDev.PERSES_DASHBOARD_SAMPLE[0] + ' - Renamed', ); @@ -204,7 +204,12 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { const randomSuffix = Math.random().toString(5); dashboardName += randomSuffix; cy.log(`6.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`6.2. Click on Create button`); listPersesDashboardsPage.clickCreateButton(); @@ -342,8 +347,7 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { listPersesDashboardsPage.deleteDashboardDeleteButton(); listPersesDashboardsPage.emptyState(); listPersesDashboardsPage.countDashboards('0'); - nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + listPersesDashboardsPage.clearAllFilters(); cy.log(`6.15. Filter by Name`); listPersesDashboardsPage.filter.byName(dashboardName); diff --git a/web/cypress/support/perses/01.coo_list_perses_admin.cy.ts b/web/cypress/support/perses/01.coo_list_perses_admin.cy.ts index 4244bf4e1..09026436b 100644 --- a/web/cypress/support/perses/01.coo_list_perses_admin.cy.ts +++ b/web/cypress/support/perses/01.coo_list_perses_admin.cy.ts @@ -10,6 +10,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOListPersesTests(perspective: PerspectiveConfig) { @@ -24,7 +25,7 @@ export function testCOOListPerses(perspective: PerspectiveConfig) { it(`1.${perspective.name} perspective - List Dashboards (Perses) page`, () => { cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2. Filter by Name`); listPersesDashboardsPage.filter.byName( diff --git a/web/cypress/support/perses/01.coo_list_perses_admin_namespace.cy.ts b/web/cypress/support/perses/01.coo_list_perses_admin_namespace.cy.ts index 16c35c175..5ca0b95e7 100644 --- a/web/cypress/support/perses/01.coo_list_perses_admin_namespace.cy.ts +++ b/web/cypress/support/perses/01.coo_list_perses_admin_namespace.cy.ts @@ -10,6 +10,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOListPersesTestsNamespace(perspective: PerspectiveConfig) { @@ -20,7 +21,7 @@ export function testCOOListPersesNamespace(perspective: PerspectiveConfig) { it(`1.${perspective.name} perspective - List Dashboards (Perses) page`, () => { cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2. Change namespace to perses-dev`); cy.changeNamespace('perses-dev'); @@ -112,8 +113,7 @@ export function testCOOListPersesNamespace(perspective: PerspectiveConfig) { listPersesDashboardsPage.emptyState(); listPersesDashboardsPage.countDashboards('0'); - nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + listPersesDashboardsPage.clearAllFilters(); cy.log(`1.11. Click on a dashboard`); listPersesDashboardsPage.clickDashboard(persesDashboardsDashboardDropdownCOO.APM_DASHBOARD[0]); @@ -127,7 +127,7 @@ export function testCOOListPersesNamespace(perspective: PerspectiveConfig) { () => { cy.log(`2.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2. Change namespace to perses-dev`); cy.changeNamespace('perses-dev'); @@ -184,7 +184,10 @@ export function testCOOListPersesNamespace(perspective: PerspectiveConfig) { cy.log(`2.7. Search for the renamed dashboard`); nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); listPersesDashboardsPage.filter.byName( persesDashboardsDashboardDropdownPersesDev.PERSES_DASHBOARD_SAMPLE[0] + ' - Renamed', ); diff --git a/web/cypress/support/perses/02.coo_edit_perses_admin.cy.ts b/web/cypress/support/perses/02.coo_edit_perses_admin.cy.ts index 146302018..773d8bad1 100644 --- a/web/cypress/support/perses/02.coo_edit_perses_admin.cy.ts +++ b/web/cypress/support/perses/02.coo_edit_perses_admin.cy.ts @@ -16,6 +16,7 @@ import { persesDashboardsEditVariables } from '../../views/perses-dashboards-edi export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOEditPersesTests(perspective: PerspectiveConfig) { @@ -26,7 +27,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { it(`1.${perspective.name} perspective - Edit perses dashboard page`, () => { cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -57,7 +58,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { cy.log(`1.6. Change namespace to All Projects`); cy.changeNamespace('All Projects'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); }); it( @@ -65,7 +66,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { () => { cy.log(`2.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -131,7 +132,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { () => { cy.log(`3.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`3.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -195,7 +196,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { () => { cy.log(`4.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`4.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -287,7 +288,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { () => { cy.log(`5.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`5.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -319,13 +320,13 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { }, ); - /**TODO: https://issues.redhat.com/browse/OU-1054 is targeted for COO1.5.0, + /**TODO: https://issues.redhat.com/browse/OU-1054 is targeted for COO1.6.0, * so, commenting all Datasources related scenarios it(`6.${perspective.name} perspective - Edit Toolbar - Edit Datasources - Add and Delete Prometheus Datasource`, () => { cy.log(`6.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`6.2. Filter by Name`); listPersesDashboardsPage @@ -376,7 +377,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { - Edit Prometheus Datasource`, () => { cy.log(`7.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`7.2. Filter by Name`); listPersesDashboardsPage @@ -457,7 +458,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { it(`6.${perspective.name} perspective - Edit Toolbar - Add Panel Group`, () => { cy.log(`6.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`6.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -499,7 +500,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { it(`7.${perspective.name} perspective - Edit Toolbar - Edit Panel Group`, () => { cy.log(`7.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`7.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -537,7 +538,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { it(`8.${perspective.name} perspective - Edit Toolbar - Move Panel Group Down and Up`, () => { cy.log(`8.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`8.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -608,7 +609,7 @@ export function testCOOEditPerses(perspective: PerspectiveConfig) { it(`9.${perspective.name} perspective - Edit Toolbar - Delete Panel Group`, () => { cy.log(`9.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`9.2. Filter by Name`); listPersesDashboardsPage.filter.byName( diff --git a/web/cypress/support/perses/02.coo_edit_perses_admin_1.cy.ts b/web/cypress/support/perses/02.coo_edit_perses_admin_1.cy.ts index fc7df22dc..9fb46ef46 100644 --- a/web/cypress/support/perses/02.coo_edit_perses_admin_1.cy.ts +++ b/web/cypress/support/perses/02.coo_edit_perses_admin_1.cy.ts @@ -13,6 +13,7 @@ import { persesDashboardsPanelGroup } from '../../views/perses-dashboards-panelg export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOEditPersesTests1(perspective: PerspectiveConfig) { @@ -23,7 +24,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`10.${perspective.name} perspective - Edit Toolbar - Add Panel`, () => { cy.log(`10.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`10.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -65,7 +66,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`11.${perspective.name} perspective - Edit Toolbar - Edit Panel`, () => { cy.log(`11.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`11.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -115,7 +116,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`12.${perspective.name} perspective - Edit Toolbar - Delete Panel`, () => { cy.log(`12.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`12.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -149,7 +150,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`13.${perspective.name} perspective - Edit Toolbar - Duplicate Panel`, () => { cy.log(`13.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`13.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -178,7 +179,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`14.${perspective.name} perspective - Edit Toolbar - Perform changes and Cancel`, () => { cy.log(`14.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`14.2. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -256,7 +257,7 @@ export function testCOOEditPerses1(perspective: PerspectiveConfig) { it(`15.${perspective.name} perspective - ` + 'Try to editAccelerators and APM dashboards', () => { cy.log(`15.1. use sidebar nav to go to Observe > Dashboards (Perses)`); commonPages.titleShouldHaveText('Dashboards'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`15.2. Filter by Name`); listPersesDashboardsPage.filter.byName( diff --git a/web/cypress/support/perses/03.coo_create_perses_admin.cy.ts b/web/cypress/support/perses/03.coo_create_perses_admin.cy.ts index dab4172e8..48c3ae489 100644 --- a/web/cypress/support/perses/03.coo_create_perses_admin.cy.ts +++ b/web/cypress/support/perses/03.coo_create_perses_admin.cy.ts @@ -14,6 +14,7 @@ import { persesDashboardsAddListVariableSource } from '../../fixtures/perses/con export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOCreatePersesTests(perspective: PerspectiveConfig) { @@ -26,7 +27,7 @@ export function testCOOCreatePerses(perspective: PerspectiveConfig) { const randomSuffix = Math.random().toString(5); dashboardName += randomSuffix; cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2. Click on Create button`); listPersesDashboardsPage.clickCreateButton(); @@ -61,7 +62,7 @@ export function testCOOCreatePerses(perspective: PerspectiveConfig) { const randomSuffix = Math.random().toString(5); dashboardName += randomSuffix; cy.log(`2.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2. Click on Create button`); listPersesDashboardsPage.clickCreateButton(); @@ -115,7 +116,7 @@ export function testCOOCreatePerses(perspective: PerspectiveConfig) { const randomSuffix = Math.random().toString(5); dashboardName += randomSuffix; cy.log(`3.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`3.2. Click on Create button`); listPersesDashboardsPage.clickCreateButton(); @@ -251,7 +252,7 @@ export function testCOOCreatePerses(perspective: PerspectiveConfig) { // but all namespaces you have access to, independently of having perses object // (that creates a perses project)`, () => { // cy.log(`4.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - // listPersesDashboardsPage.shouldBeLoaded(); + // listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); // cy.log(`4.2. Click on Create button`); // listPersesDashboardsPage.clickCreateButton(); diff --git a/web/cypress/support/perses/04.coo_import_perses_admin.cy.ts b/web/cypress/support/perses/04.coo_import_perses_admin.cy.ts index f95af5101..563ac92cb 100644 --- a/web/cypress/support/perses/04.coo_import_perses_admin.cy.ts +++ b/web/cypress/support/perses/04.coo_import_perses_admin.cy.ts @@ -6,6 +6,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOOImportPersesTests(perspective: PerspectiveConfig) { @@ -15,7 +16,7 @@ export function runCOOImportPersesTests(perspective: PerspectiveConfig) { export function testCOOImportPerses(perspective: PerspectiveConfig) { it(`1. ${perspective.name} perspective - Import Dashboard - wrong format`, () => { cy.log(`1.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); @@ -60,7 +61,7 @@ export function testCOOImportPerses(perspective: PerspectiveConfig) { it(`2. ${perspective.name} perspective - Import Dashboard - ACM Grafana dashboard`, () => { cy.log(`2.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); @@ -109,7 +110,7 @@ export function testCOOImportPerses(perspective: PerspectiveConfig) { it(`3. ${perspective.name} perspective - Import Dashboard - Perses dashboard - JSON file`, () => { cy.log(`3.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`3.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); @@ -155,7 +156,7 @@ export function testCOOImportPerses(perspective: PerspectiveConfig) { it(`4. ${perspective.name} perspective - Import Dashboard - Perses dashboard - YAML file`, () => { cy.log(`4.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`4.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); @@ -207,7 +208,7 @@ export function testCOOImportPerses(perspective: PerspectiveConfig) { ]; cy.log(`5.1 Navigate to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); dashboardsToDelete.forEach((dashboardName, index) => { cy.log(`5.${index + 2}.1 Filter by Name: ${dashboardName}`); @@ -222,13 +223,19 @@ export function testCOOImportPerses(perspective: PerspectiveConfig) { listPersesDashboardsPage.emptyState(); listPersesDashboardsPage.countDashboards('0'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); cy.log(`5.${index + 2}.3 Verify dashboard is deleted`); listPersesDashboardsPage.filter.byName(dashboardName); listPersesDashboardsPage.countDashboards('0'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); - nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); }); }); } diff --git a/web/cypress/support/perses/05.coo_create_import_perses_admin.cy.ts b/web/cypress/support/perses/05.coo_create_import_perses_admin.cy.ts index a1b52017b..eca64a548 100644 --- a/web/cypress/support/perses/05.coo_create_import_perses_admin.cy.ts +++ b/web/cypress/support/perses/05.coo_create_import_perses_admin.cy.ts @@ -8,6 +8,7 @@ import { persesImportDashboardsPage } from '../../views/perses-dashboards-import export interface PerspectiveConfig { name: string; + dashboardsPageName?: string; beforeEach?: () => void; } @@ -24,7 +25,7 @@ export function testCOOCreateImportPerses(perspective: PerspectiveConfig) { const randomSuffix = Math.random().toString(5); dashboardName += randomSuffix; cy.log(`1.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`1.2. Click on Create button`); listPersesDashboardsPage.clickCreateButton(); @@ -96,7 +97,7 @@ export function testCOOCreateImportPerses(perspective: PerspectiveConfig) { it(`2. ${perspective.name} perspective - Import Dashboard - Perses dashboard - JSON file`, () => { cy.log(`2.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); @@ -126,7 +127,7 @@ export function testCOOCreateImportPerses(perspective: PerspectiveConfig) { it(`3. ${perspective.name} perspective - Import Dashboard - Perses dashboard - YAML file`, () => { cy.log(`3.1 use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`3.2 Click on Import button`); listPersesDashboardsPage.clickImportButton(); diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user1.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user1.cy.ts index ea1d380ae..cdd6f6568 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user1.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user1.cy.ts @@ -19,6 +19,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOORBACPersesTestsDevUser1(perspective: PerspectiveConfig) { @@ -39,7 +40,7 @@ export function testCOORBACPersesTestsDevUser1(perspective: PerspectiveConfig) { `Dashboard search`, () => { cy.log(`1.1. Namespace validation`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.assertNamespace('All Projects', true); cy.assertNamespace('openshift-cluster-observability-operator', true); cy.assertNamespace('observ-test', true); @@ -100,11 +101,11 @@ export function testCOORBACPersesTestsDevUser1(perspective: PerspectiveConfig) { it(`2.${perspective.name} perspective - Edit button validation - Editable dashboard`, () => { cy.log(`2.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2 change namespace to openshift-cluster-observability-operator`); cy.changeNamespace('openshift-cluster-observability-operator'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.3. Filter by Name`); listPersesDashboardsPage.filter.byName( diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user2.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user2.cy.ts index 5813b627f..27d5d74e8 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user2.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user2.cy.ts @@ -8,6 +8,7 @@ import { export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { @@ -26,7 +27,7 @@ export function testCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { `Dashboard search`, () => { cy.log(`1.1. Namespace validation`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.assertNamespace('All Projects', true); cy.assertNamespace('openshift-cluster-observability-operator', false); cy.assertNamespace('observ-test', false); @@ -99,11 +100,11 @@ export function testCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { it(`2.${perspective.name} perspective - Edit button validation - Not Editable dashboard`, () => { cy.log(`2.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.2 change namespace to perses-dev`); cy.changeNamespace('perses-dev'); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`2.3. Filter by Name`); listPersesDashboardsPage.filter.byName( @@ -121,7 +122,7 @@ export function testCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { it(`3.${perspective.name} perspective - Create button validation - Disabled`, () => { cy.log(`3.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`3.2. Verify Create button is disabled`); listPersesDashboardsPage.assertCreateButtonIsDisabled(); @@ -139,7 +140,7 @@ export function testCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { it(`4.${perspective.name} perspective - Kebab icon - Disabled`, () => { cy.log(`4.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`4.2. Change namespace to perses-dev`); cy.changeNamespace('perses-dev'); @@ -166,7 +167,7 @@ export function testCOORBACPersesTestsDevUser2(perspective: PerspectiveConfig) { it(`5.${perspective.name} perspective - Import button validation - Disabled`, () => { cy.log(`5.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`5.2. Change namespace to perses-dev`); cy.changeNamespace('perses-dev'); diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user3.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user3.cy.ts index 7890ed30e..d4d2d87d1 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user3.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user3.cy.ts @@ -18,6 +18,7 @@ import { persesAriaLabels } from '@/shared/constants/data-test'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOORBACPersesTestsDevUser3(perspective: PerspectiveConfig) { @@ -274,7 +275,7 @@ export function testCOORBACPersesTestsDevUser3(perspective: PerspectiveConfig) { it(`4.${perspective.name} perspective - Kebab icon - Enabled / Disabled`, () => { cy.log(`4.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`4.2. Change namespace to empty-namespace3`); cy.changeNamespace('empty-namespace3'); @@ -305,7 +306,7 @@ export function testCOORBACPersesTestsDevUser3(perspective: PerspectiveConfig) { renamedDashboardName += randomSuffix; cy.log(`5.1. use sidebar nav to go to Observe > Dashboards (Perses)`); - listPersesDashboardsPage.shouldBeLoaded(); + listPersesDashboardsPage.shouldBeLoaded(perspective.dashboardsPageName); cy.log(`5.2. Change namespace to empty-namespace3`); cy.changeNamespace('empty-namespace3'); diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user4.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user4.cy.ts index b5e1690ab..a023cee80 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user4.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user4.cy.ts @@ -3,6 +3,7 @@ import { listPersesDashboardsPage } from '../../views/perses-dashboards-list-das export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOORBACPersesTestsDevUser4(perspective: PerspectiveConfig) { diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user5.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user5.cy.ts index 85b2fde7b..00fc38fe8 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user5.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user5.cy.ts @@ -16,6 +16,7 @@ import { nav } from '../../views/nav'; export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardsPageName?: string; } export function runCOORBACPersesTestsDevUser5(perspective: PerspectiveConfig) { diff --git a/web/cypress/support/perses/99.coo_rbac_perses_user6.cy.ts b/web/cypress/support/perses/99.coo_rbac_perses_user6.cy.ts index ae54b1bfd..77a08d1d7 100644 --- a/web/cypress/support/perses/99.coo_rbac_perses_user6.cy.ts +++ b/web/cypress/support/perses/99.coo_rbac_perses_user6.cy.ts @@ -3,6 +3,7 @@ import { listPersesDashboardsPage } from '../../views/perses-dashboards-list-das export interface PerspectiveConfig { name: string; beforeEach?: () => void; + dashboardSearch?: string; } export function runCOORBACPersesTestsDevUser6(perspective: PerspectiveConfig) { diff --git a/web/cypress/views/alerting-rule-list-page.ts b/web/cypress/views/alerting-rule-list-page.ts index 67a4e858b..fc81a50f9 100644 --- a/web/cypress/views/alerting-rule-list-page.ts +++ b/web/cypress/views/alerting-rule-list-page.ts @@ -1,11 +1,9 @@ import { Classes, DataTestIDs, FilterOUIAIDs } from '@/shared/constants/data-test'; -import { Source } from '../fixtures/monitoring/constants'; import { listPage } from './list-page'; export const alertingRuleListPage = { shouldBeLoaded: () => { cy.log('alertingRuleListPage.shouldBeLoaded'); - listPage.filter.removeIndividualTag(Source.PLATFORM); cy.byTestID(DataTestIDs.AlertingRuleResourceIcon).contains('AR'); cy.get(Classes.TableHeaderColumn).contains('Name').should('be.visible'); cy.get(Classes.TableHeaderColumn).contains('Severity').should('be.visible'); @@ -74,9 +72,11 @@ export const alertingRuleListPage = { cy.byTestID(DataTestIDs.AlertingRuleStateBadge).contains(total).should('exist'); cy.byTestID(DataTestIDs.AlertingRuleStateBadge).contains(state).should('exist'); }, + /** + * @deprecated This method is deprecated and will be removed in the future. + */ emptyState: () => { cy.log('alertingRuleListPage.emptyState'); cy.byTestID(DataTestIDs.EmptyBoxBody).contains('No alerting rules found').should('be.visible'); - cy.byOUIAID('DataViewToolbar-clear-all-filters').should('not.be.visible'); }, }; diff --git a/web/cypress/views/incidents-page.ts b/web/cypress/views/incidents-page.ts index 6e4f42caf..f4d459e7c 100644 --- a/web/cypress/views/incidents-page.ts +++ b/web/cypress/views/incidents-page.ts @@ -1,5 +1,5 @@ import { nav } from './nav'; -import { DataTestIDs } from '@/shared/constants/data-test'; +import { Classes, DataTestIDs } from '@/shared/constants/data-test'; // Hard timeout safety net for findIncidentWithAlert retry loops. // Prevents infinite loops if cy.waitUntil's timeout mechanism fails. @@ -1074,4 +1074,12 @@ export const incidentsPage = { }); }); }, + /** + * Checks if the Incidents page is not visible, such as Fleet management, + * Developer perspective, etc. + */ + incidentsPageShouldNotBeVisible: () => { + cy.log('incidentsPage.incidentsPageShouldNotBeVisible'); + cy.get(Classes.HorizontalNav).contains('Incidents').should('not.exist'); + }, }; diff --git a/web/cypress/views/list-page.ts b/web/cypress/views/list-page.ts index bc312ec3b..87484ccf9 100644 --- a/web/cypress/views/list-page.ts +++ b/web/cypress/views/list-page.ts @@ -106,13 +106,12 @@ export const listPage = { clearAllFilters: () => { cy.log('listPage.filter.clearAllFilters'); - try { - cy.byOUIAID('DataViewFilters').scrollIntoView(); - cy.byOUIAID('DataViewToolbar-clear-all-filters').first().click(); - } catch (error) { - cy.log(`${error.message}`); - throw error; - } + cy.byOUIAID('DataViewFilters').scrollIntoView(); + cy.get('body').then(($body) => { + if ($body.find('[data-ouia-component-id="DataViewToolbar-clear-all-filters"]').length > 0) { + cy.byOUIAID('DataViewToolbar-clear-all-filters').first().click(); + } + }); }, /** @@ -287,8 +286,6 @@ export const listPage = { emptyState: () => { cy.log('listPage.emptyState'); cy.byTestID(DataTestIDs.EmptyBoxBody).contains('No alerts found').should('be.visible'); - cy.byOUIAID('DataViewToolbar-clear-all-filters').should('not.be.visible'); - cy.byTestID(DataTestIDs.DownloadCSVButton).should('not.exist'); cy.byOUIAID(DataTestIDs.Table).should('not.exist'); }, }; diff --git a/web/cypress/views/nav.ts b/web/cypress/views/nav.ts index d947f2467..dff5c7af9 100644 --- a/web/cypress/views/nav.ts +++ b/web/cypress/views/nav.ts @@ -8,6 +8,7 @@ export const nav = { }, switcher: { changePerspectiveTo: (...perspectives: string[]) => { + cy.log('changePerspectiveTo - ' + perspectives.join(', ')); cy.get('body').then((body) => { if (body.find('button[data-test-id="perspective-switcher-toggle"]:visible').length > 0) { cy.byLegacyTestID('perspective-switcher-toggle') @@ -41,7 +42,11 @@ export const nav = { * @param tabname - The name of the tab to switch to */ switchTab: (tabname: string) => { - cy.get(Classes.HorizontalNav).contains(tabname).should('be.visible').click({ force: true }); + cy.get(Classes.HorizontalNav) + .contains(tabname) + .scrollIntoView() + .should('be.visible') + .click({ force: true }); cy.wait(2000); }, }, diff --git a/web/cypress/views/perses-dashboards-edit-variables.ts b/web/cypress/views/perses-dashboards-edit-variables.ts index f25a481af..5fbe9f387 100644 --- a/web/cypress/views/perses-dashboards-edit-variables.ts +++ b/web/cypress/views/perses-dashboards-edit-variables.ts @@ -146,7 +146,8 @@ export const persesDashboardsEditVariables = { .eq(0) .find('input[role="combobox"]') .click() - .type(value + '{enter}'); + .type(value, { delay: 100 }) + .type('{enter}'); cy.wait(2000); cy.byDataTestID(persesMUIDataTestIDs.editDashboardAddVariableRunQueryButton).click(); cy.wait(2000); diff --git a/web/cypress/views/perses-dashboards-list-dashboards.ts b/web/cypress/views/perses-dashboards-list-dashboards.ts index 81ce5d8c4..c1717ca95 100644 --- a/web/cypress/views/perses-dashboards-list-dashboards.ts +++ b/web/cypress/views/perses-dashboards-list-dashboards.ts @@ -26,7 +26,9 @@ export const listPersesDashboardsPage = { cy.byTestID(listPersesDashboardsDataTestIDs.EmptyStateBody) .should('be.visible') .contains(listPersesDashboardsEmptyState.BODY); - cy.byTestID(listPersesDashboardsDataTestIDs.ClearAllFiltersButton).should('be.visible'); + cy.byOUIAID(listPersesDashboardsOUIAIDs.persesListDataViewHeaderClearAllFiltersButton).should( + 'be.visible', + ); }, noDashboardsFoundState: () => { @@ -37,10 +39,12 @@ export const listPersesDashboardsPage = { cy.byTestID(listPersesDashboardsDataTestIDs.EmptyStateBody) .should('be.visible') .contains(listPersesDashboardsNoDashboardsFoundState.BODY); - cy.byTestID(listPersesDashboardsDataTestIDs.ClearAllFiltersButton).should('not.exist'); + cy.byOUIAID(listPersesDashboardsOUIAIDs.persesListDataViewHeaderClearAllFiltersButton).should( + 'not.exist', + ); }, - shouldBeLoaded: () => { + shouldBeLoaded: (dashboardsPageName?: string) => { cy.log('listPersesDashboardsPage.shouldBeLoaded'); cy.byOUIAID(listPersesDashboardsOUIAIDs.PersesBreadcrumb).should('not.exist'); commonPages.titleShouldHaveText(MonitoringPageTitles.DASHBOARDS); @@ -48,7 +52,11 @@ export const listPersesDashboardsPage = { .should('contain', listPersesDashboardsPageSubtitle) .should('be.visible'); cy.byTestID(DataTestIDs.PersesCreateDashboardButton).scrollIntoView().should('be.visible'); - cy.byTestID(DataTestIDs.FavoriteStarButton).should('be.visible'); + if (dashboardsPageName === 'Dashboards') { + cy.byTestID(DataTestIDs.FavoriteStarButton).should('not.exist'); + } else { + cy.byTestID(DataTestIDs.FavoriteStarButton).should('be.visible'); + } cy.byOUIAID(listPersesDashboardsOUIAIDs.PersesDashListDataViewTable).should('be.visible'); }, @@ -57,12 +65,12 @@ export const listPersesDashboardsPage = { cy.log('listPersesDashboardsPage.filter.byName'); cy.wait(1000); cy.byOUIAID(listPersesDashboardsOUIAIDs.persesListDataViewFilters) - .contains('button', /Name|Project/) + .contains('button', /Dashboard|Project/) .click({ force: true }); cy.wait(1000); cy.get(Classes.FilterDropdownOption) .should('be.visible') - .contains('Name') + .contains('Dashboard') .click({ force: true }); cy.wait(1000); cy.byTestID(listPersesDashboardsDataTestIDs.NameFilter).should('be.visible').type(name); @@ -75,7 +83,7 @@ export const listPersesDashboardsPage = { byProject: (project: string) => { cy.log('listPersesDashboardsPage.filter.byProject'); cy.byOUIAID(listPersesDashboardsOUIAIDs.persesListDataViewFilters) - .contains('button', /Name|Project/) + .contains('button', /Dashboard|Project/) .click({ force: true }); cy.wait(1000); cy.get(Classes.FilterDropdownOption) diff --git a/web/cypress/views/silence-alert-page.ts b/web/cypress/views/silence-alert-page.ts index f32801227..e8885253a 100644 --- a/web/cypress/views/silence-alert-page.ts +++ b/web/cypress/views/silence-alert-page.ts @@ -97,6 +97,7 @@ export const silenceAlertPage = { } cy.wrap($row) .find('[data-test="' + DataTestIDs.SilencesPageFormTestIDs.RemoveLabel + '"]') + .scrollIntoView() .should('be.visible'); } else { return; diff --git a/web/cypress/views/silences-list-page.ts b/web/cypress/views/silences-list-page.ts index 0934ecc1d..48e14efa3 100644 --- a/web/cypress/views/silences-list-page.ts +++ b/web/cypress/views/silences-list-page.ts @@ -15,7 +15,6 @@ export const silencesListPage = { firstTimeEmptyState: () => { cy.log('silencesListPage.firstTimeEmptyState'); cy.byTestID(DataTestIDs.EmptyBoxBody).contains('No silences found').should('be.visible'); - cy.byOUIAID('DataViewToolbar-clear-all-filters').should('not.be.visible'); }, emptyState: () => { diff --git a/web/src/shared/constants/data-test.ts b/web/src/shared/constants/data-test.ts index 270a128c3..d44a9de3c 100644 --- a/web/src/shared/constants/data-test.ts +++ b/web/src/shared/constants/data-test.ts @@ -333,7 +333,6 @@ export const listPersesDashboardsDataTestIDs = { ProjectFilter: 'project-filter', EmptyStateTitle: 'empty-state-title', EmptyStateBody: 'empty-state-body', - ClearAllFiltersButton: 'clear-all-filters-button', DashboardLinkPrefix: 'perseslistpage-', }; From 02a9aa8100ff9c0613af6f384630ce1313a0d9a5 Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Fri, 31 Jul 2026 20:24:43 -0300 Subject: [PATCH 2/9] fixing MCP CONSOLE IMAGE injection --- web/cypress/fixtures/coo/update-mcp-image.sh | 63 +++++++++++-- .../support/commands/image-patch-commands.ts | 88 +++++++++++++++++++ .../support/commands/operator-commands.ts | 1 + 3 files changed, 143 insertions(+), 9 deletions(-) diff --git a/web/cypress/fixtures/coo/update-mcp-image.sh b/web/cypress/fixtures/coo/update-mcp-image.sh index d4722517f..859786de4 100755 --- a/web/cypress/fixtures/coo/update-mcp-image.sh +++ b/web/cypress/fixtures/coo/update-mcp-image.sh @@ -1,22 +1,67 @@ #!/bin/bash +echo "--------------------------------" +echo "MCP_CONSOLE_IMAGE: ${MCP_CONSOLE_IMAGE}" +echo "--------------------------------" # Generate a random filename RANDOM_FILE="/tmp/coo_monitoring_csv_$(date +%s%N).yaml" COO_CSV_NAME=$(oc get csv --kubeconfig "${KUBECONFIG}" --namespace="${MCP_NAMESPACE}" | grep "cluster-observability-operator" | awk '{print $1}') +echo "COO_CSV_NAME: ${COO_CSV_NAME}" -oc get csv "${COO_CSV_NAME}" -n "${MCP_NAMESPACE}" -o yaml > "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" +patch_csv() { + oc get csv "${COO_CSV_NAME}" -n "${MCP_NAMESPACE}" -o yaml > "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" -# Patch the CSV file env vars -sed -i "s#value: .*monitoring-console-plugin.*#value: ${MCP_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" + # Patch the CSV file env vars + sed -i "s#value: .*monitoring-console-plugin.*#value: ${MCP_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" -# Patch the CSV file related images -sed -i "s#^\([[:space:]]*- image:\).*monitoring-console-plugin.*#\1 ${MCP_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" + # Patch the CSV file related images + sed -i "s#^\([[:space:]]*- image:\).*monitoring-console-plugin.*#\1 ${MCP_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" -# Apply the patched CSV resource file -oc replace -f "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" + echo "--------------------------------" + echo "COO CSV content after patching (local file)" + echo "--------------------------------" + cat "${RANDOM_FILE}" + echo "--------------------------------" + + oc replace -f "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" +} + +# Initial patch +patch_csv + +# Verify the CSV patch was applied and not reverted by OLM. +# OLM may reconcile the CSV and revert the image back to the original. +MAX_CSV_RETRIES=6 +CSV_RETRY_INTERVAL=10 +CSV_PATCHED=false + +for i in $(seq 1 $MAX_CSV_RETRIES); do + sleep $CSV_RETRY_INTERVAL + + LIVE_CSV=$(oc get csv "${COO_CSV_NAME}" -n "${MCP_NAMESPACE}" -o yaml --kubeconfig "${KUBECONFIG}") + + if echo "${LIVE_CSV}" | grep -q "${MCP_CONSOLE_IMAGE}"; then + echo "CSV patch verified successfully on attempt ${i}" + CSV_PATCHED=true + break + fi + + echo "CSV patch was reverted by OLM (attempt ${i}/${MAX_CSV_RETRIES}). Re-applying..." + patch_csv +done + +if [ "${CSV_PATCHED}" = false ]; then + echo "WARNING: CSV patch could not be stabilized after ${MAX_CSV_RETRIES} attempts" +fi + +echo "--------------------------------" +echo "COO CSV live content after verification loop" +echo "--------------------------------" +oc get csv "${COO_CSV_NAME}" -n "${MCP_NAMESPACE}" -o yaml --kubeconfig "${KUBECONFIG}" +echo "--------------------------------" # Wait for the operator to reconcile the change and make sure all the pods are running. sleep 25 -OUTPUT=`oc wait --for=condition=ready pods -l app.kubernetes.io/name=observability-operator -n "${MCP_NAMESPACE}" --timeout=60s --kubeconfig "${KUBECONFIG}"` -echo "${OUTPUT}" \ No newline at end of file +OUTPUT=$(oc wait --for=condition=ready pods -l app.kubernetes.io/name=observability-operator -n "${MCP_NAMESPACE}" --timeout=60s --kubeconfig "${KUBECONFIG}") +echo "${OUTPUT}" diff --git a/web/cypress/support/commands/image-patch-commands.ts b/web/cypress/support/commands/image-patch-commands.ts index d8dd04acd..bd9f98035 100644 --- a/web/cypress/support/commands/image-patch-commands.ts +++ b/web/cypress/support/commands/image-patch-commands.ts @@ -83,6 +83,94 @@ export const imagePatchUtils = { }); }, + /** + * After the monitoring-console-plugin pod is running, verify it uses the + * expected CI image. If OLM reverted the CSV patch, re-apply it, patch the + * deployment directly, and wait until the pod rolls out with the correct image. + */ + verifyMonitoringConsolePluginImage(MCP: { namespace: string }): void { + const expectedImage = Cypress.env('MCP_CONSOLE_IMAGE'); + if (!expectedImage) { + return; + } + + cy.log('Verify monitoring-console-plugin pod image matches expected CI image'); + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + const maxAttempts = 5; + + const checkAndFix = (attempt: number): void => { + cy.exec( + `oc get pods -l app.kubernetes.io/instance=monitoring -n ${MCP.namespace} ` + + `-o jsonpath='{.items[0].spec.containers[0].image}' --kubeconfig "${kubeconfig}"`, + { failOnNonZeroExit: false }, + ).then((result) => { + const currentImage = result.stdout.replace(/'/g, '').trim(); + cy.log( + `monitoring-console-plugin image check ` + + `(attempt ${attempt}/${maxAttempts}): ${currentImage}`, + ); + + if (currentImage === expectedImage) { + cy.log('monitoring-console-plugin pod image verified successfully'); + return; + } + + if (attempt >= maxAttempts) { + throw new Error( + `monitoring-console-plugin pod image mismatch after ${maxAttempts} attempts.\n` + + `Expected: ${expectedImage}\nActual: ${currentImage}`, + ); + } + + cy.log('Image mismatch detected. Re-patching CSV and deployment...'); + + // Re-run the CSV patch script + cy.exec('./cypress/fixtures/coo/update-mcp-image.sh', { + env: { + MCP_CONSOLE_IMAGE: expectedImage, + KUBECONFIG: kubeconfig, + MCP_NAMESPACE: MCP.namespace, + }, + timeout: readyTimeoutMilliseconds, + failOnNonZeroExit: false, + }); + + // Patch the deployment directly in case the operator doesn't reconcile + cy.exec( + `oc get deployment -l app.kubernetes.io/instance=monitoring -n ${MCP.namespace} ` + + `-o jsonpath='{.items[0].metadata.name}' --kubeconfig "${kubeconfig}"`, + { failOnNonZeroExit: false }, + ).then((deployResult) => { + const deployName = deployResult.stdout.replace(/'/g, '').trim(); + if (deployName) { + cy.log(`Patching deployment/${deployName} image directly`); + cy.exec( + `oc patch deployment ${deployName} -n ${MCP.namespace} --type=json ` + + `-p '[{"op":"replace","path":"/spec/template/spec/containers/0/image",` + + `"value":"${expectedImage}"}]' ` + + `--kubeconfig "${kubeconfig}"`, + ); + cy.exec( + `oc rollout status deployment/${deployName} -n ${MCP.namespace} ` + + `--timeout=120s --kubeconfig "${kubeconfig}"`, + { timeout: 130000 }, + ); + } + }); + + waitForPodsReady( + 'app.kubernetes.io/instance=monitoring', + MCP.namespace, + readyTimeoutMilliseconds, + ); + + checkAndFix(attempt + 1); + }); + }; + + checkAndFix(1); + }, + revertMonitoringPluginImage(MP: { namespace: string }): void { if (Cypress.env('MP_IMAGE')) { cy.log('MP_IMAGE is set. Lets revert CMO operator CSV'); diff --git a/web/cypress/support/commands/operator-commands.ts b/web/cypress/support/commands/operator-commands.ts index e05168320..c81a858b6 100644 --- a/web/cypress/support/commands/operator-commands.ts +++ b/web/cypress/support/commands/operator-commands.ts @@ -240,6 +240,7 @@ Cypress.Commands.add( imagePatchUtils.setupClusterHealthAnalyzer(MCP); } dashboardsUtils.setupMonitoringUIPlugin(MCP); + imagePatchUtils.verifyMonitoringConsolePluginImage(MCP); if (opts.dashboards) { dashboardsUtils.setupDashboardsAndPlugins(MCP); } From 57dda5e38a77768a2d21ab882a4a3a3d428ee11e Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Fri, 31 Jul 2026 20:49:19 -0300 Subject: [PATCH 3/9] coderabbit interpolation fix --- web/cypress/support/commands/image-patch-commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/cypress/support/commands/image-patch-commands.ts b/web/cypress/support/commands/image-patch-commands.ts index bd9f98035..3dae30fe3 100644 --- a/web/cypress/support/commands/image-patch-commands.ts +++ b/web/cypress/support/commands/image-patch-commands.ts @@ -142,7 +142,7 @@ export const imagePatchUtils = { { failOnNonZeroExit: false }, ).then((deployResult) => { const deployName = deployResult.stdout.replace(/'/g, '').trim(); - if (deployName) { + if (deployName && /^[a-z0-9][a-z0-9.-]*$/.test(deployName)) { cy.log(`Patching deployment/${deployName} image directly`); cy.exec( `oc patch deployment ${deployName} -n ${MCP.namespace} --type=json ` + From 0146fd2d4b77ef5ac10ecfbd8d18d1a333f1dafe Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Sat, 1 Aug 2026 00:51:14 -0300 Subject: [PATCH 4/9] e2e-coo --- web/cypress/e2e/coo/01.coo_ivt.cy.ts | 2 +- web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts | 2 ++ web/cypress/support/monitoring/01.reg_alerts.cy.ts | 4 +++- web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts | 6 +++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/web/cypress/e2e/coo/01.coo_ivt.cy.ts b/web/cypress/e2e/coo/01.coo_ivt.cy.ts index 810791065..48f5d48c8 100644 --- a/web/cypress/e2e/coo/01.coo_ivt.cy.ts +++ b/web/cypress/e2e/coo/01.coo_ivt.cy.ts @@ -23,7 +23,7 @@ describe('IVT: Monitoring UIPlugin + Virtualization', { tags: ['@smoke', '@coo'] it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); troubleshootingPanelPage.signalCorrelationShouldNotBeVisible(); cy.switchPerspective('Core platform', 'Administrator'); diff --git a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts index 530193ef8..9764b2317 100644 --- a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts +++ b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts @@ -7,6 +7,7 @@ import { acmAlertingPage } from '../../views/acm-alerting-page'; import { troubleshootingPanelPage } from 'cypress/views/troubleshooting-panel'; import { incidentsPage } from 'cypress/views/incidents-page'; import { runAllRegressionFleetManagementAlertsTests } from 'cypress/support/monitoring/01.reg_alerts.cy'; +import { listPage } from 'cypress/views/list-page'; const MCP = { namespace: Cypress.env('COO_NAMESPACE'), @@ -45,6 +46,7 @@ describe('ACM Alerting UI', { tags: ['@coo', '@alerts'] }, () => { nav.sidenav.clickNavLink(['Observe', 'Alerting']); // confirm Alerting page loading completed acmAlertingPage.shouldBeLoaded(); + listPage.filter.clearAllFilters(); // check test alerts exist expectedAlerts.forEach((alert) => { cy.contains('a[data-test-id="alert-resource-link"]', alert, { timeout: 120000 }).should( diff --git a/web/cypress/support/monitoring/01.reg_alerts.cy.ts b/web/cypress/support/monitoring/01.reg_alerts.cy.ts index 607590e07..8800707ed 100644 --- a/web/cypress/support/monitoring/01.reg_alerts.cy.ts +++ b/web/cypress/support/monitoring/01.reg_alerts.cy.ts @@ -397,7 +397,9 @@ export function testAlertsRegression(perspective: PerspectiveConfig) { cy.log('5.5 Alerting rule details > Assert Kebab'); nav.sidenav.clickNavLink(['Observe', 'Alerting']); nav.tabs.switchTab('Alerting rules'); - listPage.filter.clearAllFilters(); + if (perspective.name !== 'Fleet management') { + listPage.filter.clearAllFilters(); + } listPage.filter.byName(`${alertName}`, FilterOUIAIDs.RuleNameFilter); alertingRuleListPage.clickAlertingRule(`${alertName}`); alertingRuleDetailsPage.assertNoKebab(); diff --git a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts index d9487232d..cb91e787e 100644 --- a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts +++ b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts @@ -347,7 +347,11 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { listPersesDashboardsPage.deleteDashboardDeleteButton(); listPersesDashboardsPage.emptyState(); listPersesDashboardsPage.countDashboards('0'); - listPersesDashboardsPage.clearAllFilters(); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); cy.log(`6.15. Filter by Name`); listPersesDashboardsPage.filter.byName(dashboardName); From 6e5b25d1ea94eb21598254eb2f363c2c32713132 Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Sat, 1 Aug 2026 21:49:28 -0300 Subject: [PATCH 5/9] uncaught exception --- web/cypress/support/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/cypress/support/index.ts b/web/cypress/support/index.ts index a59b4cec5..433a46c36 100644 --- a/web/cypress/support/index.ts +++ b/web/cypress/support/index.ts @@ -34,7 +34,9 @@ Cypress.on('uncaught:exception', (err) => { message.includes(`(intermediate value) is not a function`) || message.includes(`Cannot read properties of null (reading '0')`) || message.includes(`load_plugin_entry`) || - message.includes(`Cannot access 'y' before initialization`) + message.includes(`Cannot access 'y' before initialization`) || + message.includes(`NotFound`) || + message.includes(`Cannot access 'f' before initialization`) ) { console.warn('Ignored frontend exception:', err.message); return false; From cb4dc9e471c52457bf1ee3011d57b8e5b0518556 Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Sun, 2 Aug 2026 14:55:35 -0300 Subject: [PATCH 6/9] acm alert --- web/cypress/fixtures/coo/acm-alerrule-test.yaml | 4 ++-- web/cypress/fixtures/coo/acm-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/cypress/fixtures/coo/acm-alerrule-test.yaml b/web/cypress/fixtures/coo/acm-alerrule-test.yaml index 68b69775d..57181d56f 100644 --- a/web/cypress/fixtures/coo/acm-alerrule-test.yaml +++ b/web/cypress/fixtures/coo/acm-alerrule-test.yaml @@ -38,6 +38,6 @@ data: expr: | max(cluster:cpu_usage_cores:sum) by (clusterID, cluster, prometheus) > 0 labels: - cluster: "{{ $labels.cluster }}" - prometheus: "{{ $labels.prometheus }}" + cluster: "local-cluster" + prometheus: "prometheus-k8s" severity: critical diff --git a/web/cypress/fixtures/coo/acm-install.sh b/web/cypress/fixtures/coo/acm-install.sh index 55d1e1b8e..176a0fc27 100755 --- a/web/cypress/fixtures/coo/acm-install.sh +++ b/web/cypress/fixtures/coo/acm-install.sh @@ -247,7 +247,7 @@ data: expr: | max(cluster:cpu_usage_cores:sum) by (clusterID, cluster, prometheus) > 0 labels: - cluster: "{{ $labels.cluster }}" - prometheus: "{{ $labels.prometheus }}" + cluster: "local-cluster" + prometheus: "prometheus-k8s" severity: critical EOF From 2777faac3104b474daf14fea228138e4cd0cafb5 Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Mon, 3 Aug 2026 16:04:58 -0300 Subject: [PATCH 7/9] wait --- web/cypress/support/commands/utility-commands.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/cypress/support/commands/utility-commands.ts b/web/cypress/support/commands/utility-commands.ts index 184996e69..f2d3626af 100644 --- a/web/cypress/support/commands/utility-commands.ts +++ b/web/cypress/support/commands/utility-commands.ts @@ -183,6 +183,7 @@ Cypress.Commands.add('podImage', (pod: string, namespace: string) => { cy.get('[data-ouia-component-id="DataViewFilters"]', { timeout: 30000, }).should('have.length.greaterThan', 0); + cy.wait(10000); // Re-check for DataViewFilters after the table has stabilized cy.get('body').then(($body) => { const hasDataViewFilters = $body.find('[data-ouia-component-id="DataViewFilters"]').length > 0; From 8024831f8ef92186e210d7e64ac9953083d69628 Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Thu, 6 Aug 2026 18:31:16 -0300 Subject: [PATCH 8/9] e2e alerts working --- web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts | 3 +- web/cypress/e2e/coo/02.acm_perses.cy.ts | 2 +- .../e2e/virtualization/00.coo_ivt.cy.ts | 4 +- .../virtualization/01.coo_ivt_alerts.cy.ts | 6 +- .../virtualization/02.coo_ivt_metrics_1.cy.ts | 10 +-- .../virtualization/02.coo_ivt_metrics_2.cy.ts | 10 +-- .../03.coo_ivt_legacy_dashboards.cy.ts | 10 +-- .../virtualization/04.coo_ivt_perses.cy.ts | 6 +- .../fixtures/coo/acm-alerrule-test.yaml | 5 +- web/cypress/fixtures/coo/acm-install.sh | 5 +- .../support/commands/operator-commands.ts | 81 +++++++++++++++++++ .../support/commands/utility-commands.ts | 1 + .../commands/virtualization-commands.ts | 27 +++++-- web/cypress/views/operator-hub-page.ts | 1 + web/package.json | 1 + 15 files changed, 136 insertions(+), 36 deletions(-) diff --git a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts index 9764b2317..a4e712a07 100644 --- a/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts +++ b/web/cypress/e2e/coo/02.acm_alerting_ui.cy.ts @@ -24,7 +24,7 @@ const MP = { }; const expectedAlerts = ['Watchdog', 'Watchdog-spoke', 'ClusterCPUHealth-jb']; -describe('ACM Alerting UI', { tags: ['@coo', '@alerts'] }, () => { +describe('ACM Alerting UI', { tags: ['@coo', '@alerts', '@acm'] }, () => { before(() => { cy.beforeBlockACM(MCP, MP); }); @@ -37,6 +37,7 @@ describe('ACM Alerting UI', { tags: ['@coo', '@alerts'] }, () => { nav.tabs.switchTab('Incidents'); nav.sidenav.clickNavLink(['Observe', 'Metrics']); commonPages.titleShouldHaveText('Metrics'); + cy.changeNamespace('All Projects'); // switch to Fleet Management page cy.switchPerspective('Fleet management'); // close pop-up window diff --git a/web/cypress/e2e/coo/02.acm_perses.cy.ts b/web/cypress/e2e/coo/02.acm_perses.cy.ts index ff2136dce..4a919106b 100644 --- a/web/cypress/e2e/coo/02.acm_perses.cy.ts +++ b/web/cypress/e2e/coo/02.acm_perses.cy.ts @@ -18,7 +18,7 @@ const MP = { operatorName: 'Cluster Monitoring Operator', }; -describe('ACM - Perses', { tags: ['@coo', '@perses'] }, () => { +describe('ACM - Perses', { tags: ['@coo', '@perses', '@acm'] }, () => { before(() => { cy.beforeBlockACM(MCP, MP); cy.switchPerspective('Core platform'); diff --git a/web/cypress/e2e/virtualization/00.coo_ivt.cy.ts b/web/cypress/e2e/virtualization/00.coo_ivt.cy.ts index 19df13b3b..30280889f 100644 --- a/web/cypress/e2e/virtualization/00.coo_ivt.cy.ts +++ b/web/cypress/e2e/virtualization/00.coo_ivt.cy.ts @@ -54,7 +54,7 @@ describe('Installation: Virtualization', { tags: ['@virtualization', '@slow'] }, it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }); @@ -64,7 +64,7 @@ describe('IVT: Monitoring + Virtualization', { tags: ['@smoke', '@virtualization cy.visit('/'); guidedTour.close(); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); commonPages.titleShouldHaveText('Metrics'); diff --git a/web/cypress/e2e/virtualization/01.coo_ivt_alerts.cy.ts b/web/cypress/e2e/virtualization/01.coo_ivt_alerts.cy.ts index 10e0dc07c..3e2bb8677 100644 --- a/web/cypress/e2e/virtualization/01.coo_ivt_alerts.cy.ts +++ b/web/cypress/e2e/virtualization/01.coo_ivt_alerts.cy.ts @@ -57,7 +57,7 @@ describe( it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }, @@ -65,12 +65,12 @@ describe( describe( 'Regression: Monitoring - Alerts (Virtualization)', - { tags: ['@virtualization', '@alerts'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); commonPages.titleShouldHaveText('Metrics'); diff --git a/web/cypress/e2e/virtualization/02.coo_ivt_metrics_1.cy.ts b/web/cypress/e2e/virtualization/02.coo_ivt_metrics_1.cy.ts index 54b70b554..ccac18457 100644 --- a/web/cypress/e2e/virtualization/02.coo_ivt_metrics_1.cy.ts +++ b/web/cypress/e2e/virtualization/02.coo_ivt_metrics_1.cy.ts @@ -57,7 +57,7 @@ describe( it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }, @@ -65,12 +65,12 @@ describe( describe( 'Regression: Monitoring - Metrics (Virtualization)', - { tags: ['@virtualization', '@metrics'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); alerts.getWatchdogAlert(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); @@ -87,12 +87,12 @@ describe( describe( 'Regression: Monitoring - Metrics Namespaced (Virtualization)', - { tags: ['@virtualization', '@metrics'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); alerts.getWatchdogAlert(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); diff --git a/web/cypress/e2e/virtualization/02.coo_ivt_metrics_2.cy.ts b/web/cypress/e2e/virtualization/02.coo_ivt_metrics_2.cy.ts index d13a65eb9..b840804fe 100644 --- a/web/cypress/e2e/virtualization/02.coo_ivt_metrics_2.cy.ts +++ b/web/cypress/e2e/virtualization/02.coo_ivt_metrics_2.cy.ts @@ -57,7 +57,7 @@ describe( it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }, @@ -65,12 +65,12 @@ describe( describe( 'Regression: Monitoring - Metrics (Virtualization)', - { tags: ['@virtualization', '@metrics'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); alerts.getWatchdogAlert(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); @@ -87,12 +87,12 @@ describe( describe( 'Regression: Monitoring - Metrics Namespaced (Virtualization)', - { tags: ['@virtualization', '@metrics'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); alerts.getWatchdogAlert(); nav.sidenav.clickNavLink(['Observe', 'Metrics']); diff --git a/web/cypress/e2e/virtualization/03.coo_ivt_legacy_dashboards.cy.ts b/web/cypress/e2e/virtualization/03.coo_ivt_legacy_dashboards.cy.ts index bcbaf9fb2..28fe440d2 100644 --- a/web/cypress/e2e/virtualization/03.coo_ivt_legacy_dashboards.cy.ts +++ b/web/cypress/e2e/virtualization/03.coo_ivt_legacy_dashboards.cy.ts @@ -56,7 +56,7 @@ describe( it('1. Virtualization perspective - Observe Menu', () => { cy.log('Virtualization perspective - Observe Menu and verify all submenus'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }, @@ -64,12 +64,12 @@ describe( describe( 'Regression: Monitoring - Legacy Dashboards (Virtualization)', - { tags: ['@virtualization', '@dashboards'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); nav.sidenav.clickNavLink(['Observe', 'Dashboards']); commonPages.titleShouldHaveText('Dashboards'); @@ -84,12 +84,12 @@ describe( describe( 'Regression: Monitoring - Legacy Dashboards Namespaced (Virtualization)', - { tags: ['@virtualization', '@dashboards'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); nav.sidenav.clickNavLink(['Observe', 'Dashboards']); commonPages.titleShouldHaveText('Dashboards'); diff --git a/web/cypress/e2e/virtualization/04.coo_ivt_perses.cy.ts b/web/cypress/e2e/virtualization/04.coo_ivt_perses.cy.ts index 52db7e115..ce9a43f60 100644 --- a/web/cypress/e2e/virtualization/04.coo_ivt_perses.cy.ts +++ b/web/cypress/e2e/virtualization/04.coo_ivt_perses.cy.ts @@ -54,20 +54,20 @@ describe('Installation: Virtualization', { tags: ['@virtualization', '@slow'] }, it('1. Installation: Virtualization', () => { cy.log('Installation: Virtualization'); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); }); }); describe( 'IVT: COO - Dashboards (Perses) - Virtualization perspective', - { tags: ['@virtualization', '@perses-ivt'] }, + { tags: ['@virtualization', '@slow'] }, () => { beforeEach(() => { cy.visit('/'); guidedTour.close(); cy.validateLogin(); - cy.switchPerspective('Virtualization'); + cy.switchPerspective('Virtualization', 'Fleet virtualization'); guidedTour.closeKubevirtTour(); nav.sidenav.clickNavLink(['Observe', 'Dashboards (Perses)']); commonPages.titleShouldHaveText('Dashboards'); diff --git a/web/cypress/fixtures/coo/acm-alerrule-test.yaml b/web/cypress/fixtures/coo/acm-alerrule-test.yaml index 57181d56f..46c07f271 100644 --- a/web/cypress/fixtures/coo/acm-alerrule-test.yaml +++ b/web/cypress/fixtures/coo/acm-alerrule-test.yaml @@ -35,8 +35,9 @@ data: annotations: summary: Notify when CPU utilization on a cluster is greater than the defined utilization limit description: "The cluster has a high CPU usage: core for" - expr: | - max(cluster:cpu_usage_cores:sum) by (clusterID, cluster, prometheus) > 0 + # vector(1) so the alert always fires for UI e2e; the real CPU expr depends on + # metrics-collector scrape lag and is too flaky for setup waits. + expr: vector(1) labels: cluster: "local-cluster" prometheus: "prometheus-k8s" diff --git a/web/cypress/fixtures/coo/acm-install.sh b/web/cypress/fixtures/coo/acm-install.sh index 176a0fc27..dca69ae06 100755 --- a/web/cypress/fixtures/coo/acm-install.sh +++ b/web/cypress/fixtures/coo/acm-install.sh @@ -244,8 +244,9 @@ data: annotations: summary: Notify when CPU utilization on a cluster is greater than the defined utilization limit description: "The cluster has a high CPU usage: core for" - expr: | - max(cluster:cpu_usage_cores:sum) by (clusterID, cluster, prometheus) > 0 + # vector(1) so the alert always fires for UI e2e; the real CPU expr depends on + # metrics-collector scrape lag and is too flaky for setup waits. + expr: vector(1) labels: cluster: "local-cluster" prometheus: "prometheus-k8s" diff --git a/web/cypress/support/commands/operator-commands.ts b/web/cypress/support/commands/operator-commands.ts index c81a858b6..4ff84759d 100644 --- a/web/cypress/support/commands/operator-commands.ts +++ b/web/cypress/support/commands/operator-commands.ts @@ -44,11 +44,16 @@ declare global { MCP: { namespace: string; operatorName: string; packageName: string }, MP: { namespace: string; operatorName: string }, ): Chainable; + waitForAcmAlertsFiring(alertNames?: string[]): Chainable; closeOnboardingModalIfPresent(): Chainable; } } } +const ACM_OBSERVABILITY_NS = 'open-cluster-management-observability'; +const ACM_DEFAULT_TEST_ALERTS = ['Watchdog', 'Watchdog-spoke', 'ClusterCPUHealth-jb']; +const acmAlertReadyTimeoutMilliseconds = 600000; + const useSession = String(Cypress.env('SESSION')).toLowerCase() === 'true'; // ── Helpers used only by the orchestration commands ──────────────── @@ -74,6 +79,77 @@ function collectDebugInfo(MP: { namespace: string }, MCP?: { namespace: string } } } +/** + * Wait until ACM custom alert rules are evaluated and present in Alertmanager. + * oc apply of thanos-ruler-custom-rules only updates the ConfigMap; Thanos Ruler + * must reload/evaluate rules and push alerts to Alertmanager before the UI can show them. + */ +function waitForAcmAlertsFiring(alertNames: string[] = ACM_DEFAULT_TEST_ALERTS): void { + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + const ns = ACM_OBSERVABILITY_NS; + + cy.log(`Waiting for ACM alerts to become firing: ${alertNames.join(', ')}`); + + cy.log('Waiting for observability-thanos-rule pods to be Ready'); + cy.exec( + `oc rollout status statefulset/observability-thanos-rule ` + + `-n ${ns} --timeout=300s --kubeconfig "${kubeconfig}"`, + { failOnNonZeroExit: false, timeout: acmAlertReadyTimeoutMilliseconds }, + ).then((result) => { + if (result.code !== 0) { + cy.log( + `thanos-rule rollout status not ready yet (${result.stderr || result.stdout}); ` + + 'continuing to poll Alertmanager', + ); + } + }); + + cy.log('Waiting for ACM Alertmanager pods to be Ready'); + cy.exec( + `oc wait --for=condition=Ready pod ` + + `-l alertmanager=observability,app=multicluster-observability-alertmanager ` + + `-n ${ns} --timeout=300s --kubeconfig "${kubeconfig}"`, + { failOnNonZeroExit: false, timeout: acmAlertReadyTimeoutMilliseconds }, + ); + + cy.waitUntil( + () => + cy + .exec( + `POD=$(oc get pods -n ${ns} -l alertmanager=observability ` + + `--field-selector=status.phase=Running ` + + `-o jsonpath='{.items[0].metadata.name}' --kubeconfig "${kubeconfig}") && ` + + `test -n "$POD" && ` + + `(oc exec -n ${ns} "$POD" -c alertmanager --kubeconfig "${kubeconfig}" -- ` + + `amtool alert query --alertmanager.url=http://127.0.0.1:9093 || ` + + `oc exec -n ${ns} "$POD" -c alertmanager --kubeconfig "${kubeconfig}" -- ` + + `wget -qO- http://127.0.0.1:9093/api/v2/alerts)`, + { failOnNonZeroExit: false }, + ) + .then((result) => { + if (result.code !== 0 || !result.stdout) { + return false; + } + const missing = alertNames.filter((name) => !result.stdout.includes(name)); + if (missing.length > 0) { + // eslint-disable-next-line no-console + console.log(`ACM alerts still missing from Alertmanager: ${missing.join(', ')}`); + return false; + } + // eslint-disable-next-line no-console + console.log(`All expected ACM alerts found in Alertmanager: ${alertNames.join(', ')}`); + return true; + }), + { + timeout: acmAlertReadyTimeoutMilliseconds, + interval: 15000, + errorMsg: + `Timed out waiting for ACM alerts to fire in Alertmanager: ${alertNames.join(', ')}. ` + + 'ConfigMap may be applied but Thanos Ruler has not evaluated/pushed the alerts yet.', + }, + ); +} + function cleanupUIPlugin( MCP: { namespace: string; config?: { kind: string; name: string } }, opts: Required, @@ -259,6 +335,10 @@ Cypress.Commands.add('RemoveClusterAdminRole', () => { cy.log('Remove cluster-admin role from user completed'); }); +Cypress.Commands.add('waitForAcmAlertsFiring', (alertNames?: string[]) => { + waitForAcmAlertsFiring(alertNames); +}); + Cypress.Commands.add('beforeBlockACM', (MCP, MP) => { cy.beforeBlockCOO(MCP, MP); cy.log('=== [Setup] Installing ACM test resources ==='); @@ -277,6 +357,7 @@ Cypress.Commands.add('beforeBlockACM', (MCP, MP) => { 'KUBECONFIG_PATH', )}`, ); + cy.waitForAcmAlertsFiring(ACM_DEFAULT_TEST_ALERTS); cy.log('ACM environment setup completed'); }); diff --git a/web/cypress/support/commands/utility-commands.ts b/web/cypress/support/commands/utility-commands.ts index f2d3626af..20eadb699 100644 --- a/web/cypress/support/commands/utility-commands.ts +++ b/web/cypress/support/commands/utility-commands.ts @@ -136,6 +136,7 @@ Cypress.Commands.add('aboutModal', () => { if (Cypress.env('LOGIN_USERNAME') === 'kubeadmin') { cy.visit('/'); cy.wait(10000); + cy.switchPerspective('Core platform'); cy.waitUntil(() => cy.byTestID('Operators', { timeout: 10000 }).should('be.visible')); cy.byTestID(DataTestIDs.MastHeadHelpIcon).should('be.visible'); cy.byTestID(DataTestIDs.MastHeadHelpIcon).should('be.visible').click({ force: true }); diff --git a/web/cypress/support/commands/virtualization-commands.ts b/web/cypress/support/commands/virtualization-commands.ts index 886517e8b..907463eff 100644 --- a/web/cypress/support/commands/virtualization-commands.ts +++ b/web/cypress/support/commands/virtualization-commands.ts @@ -224,11 +224,23 @@ const virtualizationUtils = { ); cy.log('Remove Openshift Virtualization namespace'); - cy.executeAndDelete( - `oc delete namespace ${KBV.namespace} --ignore-not-found --kubeconfig ${Cypress.env( - 'KUBECONFIG_PATH', - )}`, - ); + const kubeconfig = Cypress.env('KUBECONFIG_PATH'); + cy.exec( + `oc delete namespace ${KBV.namespace} --ignore-not-found ` + + `--timeout=60s --kubeconfig ${kubeconfig}`, + { failOnNonZeroExit: false, timeout: 90000 }, + ).then((result) => { + if (result.code !== 0 && result.stderr?.includes('timed out')) { + cy.log('Namespace stuck in Terminating, removing finalizers'); + cy.exec( + `oc get namespace ${KBV.namespace} -o json --kubeconfig ${kubeconfig}` + + ` | jq '.spec.finalizers = []'` + + ` | oc replace --raw "/api/v1/namespaces/${KBV.namespace}/finalize" -f - ` + + `--kubeconfig ${kubeconfig}`, + { failOnNonZeroExit: false }, + ); + } + }); cy.log('Delete Hyperconverged CRD instance.'); cy.executeAndDelete( @@ -238,10 +250,11 @@ const virtualizationUtils = { ); cy.log('Delete Kubevirt instance.'); - cy.executeAndDelete( + cy.exec( `oc delete crd ` + `-l operators.coreos.com/kubevirt-hyperconverged.openshift-cnv ` + - `--ignore-not-found --kubeconfig "${Cypress.env('KUBECONFIG_PATH')}"`, + `--ignore-not-found --timeout=120s --kubeconfig "${kubeconfig}"`, + { failOnNonZeroExit: false, timeout: 150000 }, ); } }, diff --git a/web/cypress/views/operator-hub-page.ts b/web/cypress/views/operator-hub-page.ts index 50fc92462..584f07656 100644 --- a/web/cypress/views/operator-hub-page.ts +++ b/web/cypress/views/operator-hub-page.ts @@ -2,6 +2,7 @@ import * as helperfuncs from '../views/utils'; export const operatorHubPage = { installOperator: (operatorName, csName, installNamespace?) => { + cy.switchPerspective('Core platform', 'Administrator'); cy.visit( `/operatorhub/subscribe?pkg=${operatorName}&catalog=${csName}` + `&catalogNamespace=openshift-marketplace&targetNamespace=undefined`, diff --git a/web/package.json b/web/package.json index 93fa55c19..cdc332147 100644 --- a/web/package.json +++ b/web/package.json @@ -47,6 +47,7 @@ "test-cypress-perses-dev": "node --max-old-space-size=4096 ./node_modules/.bin/cypress run --browser chrome --headless --env grepTags='@perses-dev --@demo --@xfail'", "test-cypress-perses": "node --max-old-space-size=4096 ./node_modules/.bin/cypress run --browser chrome --headless --env grepTags='@perses --@smoke --@flaky --@demo --@xfail'", "test-cypress-perses-ivt": "node --max-old-space-size=4096 ./node_modules/.bin/cypress run --browser chrome --headless --env grepTags='@perses-ivt --@smoke --@flaky --@demo --@xfail'", + "test-cypress-acm": "node --max-old-space-size=4096 ./node_modules/.bin/cypress run --browser chrome --headless --env grepTags='@acm --@smoke --@flaky --@demo --@xfail'", "ts-node": "ts-node -O '{\"module\":\"commonjs\"}'" }, "dependencies": { From c1cdae369f4220494f68a204527672fdf8518c9b Mon Sep 17 00:00:00 2001 From: Evelyn Tanigawa Murasaki Date: Fri, 7 Aug 2026 18:46:12 -0300 Subject: [PATCH 9/9] e2e coo --- web/cypress/fixtures/coo/acm-uninstall.sh | 159 ++++++++++++++---- .../virtualization/uninstall-kubevirt.sh | 127 ++++++++++++++ .../support/commands/dashboards-commands.ts | 3 +- .../support/commands/utility-commands.ts | 30 +++- .../perses/00.coo_bvt_perses_admin.cy.ts | 12 +- web/cypress/views/nav.ts | 55 ++++-- .../perses-dashboards-list-dashboards.ts | 6 +- web/cypress/views/tour.ts | 86 ++++++---- 8 files changed, 390 insertions(+), 88 deletions(-) create mode 100755 web/cypress/fixtures/virtualization/uninstall-kubevirt.sh diff --git a/web/cypress/fixtures/coo/acm-uninstall.sh b/web/cypress/fixtures/coo/acm-uninstall.sh index 6193c3486..558917b8e 100755 --- a/web/cypress/fixtures/coo/acm-uninstall.sh +++ b/web/cypress/fixtures/coo/acm-uninstall.sh @@ -1,45 +1,146 @@ #!/bin/bash -# this scrpit use to uninstall ACM test resources and do not excute automatlly with test -set -eux -echo "[ACM Uninstall] Using KUBECONFIG=${KUBECONFIG:-~/.kube/config}" +# Uninstall ACM test resources. +# +# Usage: +# ./acm-uninstall.sh [--kubeconfig ] +# +# Defaults: +# kubeconfig: $KUBECONFIG (or ~/.kube/config) +# +# Self-heals against a common failure mode: leftover "ServiceNotFound" APIServices +# (e.g. left behind by uninstall-kubevirt.sh) break cluster-wide API discovery, +# which silently stalls namespace/CR termination for *any* resource, including +# MultiClusterHub/MultiClusterEngine here. See cleanup_broken_apiservices(). +# +# Every delete below is non-blocking (--wait=false) followed by a bounded wait, +# so this script can never hang indefinitely the way a plain `oc delete` can. +set -uo pipefail +set -x -# --- Step 1: delete MultiClusterObservability --- +KUBECONFIG_PATH="${KUBECONFIG:-${HOME}/.kube/config}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --kubeconfig) KUBECONFIG_PATH="$2"; shift 2 ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +echo "[ACM Uninstall] Using KUBECONFIG=${KUBECONFIG_PATH}" + +run() { + echo "+ $*" + if ! "$@" --kubeconfig "${KUBECONFIG_PATH}"; then + echo " (command failed, continuing)" >&2 + fi +} + +cleanup_broken_apiservices() { + echo "[ACM Uninstall] Checking for broken APIServices (stale discovery entries)..." + local broken + broken=$(oc get apiservices -o json --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null \ + | jq -r '.items[] | select(.status.conditions[]? | .status=="False" and .reason=="ServiceNotFound") | .metadata.name') + if [[ -n "${broken}" ]]; then + echo "[ACM Uninstall] Found broken APIServices pointing at missing services (these can stall unrelated namespace/CR deletions):" + echo "${broken}" + while IFS= read -r name; do + [[ -z "${name}" ]] && continue + run oc delete apiservice "${name}" --ignore-not-found + done <<< "${broken}" + else + echo "[ACM Uninstall] No broken APIServices found." + fi +} + +# Usage: wait_or_force_delete [-n ] +wait_or_force_delete() { + local kind="$1" name="$2" + shift 2 + local ns_args=() + if [[ "${1:-}" == "-n" ]]; then + ns_args=("-n" "$2") + shift 2 + fi + local timeout="${1:-180}" + + if oc wait "${kind}" "${name}" ${ns_args[@]+"${ns_args[@]}"} --for=delete --timeout="${timeout}s" --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null; then + return 0 + fi + + echo "[ACM Uninstall] ${kind}/${name} still present after ${timeout}s; attempting self-heal..." + cleanup_broken_apiservices + + if oc wait "${kind}" "${name}" ${ns_args[@]+"${ns_args[@]}"} --for=delete --timeout=60s --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null; then + return 0 + fi + + echo "[ACM Uninstall] ${kind}/${name} still stuck; forcibly clearing finalizers..." + oc get "${kind}" "${name}" ${ns_args[@]+"${ns_args[@]}"} -o json --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null \ + | jq '.metadata.finalizers = []' \ + | oc replace -f - --kubeconfig "${KUBECONFIG_PATH}" || true +} + +force_delete_namespace() { + local ns="$1" + echo "[ACM Uninstall] Namespace ${ns} stuck in Terminating, removing finalizers..." + oc get namespace "${ns}" -o json --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null \ + | jq '.spec.finalizers = []' \ + | oc replace --raw "/api/v1/namespaces/${ns}/finalize" -f - --kubeconfig "${KUBECONFIG_PATH}" || true +} + +delete_namespace() { + local ns="$1" timeout="${2:-120}" + echo "[ACM Uninstall] Deleting namespace ${ns}..." + if ! oc delete ns "${ns}" --ignore-not-found=true --timeout="${timeout}s" --kubeconfig "${KUBECONFIG_PATH}" 2>&1; then + cleanup_broken_apiservices + force_delete_namespace "${ns}" + fi +} + +# --- Step 0: pre-flight self-heal --- +# Do this first so leftovers from other cleanup scripts (e.g. uninstall-kubevirt.sh) +# don't stall the deletions below. +cleanup_broken_apiservices + +# --- Step 1: delete MultiClusterObservability (MCO) --- echo "[ACM Uninstall] Deleting MultiClusterObservability (MCO)..." -oc delete MultiClusterObservability observability -n open-cluster-management-observability --ignore-not-found=true +run oc delete MultiClusterObservability observability -n open-cluster-management-observability --ignore-not-found=true --wait=false +wait_or_force_delete MultiClusterObservability observability -n open-cluster-management-observability 180 -# --- Step 2: delete MinIO、PVC、Secret、Service --- +# --- Step 2: delete MinIO, PVC, Secret, Service --- echo "[ACM Uninstall] Cleaning up MinIO and related resources..." -oc delete deploy minio -n open-cluster-management-observability --ignore-not-found=true -oc delete pvc minio -n open-cluster-management-observability --ignore-not-found=true -oc delete secret thanos-object-storage -n open-cluster-management-observability --ignore-not-found=true -oc delete svc minio -n open-cluster-management-observability --ignore-not-found=true +run oc delete deploy minio -n open-cluster-management-observability --ignore-not-found=true +run oc delete pvc minio -n open-cluster-management-observability --ignore-not-found=true +run oc delete secret thanos-object-storage -n open-cluster-management-observability --ignore-not-found=true +run oc delete svc minio -n open-cluster-management-observability --ignore-not-found=true -# --- Step 3: delete MultiClusterHub --- +# --- Step 3: delete MultiClusterHub, and its MultiClusterEngine dependency --- echo "[ACM Uninstall] Deleting MultiClusterHub..." -oc delete MultiClusterHub multiclusterhub -n open-cluster-management --ignore-not-found=true +run oc delete MultiClusterHub multiclusterhub -n open-cluster-management --ignore-not-found=true --wait=false +wait_or_force_delete MultiClusterHub multiclusterhub -n open-cluster-management 300 -# wait for MultiClusterHub deleted -echo "[ACM Uninstall] Waiting for MultiClusterHub cleanup..." -oc wait MultiClusterHub multiclusterhub -n open-cluster-management --for=delete --timeout=300s || true +echo "[ACM Uninstall] Ensuring MultiClusterEngine is cleaned up..." +run oc delete MultiClusterEngine multiclusterengine --ignore-not-found=true --wait=false +wait_or_force_delete MultiClusterEngine multiclusterengine 180 # --- Step 4: delete Subscription and OperatorGroup --- echo "[ACM Uninstall] Deleting ACM Operator Subscription & OperatorGroup..." -oc delete sub advanced-cluster-management -n open-cluster-management --ignore-not-found=true -oc delete og og-global -n open-cluster-management --ignore-not-found=true +run oc delete sub advanced-cluster-management -n open-cluster-management --ignore-not-found=true +run oc delete og og-global -n open-cluster-management --ignore-not-found=true -# --- Step 5: delete namespace --- +# --- Step 5: delete namespaces --- echo "[ACM Uninstall] Deleting ACM-related namespaces..." -oc delete ns open-cluster-management-observability --ignore-not-found=true -oc delete ns open-cluster-management --ignore-not-found=true +delete_namespace open-cluster-management-observability 120 +delete_namespace open-cluster-management-hub 120 +delete_namespace open-cluster-management 120 -# # --- Step 6: clean up CRDs(optional)--- -# echo "[ACM Uninstall] Cleaning up CRDs (optional cleanup)..." -# oc delete crd multiclusterhubs.operator.open-cluster-management.io --ignore-not-found=true -# oc delete crd multiclusterobservabilities.observability.open-cluster-management.io --ignore-not-found=true +# --- Step 6: clean up orphaned APIServices left behind by ACM --- +echo "[ACM Uninstall] Re-checking for broken APIServices after namespace deletion..." +cleanup_broken_apiservices -# --- Step 7: Pod / Finalizer --- -# echo "[ACM Uninstall] Removing potential finalizers..." -# oc get ns open-cluster-management-observability -o json | jq '.spec.finalizers=[]' | oc replace --raw "/api/v1/namespaces/open-cluster-management-observability/finalize" -f - || true -# oc get ns open-cluster-management -o json | jq '.spec.finalizers=[]' | oc replace --raw "/api/v1/namespaces/open-cluster-management/finalize" -f - || true +# # --- Step 7: clean up CRDs (optional) --- +# echo "[ACM Uninstall] Cleaning up CRDs (optional cleanup)..." +# oc delete crd multiclusterhubs.operator.open-cluster-management.io --ignore-not-found=true --kubeconfig "${KUBECONFIG_PATH}" +# oc delete crd multiclusterobservabilities.observability.open-cluster-management.io --ignore-not-found=true --kubeconfig "${KUBECONFIG_PATH}" -echo "[ACM Uninstall] ✅ Completed cleanup." +echo "[ACM Uninstall] Completed cleanup." diff --git a/web/cypress/fixtures/virtualization/uninstall-kubevirt.sh b/web/cypress/fixtures/virtualization/uninstall-kubevirt.sh new file mode 100755 index 000000000..c12584c9a --- /dev/null +++ b/web/cypress/fixtures/virtualization/uninstall-kubevirt.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# Uninstall OpenShift Virtualization (KubeVirt) from a cluster. +# Mirrors the cleanup logic in virtualization-commands.ts. +# +# Usage: +# ./uninstall-kubevirt.sh [--namespace ] [--kubeconfig ] +# +# Defaults: +# namespace: openshift-cnv +# kubeconfig: $KUBECONFIG (or ~/.kube/config) + +set -euo pipefail + +NAMESPACE="${NAMESPACE:-openshift-cnv}" +KUBECONFIG_PATH="${KUBECONFIG:-${HOME}/.kube/config}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --namespace) NAMESPACE="$2"; shift 2 ;; + --kubeconfig) KUBECONFIG_PATH="$2"; shift 2 ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +run() { + echo "+ $*" + if ! "$@"; then + echo " (command failed, continuing)" >&2 + fi +} + +# Deleting the namespace/CRDs below removes the backing services for any +# aggregated APIServices (e.g. virt-api, cdi-api), but OLM does not always +# clean up the APIService objects themselves. Leftover APIServices in +# "ServiceNotFound" state break cluster-wide API discovery, which can +# silently stall unrelated namespace/CR deletions elsewhere in the cluster +# (e.g. acm-uninstall.sh). Detect and remove them so this script doesn't +# leave landmines for other cleanup jobs. +cleanup_broken_apiservices() { + echo "--- Checking for broken APIServices (stale discovery entries) ---" + local broken + broken=$(oc get apiservices -o json --kubeconfig "${KUBECONFIG_PATH}" 2>/dev/null \ + | jq -r '.items[] | select(.status.conditions[]? | .status=="False" and .reason=="ServiceNotFound") | .metadata.name') + if [[ -n "${broken}" ]]; then + echo "Found broken APIServices pointing at missing services:" + echo "${broken}" + while IFS= read -r name; do + [[ -z "${name}" ]] && continue + run oc delete apiservice "${name}" --ignore-not-found --kubeconfig "${KUBECONFIG_PATH}" + done <<< "${broken}" + else + echo "No broken APIServices found." + fi +} + +echo "=== Uninstalling OpenShift Virtualization from namespace ${NAMESPACE} ===" + +echo "" +echo "--- Removing finalizers from HyperConverged ---" +run oc patch hyperconverged.hco.kubevirt.io/kubevirt-hyperconverged \ + -n "${NAMESPACE}" \ + -p '{"metadata":{"finalizers":[]}}' \ + --type=merge \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Removing finalizers from KubeVirt ---" +run oc patch kubevirt.kubevirt.io/kubevirt \ + -n "${NAMESPACE}" \ + --type=merge \ + -p '{"metadata":{"finalizers":[]}}' \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Deleting HyperConverged instance ---" +run oc delete HyperConverged kubevirt-hyperconverged \ + -n "${NAMESPACE}" \ + --ignore-not-found \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Removing subscription ---" +run oc delete subscription kubevirt-hyperconverged \ + -n "${NAMESPACE}" \ + --ignore-not-found \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Removing CSV ---" +run oc delete csv \ + -n "${NAMESPACE}" \ + -l operators.coreos.com/kubevirt-hyperconverged.openshift-cnv \ + --ignore-not-found \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Deleting namespace ${NAMESPACE} ---" +if ! oc delete namespace "${NAMESPACE}" \ + --ignore-not-found \ + --timeout=60s \ + --kubeconfig "${KUBECONFIG_PATH}" 2>&1; then + + echo "Namespace stuck in Terminating, removing finalizers..." + oc get namespace "${NAMESPACE}" -o json --kubeconfig "${KUBECONFIG_PATH}" \ + | jq '.spec.finalizers = []' \ + | oc replace --raw "/api/v1/namespaces/${NAMESPACE}/finalize" -f - \ + --kubeconfig "${KUBECONFIG_PATH}" || true +fi + +echo "" +echo "--- Cleaning up orphaned APIServices left behind by ${NAMESPACE} ---" +cleanup_broken_apiservices + +echo "" +echo "--- Deleting CRDs ---" +run oc delete crd \ + -l operators.coreos.com/kubevirt-hyperconverged.openshift-cnv \ + --ignore-not-found \ + --timeout=120s \ + --kubeconfig "${KUBECONFIG_PATH}" + +echo "" +echo "--- Re-checking for broken APIServices after CRD deletion ---" +cleanup_broken_apiservices + +echo "" +echo "=== OpenShift Virtualization uninstall complete ===" diff --git a/web/cypress/support/commands/dashboards-commands.ts b/web/cypress/support/commands/dashboards-commands.ts index 82fa0d444..d7bb7ab91 100644 --- a/web/cypress/support/commands/dashboards-commands.ts +++ b/web/cypress/support/commands/dashboards-commands.ts @@ -116,6 +116,7 @@ export const dashboardsUtils = { cy.checkForAlertRecursively(); cy.dynamicPluginWorkConsoleAround(); cy.reload(true); + cy.closeOnboardingModalIfPresent(); // Dynamic plugins may take time to register after reload. // Retry by closing/re-opening the launcher until the item appears. @@ -127,7 +128,7 @@ export const dashboardsUtils = { .click() .then(() => cy - .get(`[data-test="${DataTestIDs.MastHeadApplicationItem}"]`, { timeout: 5000 }) + .get(`[data-test="${DataTestIDs.MastHeadApplicationItem}"]`, { timeout: 10000 }) .then(($items) => $items.filter(':contains("Signal correlation")').length > 0) .then((found) => { if (!found) { diff --git a/web/cypress/support/commands/utility-commands.ts b/web/cypress/support/commands/utility-commands.ts index 20eadb699..adc5069a5 100644 --- a/web/cypress/support/commands/utility-commands.ts +++ b/web/cypress/support/commands/utility-commands.ts @@ -131,6 +131,29 @@ Cypress.Commands.add('changeNamespace', (namespace: string) => { cy.log('Namespace changed to: ' + namespace); }); +/** + * Clicks an element, retrying if it detaches from the DOM mid-click. + * + * Right after installing/patching operators, the console masthead/nav can + * re-render while dynamic plugins finish registering. Cypress may locate the + * element and then have it disappear before the click completes, producing + * "...but while waiting for them to become actionable, they disappeared from + * the page". `{ force: true }` skips visibility/coverage checks but can't + * protect against this race, so retry instead of failing outright. + */ +function clickResilient(getElement: () => Cypress.Chainable, attemptsLeft = 3): void { + cy.once('fail', (err) => { + if (attemptsLeft > 0 && /disappeared from the page/.test(err.message)) { + cy.log(`Click target detached before click completed, retrying (${attemptsLeft} left)...`); + cy.wait(1000); + clickResilient(getElement, attemptsLeft - 1); + } else { + throw err; + } + }); + getElement().click({ force: true }); +} + Cypress.Commands.add('aboutModal', () => { cy.log('Getting OCP version'); if (Cypress.env('LOGIN_USERNAME') === 'kubeadmin') { @@ -138,10 +161,11 @@ Cypress.Commands.add('aboutModal', () => { cy.wait(10000); cy.switchPerspective('Core platform'); cy.waitUntil(() => cy.byTestID('Operators', { timeout: 10000 }).should('be.visible')); - cy.byTestID(DataTestIDs.MastHeadHelpIcon).should('be.visible'); - cy.byTestID(DataTestIDs.MastHeadHelpIcon).should('be.visible').click({ force: true }); + clickResilient(() => cy.byTestID(DataTestIDs.MastHeadHelpIcon).should('be.visible')); cy.wait(3000); - cy.byTestID(DataTestIDs.MastHeadApplicationItem).contains('About').should('be.visible').click(); + clickResilient(() => + cy.byTestID(DataTestIDs.MastHeadApplicationItem).contains('About').should('be.visible'), + ); cy.byAriaLabel('About modal') .find('div[class*="co-select-to-copy"]') .eq(0) diff --git a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts index cb91e787e..1f65659d5 100644 --- a/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts +++ b/web/cypress/support/perses/00.coo_bvt_perses_admin.cy.ts @@ -186,13 +186,21 @@ export function testBVTCOOPerses1(perspective: PerspectiveConfig) { listPersesDashboardsPage.countDashboards('0'); cy.log(`5.7. Search for the renamed dashboard`); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); cy.changeNamespace('All Projects'); - listPersesDashboardsPage.clearAllFilters(); listPersesDashboardsPage.filter.byName( persesDashboardsDashboardDropdownPersesDev.PERSES_DASHBOARD_SAMPLE[0] + ' - Renamed', ); listPersesDashboardsPage.countDashboards('0'); - listPersesDashboardsPage.clearAllFilters(); + nav.sidenav.clickNavLink(['Observe', 'Alerting']); + nav.sidenav.clickNavLink([ + 'Observe', + perspective.dashboardsPageName ?? 'Dashboards (Perses)', + ]); }, ); diff --git a/web/cypress/views/nav.ts b/web/cypress/views/nav.ts index dff5c7af9..acbe5ae3d 100644 --- a/web/cypress/views/nav.ts +++ b/web/cypress/views/nav.ts @@ -9,25 +9,44 @@ export const nav = { switcher: { changePerspectiveTo: (...perspectives: string[]) => { cy.log('changePerspectiveTo - ' + perspectives.join(', ')); - cy.get('body').then((body) => { - if (body.find('button[data-test-id="perspective-switcher-toggle"]:visible').length > 0) { - cy.byLegacyTestID('perspective-switcher-toggle') - .scrollIntoView() - .click({ force: true }); - cy.get('[data-test-id="perspective-switcher-menu-option"]').then(($options) => { - const foundPerspective = perspectives.find((p) => $options.text().includes(p)); - if (foundPerspective) { - cy.byLegacyTestID('perspective-switcher-menu-option') - .contains(foundPerspective) - .click({ force: true }); - } else { - cy.log('No matching perspective found'); - cy.get('body').type('{esc}'); - } - }); - } - }); + const toggleSelector = 'button[data-test-id="perspective-switcher-toggle"]:visible'; + const pollIntervalMs = 250; + const timeoutMs = 5000; + const deadline = Date.now() + timeoutMs; + + const openSwitcherAndSelect = () => { + cy.byLegacyTestID('perspective-switcher-toggle').scrollIntoView().click({ force: true }); + + cy.get('[data-test-id="perspective-switcher-menu-option"]').then(($options) => { + const foundPerspective = perspectives.find((p) => $options.text().includes(p)); + if (foundPerspective) { + cy.byLegacyTestID('perspective-switcher-menu-option') + .contains(foundPerspective) + .click({ force: true }); + } else { + cy.log('No matching perspective found'); + cy.get('body').type('{esc}'); + } + }); + }; + + // The toggle is not always present, and when it is, it can take a moment to + // render, so poll for it briefly instead of failing when it never shows up. + const waitForToggleThenSelect = () => { + cy.get('body').then(($body) => { + if ($body.find(toggleSelector).length > 0) { + openSwitcherAndSelect(); + } else if (Date.now() < deadline) { + cy.wait(pollIntervalMs); + waitForToggleThenSelect(); + } else { + cy.log('Perspective switcher toggle not shown - skipping perspective change'); + } + }); + }; + + waitForToggleThenSelect(); cy.wait(2000); }, shouldHaveText: (perspective: string) => { diff --git a/web/cypress/views/perses-dashboards-list-dashboards.ts b/web/cypress/views/perses-dashboards-list-dashboards.ts index c1717ca95..02c40af2b 100644 --- a/web/cypress/views/perses-dashboards-list-dashboards.ts +++ b/web/cypress/views/perses-dashboards-list-dashboards.ts @@ -355,7 +355,7 @@ export const listPersesDashboardsPage = { .contains('Delete dashboard') .should('be.visible') .click({ force: true }); - cy.wait(2000); + cy.wait(5000); }, deleteDashboardCancelButton: () => { @@ -365,7 +365,7 @@ export const listPersesDashboardsPage = { .contains('Cancel') .should('be.visible') .click({ force: true }); - cy.wait(2000); + cy.wait(5000); }, deleteDashboardDeleteButton: () => { @@ -375,7 +375,7 @@ export const listPersesDashboardsPage = { .contains('Delete') .should('be.visible') .click({ force: true }); - cy.wait(2000); + cy.wait(5000); }, projectDropdownNotExists: () => { diff --git a/web/cypress/views/tour.ts b/web/cypress/views/tour.ts index 0c582c7db..9403334da 100644 --- a/web/cypress/views/tour.ts +++ b/web/cypress/views/tour.ts @@ -1,44 +1,66 @@ +// A guided-tour modal either renders almost immediately after login/perspective +// switch or never appears at all - this is intentionally short and unrelated to +// installTimeoutMilliseconds (which governs much longer operator-install waits). +const modalCheckTimeoutMilliseconds = 60000; + export const guidedTour = { close: () => { const modalSelector = 'button[data-ouia-component-id="clustersOnboardingModal-ModalBoxCloseButton"]'; cy.log('close guided tour'); - cy.get('body').then(($body) => { - //Core platform modal - if ($body.find(`[data-test="guided-tour-modal"]`).length > 0) { - cy.log('Core platform modal detected, attempting to close...'); - cy.byTestID('tour-step-footer-secondary').contains('Skip tour').click(); - } - //Kubevirt modal - else if ($body.find(`[aria-label="Welcome modal"]`).length > 0) { - cy.log('Kubevirt modal detected, attempting to close...'); - cy.get('[aria-label="Close"]').should('be.visible').click(); - } - //ACM Onboarding modal - else if ($body.find(modalSelector).length > 0) { - cy.log('Onboarding modal detected, attempting to close...'); - cy.get(modalSelector, { timeout: 20000 }) - .should('be.visible') - .should('not.be.disabled') - .click({ force: true }); + const deadline = Date.now() + modalCheckTimeoutMilliseconds; + const checkForModal = () => { + cy.get('body').then(($body) => { + //Core platform modal + if ($body.find(`[data-test="guided-tour-modal"]`).length > 0) { + cy.log('Core platform modal detected, attempting to close...'); + cy.byTestID('tour-step-footer-secondary').contains('Skip tour').click(); + } + //Kubevirt modal + else if ($body.find(`[aria-label="Welcome modal"]`).length > 0) { + cy.log('Kubevirt modal detected, attempting to close...'); + cy.get('[aria-label="Close"]').should('be.visible').click(); + } + //ACM Onboarding modal + else if ($body.find(modalSelector).length > 0) { + cy.log('Onboarding modal detected, attempting to close...'); + cy.get(modalSelector, { timeout: 20000 }) + .should('be.visible') + .should('not.be.disabled') + .click({ force: true }); - cy.get(modalSelector, { timeout: 10000 }) - .should('not.exist') - .then(() => cy.log('Modal successfully closed')); - } - // Prevents navigating away from the page before the tour is closed - cy.wait(2000); - }); + cy.get(modalSelector, { timeout: 10000 }) + .should('not.exist') + .then(() => cy.log('Modal successfully closed')); + } else if (Date.now() < deadline) { + cy.wait(250).then(checkForModal); + } else { + cy.log('No guided tour modal detected, moving on'); + } + }); + }; + checkForModal(); + // Prevents navigating away from the page before the tour is closed + cy.wait(2000); }, closeKubevirtTour: () => { cy.log('close Kubevirt tour'); - cy.get('body').then(($body) => { - if ($body.find(`[aria-label="Welcome modal"]`).length > 0) { - cy.get('[aria-label="Close"]').should('be.visible').click(); - } - // Prevents navigating away from the page before the tour is closed - cy.wait(2000); - }); + const deadline = Date.now() + modalCheckTimeoutMilliseconds; + const checkForModal = () => { + cy.get('body').then(($body) => { + if ($body.find(`[aria-label="Welcome modal"]`).length > 0) { + cy.log('Kubevirt modal detected, attempting to close...'); + cy.get('[aria-label="Close"]').should('be.visible').click(); + } else if (Date.now() < deadline) { + cy.wait(250).then(checkForModal); + } else { + cy.log('Kubevirt modal not detected, moving on'); + } + }); + }; + checkForModal(); + // Prevents navigating away from the page before the tour is closed + cy.wait(2000); }, };