diff --git a/web/cypress/e2e/incidents/regression/01.reg_filtering.cy.ts b/web/cypress/e2e/incidents/regression/01.reg_filtering.cy.ts
new file mode 100644
index 00000000..6b142e04
--- /dev/null
+++ b/web/cypress/e2e/incidents/regression/01.reg_filtering.cy.ts
@@ -0,0 +1,151 @@
+/*
+Regression test for incidents filtering functionality.
+
+This test uses the comprehensive filtering test scenarios to verify
+that filtering works correctly across various incident types and severities.
+Uses elements defined in incidents-page.ts for all interactions.
+
+Verifies: OU-727
+*/
+
+import { incidentsPage } from '../../../views/incidents-page';
+
+const MCP = {
+ namespace: 'openshift-cluster-observability-operator',
+ packageName: 'cluster-observability-operator',
+ operatorName: 'Cluster Observability Operator',
+ config: {
+ kind: 'UIPlugin',
+ name: 'monitoring',
+ },
+};
+
+const MP = {
+ namespace: 'openshift-monitoring',
+ operatorName: 'Cluster Monitoring Operator',
+};
+
+describe('Regression: Incidents Filtering', () => {
+
+ before(() => {
+ cy.beforeBlockCOO(MCP, MP);
+ });
+
+ beforeEach(() => {
+ cy.log('Navigate to Observe → Incidents');
+ incidentsPage.goTo();
+ cy.log('Setting up comprehensive filtering test scenarios');
+ cy.mockIncidentFixture('incident-scenarios/7-comprehensive-filtering-test-scenarios.yaml');
+ });
+
+ it('1. Severity filtering - Critical, Warning, Info', () => {
+ cy.log('1.1 Clear all filters and ensure all incidents are loaded');
+ incidentsPage.clearAllFilters();
+
+ incidentsPage.setDays('1 day');
+ incidentsPage.setDays('7 days');
+
+ incidentsPage.elements.incidentsChartContainer().should('be.visible');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 12);
+ cy.log('All 12 incidents from comprehensive filtering scenarios are loaded');
+
+
+ cy.log('1.2 Active filters: Critical');
+ incidentsPage.toggleFilter('Critical');
+
+ incidentsPage.elements.severityFilterChip().should('be.visible');
+ incidentsPage.elements.incidentsChartContainer().should('be.visible');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 7);
+
+ cy.log('Verified: 7 incidents shown');
+
+
+ cy.log('1.3 Active filters: Warning');
+ incidentsPage.toggleFilter('Critical'); // Deselect Critical
+ incidentsPage.toggleFilter('Warning'); // Select Warning
+ incidentsPage.elements.severityFilterChip().should('be.visible');
+ incidentsPage.elements.incidentsChartContainer().should('be.visible');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 5);
+
+ cy.log('Verified: 5 incidents shown');
+
+
+ cy.log('1.4 Active filters: Informative');
+ incidentsPage.toggleFilter('Warning'); // Deselect Warning
+ incidentsPage.toggleFilter('Informative'); // Select Info
+ incidentsPage.elements.severityFilterChip().should('be.visible');
+ incidentsPage.elements.incidentsChartContainer().should('be.visible');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 3);
+
+ cy.log('Verified: 3 incidents shown');
+
+
+ cy.log('1.6 Test severity filter combinations');
+ cy.log('1.6.1 Active filters: Critical + Informative');
+ incidentsPage.toggleFilter('Critical'); // Add Critical to existing Info
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 9);
+ cy.log('Verified: 9 incidents shown');
+
+
+ cy.log('1.6.2 Active filters: Warning + Informative');
+ incidentsPage.toggleFilter('Critical'); // Deselect Critical
+ incidentsPage.toggleFilter('Warning'); // Add Warning to existing Info
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 7);
+ cy.log('Verified: 7 incidents shown');
+
+
+ cy.log('1.6.3 Active filters: Critical + Warning + Informative');
+ incidentsPage.toggleFilter('Critical'); // Add Critical to existing Info + Warning
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 12);
+ cy.log('Verified: 12 incidents shown');
+
+
+ cy.log('1.7 Test state filter combinations - remove all severity filters and focus on Info + state');
+ cy.log('1.7.1 Active filters: Informative + Resolved');
+ incidentsPage.toggleFilter('Critical'); // Deselect Critical
+ incidentsPage.toggleFilter('Warning'); // Deselect Warning
+ // Now we have only Informative selected
+ incidentsPage.toggleFilter('Resolved'); // Add Resolved state filter
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 1);
+ cy.log('Verified: 1 incident shown');
+
+
+ cy.log('1.7.2 Active filters: Informative + Firing');
+ incidentsPage.toggleFilter('Resolved'); // Deselect Resolved
+ incidentsPage.toggleFilter('Firing'); // Select Firing
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 2);
+ cy.log('Verified: 2 incidents shown');
+
+
+ incidentsPage.clearAllFilters();
+ });
+
+ it('2. Chart interaction with active filters', () => {
+ cy.log('Setting up filters for chart interaction testing');
+ incidentsPage.clearAllFilters();
+
+ // Set up a specific filter state for testing
+ incidentsPage.toggleFilter('Informative');
+ incidentsPage.toggleFilter('Firing');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 2);
+ cy.log('Setup complete: Informative + Firing filters active, 2 incidents shown');
+
+ cy.log('2.1 Select incident bar while filters are active');
+ incidentsPage.selectIncidentByBarIndex(0);
+ incidentsPage.elements.incidentsTable().should('be.visible');
+ cy.log('Incident table displayed after bar selection');
+
+ cy.log('2.2 Verify just selected incident is shown in the chart');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 1);
+ cy.log('Verified: 1 incident shown');
+
+ cy.log('2.3 Deselect incident bar and verify filter persistence');
+ incidentsPage.deselectIncidentByBar();
+ incidentsPage.elements.incidentsTable().should('not.exist');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 2);
+ cy.log('Filters persisted after deselection: 2 incidents still shown');
+
+ incidentsPage.clearAllFilters();
+ });
+
+});
diff --git a/web/cypress/e2e/incidents/regression/02.reg_ui_charts_comprehensive.cy.ts b/web/cypress/e2e/incidents/regression/02.reg_ui_charts_comprehensive.cy.ts
new file mode 100644
index 00000000..436a7662
--- /dev/null
+++ b/web/cypress/e2e/incidents/regression/02.reg_ui_charts_comprehensive.cy.ts
@@ -0,0 +1,433 @@
+/*
+Regression test for Charts UI bugs (Section 2 of TESTING_CHECKLIST.md)
+
+This test loads comprehensive test data covering:
+- 2.1: Tooltip Positioning Issues
+- 2.2: Bar Sorting & Visibility Issues
+- 2.3: Date/Time Display Issues
+
+Test data: 12 incidents with 1-6 alerts each, varying durations (10m to 8h),
+alert names (4 to 180+ chars), multi-component and multi-severity scenarios.
+*/
+
+import { incidentsPage } from '../../../views/incidents-page';
+
+const ARROW_HEIGHT = 12;
+const ALLOWED_MARGIN = 8;
+
+interface RectLike {
+ top: number;
+ bottom: number;
+ left: number;
+ right: number;
+ width: number;
+ height: number;
+}
+
+function verifyTooltipPositioning(
+ tooltipRect: RectLike,
+ barRect: RectLike,
+ context: string,
+ win?: Window
+) {
+ cy.log(`${context}: Bar rect: top=${barRect.top}, bottom=${barRect.bottom}`);
+ cy.log(`${context}: Tooltip rect: top=${tooltipRect.top}, bottom=${tooltipRect.bottom}`);
+
+ expect(tooltipRect.top, `${context}: tooltip top should be above bar`).to.be.lessThan(barRect.top);
+ expect(tooltipRect.top, `${context}: tooltip should be in viewport`).to.be.greaterThan(0);
+ expect(tooltipRect.bottom + ARROW_HEIGHT, `${context}: tooltip bottom with arrow`).to.be.lessThan(barRect.top);
+ expect(tooltipRect.bottom + ARROW_HEIGHT + ALLOWED_MARGIN, `${context}: tooltip arrow should be near bar top`).to.be.greaterThan(barRect.top);
+
+ if (win) {
+ expect(tooltipRect.right, `${context}: tooltip should not overflow viewport width`).to.be.lessThan(win.innerWidth);
+ expect(tooltipRect.bottom, `${context}: tooltip should not overflow viewport`).to.be.lessThan(win.innerHeight);
+ }
+}
+
+const MCP = {
+ namespace: 'openshift-cluster-observability-operator',
+ packageName: 'cluster-observability-operator',
+ operatorName: 'Cluster Observability Operator',
+ config: {
+ kind: 'UIPlugin',
+ name: 'monitoring',
+ },
+};
+
+const MP = {
+ namespace: 'openshift-monitoring',
+ operatorName: 'Cluster Monitoring Operator',
+};
+
+describe('Regression: Charts UI - Comprehensive', () => {
+
+ before(() => {
+ cy.beforeBlockCOO(MCP, MP);
+
+ });
+
+ beforeEach(() => {
+ incidentsPage.goTo();
+ cy.mockIncidentFixture('incident-scenarios/12-charts-ui-comprehensive.yaml');
+ });
+
+ describe('Section 2.1: Tooltip Positioning', () => {
+
+ it('Tooltip positioning and content validation', () => {
+ cy.log('Setup: Clear filters and verify all incidents loaded');
+ incidentsPage.clearAllFilters();
+ incidentsPage.setDays('7 days');
+ incidentsPage.elements.incidentsChartContainer().should('be.visible');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 14);
+
+ // Chart order: Index 0 = newest (top), Index 13 = oldest (bottom)
+ // 0: network-firing-short-002 (10m)
+ // 13: version-short-name-001 (8h)
+
+ cy.log('1.2 Test top incident (newest) tooltip positioning');
+ incidentsPage.getIncidentBarRect(0).then((barRect) => {
+ incidentsPage.hoverOverIncidentBar(0);
+ incidentsPage.elements.tooltip().then(($tooltip) => {
+ verifyTooltipPositioning($tooltip[0].getBoundingClientRect(), barRect, 'Top incident');
+ });
+ });
+ cy.log('Verified: Top incident tooltip appears above bar without overlapping');
+
+ cy.log('1.3 Test middle incident tooltip positioning');
+ incidentsPage.getIncidentBarRect(7).then((barRect) => {
+ incidentsPage.hoverOverIncidentBar(7);
+ incidentsPage.elements.tooltip().then(($tooltip) => {
+ verifyTooltipPositioning($tooltip[0].getBoundingClientRect(), barRect, 'Middle incident');
+ });
+ });
+ cy.log('Verified: Middle incident tooltip appears above bar without overlapping');
+
+ cy.log('1.4 Test bottom incident (oldest) tooltip positioning');
+ incidentsPage.getIncidentBarRect(13).then((barRect) => {
+ incidentsPage.hoverOverIncidentBar(13);
+ cy.window().then((win) => {
+ incidentsPage.elements.tooltip().then(($tooltip) => {
+ verifyTooltipPositioning($tooltip[0].getBoundingClientRect(), barRect, 'Bottom incident', win);
+ });
+ });
+ });
+ cy.log('Verified: Bottom incident tooltip appears above bar and stays within viewport');
+
+ cy.log('2-4: Multi-incident verification (single traversal optimization)');
+ cy.log('3.1 Firing vs resolved incident tooltips');
+ cy.log('3.2 Find and verify firing incident (network-firing-short-002)');
+ incidentsPage.hoverOverIncidentBar(0);
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .then((text) => {
+ expect(text).to.contain('network-firing-short-002');
+ expect(text).to.match(/End.*---/);
+ });
+ cy.log('Verified: Firing incident shows --- for end time');
+
+ let foundMultiComponent = false;
+ let foundResolved = false;
+ let foundLongName = false;
+
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group, index) => {
+ const groupId = $group.attr('data-test');
+
+ if (!foundMultiComponent || !foundResolved || !foundLongName) {
+ incidentsPage.hoverOverIncidentBar(index);
+ incidentsPage.elements.tooltip().invoke('text').then((text) => {
+
+ if (!foundMultiComponent && text.includes('network-three-alerts-001')) {
+ cy.log('2.1 Multi-component tooltip content');
+ cy.log(`Found network-three-alerts-001 at index ${index}`);
+ cy.log('2.3 Verify tooltip shows all 3 components');
+ expect(text).to.contain('network');
+ expect(text).to.contain('compute');
+ expect(text).to.contain('storage');
+ cy.log('Verified: Multi-component tooltip displays all components');
+ foundMultiComponent = true;
+ }
+
+ if (!foundResolved && text.includes('network-resolved-short-001')) {
+ cy.log('3.3 Find and verify resolved incident (network-resolved-short-001)');
+ cy.log(`Found network-resolved-short-001 at index ${index}`);
+ expect(text).to.contain('Start');
+ expect(text).to.contain('End');
+ expect(text).to.not.match(/End.*---/);
+ cy.log('Verified: Resolved incident shows actual end time');
+ foundResolved = true;
+ }
+
+ if (!foundLongName && text.includes('others-very-long-name-001')) {
+ cy.log('4.1 Long alert name tooltip handling');
+ cy.log(`Found others-very-long-name-001 at index ${index}`);
+ cy.log('4.2 Verify tooltip with long name stays within viewport');
+ cy.window().then((win) => {
+ incidentsPage.elements.tooltip().then(($tooltip) => {
+ const tooltipRect = $tooltip[0].getBoundingClientRect();
+ expect(tooltipRect.right).to.be.lessThan(win.innerWidth);
+ expect(tooltipRect.bottom).to.be.lessThan(win.innerHeight);
+ expect(tooltipRect.left).to.be.greaterThan(0);
+ expect(tooltipRect.top).to.be.greaterThan(0);
+ });
+ });
+ cy.log('Verified: Tooltip with 180+ char alert name stays within viewport');
+ foundLongName = true;
+ }
+ });
+ }
+ });
+
+ cy.log('5.1 Alert chart tooltip positioning');
+ cy.log('5.2 Find and select incident with 6 alerts (etcd-six-alerts-001)');
+
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group, index) => {
+ const groupId = $group.attr('data-test');
+ if (groupId && groupId.includes('etcd-six-alerts-001')) {
+ cy.log(`Found etcd-six-alerts-001 at index ${index}`);
+ incidentsPage.selectIncidentByBarIndex(index);
+
+ cy.log('5.2 Verify alerts chart displays alerts');
+ incidentsPage.elements.alertsChartCard().should('be.visible');
+ incidentsPage.elements.alertsChartBarsGroups()
+ .should('have.length.greaterThan', 0);
+
+ cy.log('5.3 Test tooltip positioning for all alert bars');
+ incidentsPage.elements.alertsChartBarsPaths()
+ .its('length')
+ .then((alertCount) => {
+ cy.log(`Found ${alertCount} alert bars in chart`);
+
+ for (let i = 0; i < alertCount; i++) {
+ if (i > 2) {
+ // Expected failure for the latter alerts at this time
+ break;
+ }
+ incidentsPage.getAlertBarRect(i).then((barRect) => {
+ incidentsPage.hoverOverAlertBar(i);
+ cy.window().then((win) => {
+ incidentsPage.elements.alertsChartTooltip().first().then(($tooltip) => {
+ verifyTooltipPositioning($tooltip[0].getBoundingClientRect(), barRect, `Alert ${i}`, win);
+ });
+ });
+ });
+ }
+ });
+ cy.log('Verified: All alert tooltips appear correctly above their bars');
+
+ return false;
+ }
+ });
+ });
+ });
+
+ describe('Section 2.2: Bar Sorting & Visibility', () => {
+
+ it('Bar sorting, visibility, and filtering', () => {
+ cy.log('Setup: Clear filters and verify all incidents loaded');
+ incidentsPage.clearAllFilters();
+ incidentsPage.setDays('7 days');
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length', 14);
+
+ cy.log('1.2 Verify newest incident is at top (index 0)');
+ incidentsPage.hoverOverIncidentBar(0);
+
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .should('contain', 'network-firing-short-002');
+
+ cy.log('1.3 Verify oldest incident is at bottom (index 13)');
+ incidentsPage.hoverOverIncidentBar(13);
+
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .should('contain', 'version-short-name-001');
+
+ cy.log('Verified: Incidents are sorted chronologically with newest at top, oldest at bottom');
+
+ cy.log('2.1 Short duration incidents have visible bars');
+ cy.log('2.2 Check network-firing-short-002 (10 min duration, index 0)');
+ incidentsPage.getIncidentBarRect(0).then((barRect) => {
+ expect(barRect.width).to.be.greaterThan(0);
+ expect(barRect.height).to.be.greaterThan(0);
+ });
+
+ incidentsPage.elements.incidentsChartBarsGroups()
+ .eq(0)
+ .find('path[role="presentation"]')
+ .then(($paths) => {
+ const visiblePath = $paths.filter((i, el) => {
+ const fillOpacity = Cypress.$(el).css('fill-opacity') || Cypress.$(el).attr('fill-opacity');
+ return parseFloat(fillOpacity || '0') > 0;
+ }).first();
+
+ expect(visiblePath.length).to.be.greaterThan(0);
+ });
+ cy.log('Verified: Short duration firing incident has visible bar and is not transparent');
+
+ cy.log('2.3 Find and check network-resolved-short-001 (10 min duration)');
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group, index) => {
+ const groupId = $group.attr('data-test');
+ if (groupId && groupId.includes('network-resolved-short-001')) {
+ cy.log(`Found network-resolved-short-001 at index ${index}`);
+
+ incidentsPage.getIncidentBarRect(index).then((barRect) => {
+ expect(barRect.width).to.be.greaterThan(0);
+ expect(barRect.height).to.be.greaterThan(0);
+ });
+
+ cy.wrap($group)
+ .find('path[role="presentation"]')
+ .then(($paths) => {
+ const visiblePath = $paths.filter((i, el) => {
+ const fillOpacity = Cypress.$(el).css('fill-opacity') || Cypress.$(el).attr('fill-opacity');
+ return parseFloat(fillOpacity || '0') > 0;
+ }).first();
+
+ expect(visiblePath.length).to.be.greaterThan(0);
+ });
+ cy.log('Verified: Short duration resolved incident has visible bar and is not transparent');
+
+ return false;
+ }
+ });
+
+ cy.log('3.1 Filtered bars maintain uniform Y-axis spacing');
+
+ const verifyUniformSpacing = (positions: number[], context: string, maxAllowedDeviation = 2) => {
+ const spacings: number[] = [];
+ for (let i = 0; i < positions.length - 1; i++) {
+ spacings.push(positions[i + 1] - positions[i]);
+ }
+
+ const avgSpacing = spacings.reduce((a, b) => a + b, 0) / spacings.length;
+ const maxDeviation = Math.max(...spacings.map(s => Math.abs(s - avgSpacing)));
+
+ cy.log(`${context}: ${positions.length} bars, avg spacing: ${avgSpacing.toFixed(2)}px, max deviation: ${maxDeviation.toFixed(2)}px`);
+ expect(maxDeviation, `${context}: spacing should be uniform`).to.be.lessThan(maxAllowedDeviation);
+ };
+
+ cy.log('3.2 Verify uniform spacing before filtering');
+ const barPositionsBefore: number[] = [];
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group) => {
+ const rect = $group[0].getBoundingClientRect();
+ barPositionsBefore.push(rect.top);
+ }).then(() => {
+ verifyUniformSpacing(barPositionsBefore, 'Before filter');
+ });
+
+ cy.log('3.3 Apply Critical filter');
+ incidentsPage.toggleFilter('Critical');
+ incidentsPage.elements.severityFilterChip().should('be.visible');
+
+ cy.log('3.4 Verify uniform spacing after filtering');
+ const barPositionsAfter: number[] = [];
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group) => {
+ const rect = $group[0].getBoundingClientRect();
+ barPositionsAfter.push(rect.top);
+ }).then(() => {
+ verifyUniformSpacing(barPositionsAfter, 'After filter');
+ });
+
+ cy.log('Verified: Critical filter applied and visible bars maintain uniform spacing without gaps');
+ });
+ });
+
+ describe('Section 2.3: Date/Time Display', () => {
+
+ it('Date and time display validation', () => {
+ cy.log('Setup: Clear filters');
+ incidentsPage.clearAllFilters();
+
+ cy.log('1.2 Hover over firing incident and verify end shows ---');
+ incidentsPage.hoverOverIncidentBar(0);
+
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .then((text) => {
+ expect(text).to.contain('network-firing-short-002');
+ expect(text).to.contain('Start');
+ expect(text).to.match(/End.*---/);
+ });
+ cy.log('Verified: Firing incident shows start time and --- for end');
+
+ cy.log('1.3 Find and verify resolved incident shows both start and end times');
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group, index) => {
+ const groupId = $group.attr('data-test');
+ if (groupId && groupId.includes('network-resolved-short-001')) {
+ cy.log(`Found network-resolved-short-001 at index ${index}`);
+ incidentsPage.hoverOverIncidentBar(index);
+
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .then((text) => {
+ expect(text).to.contain('Start');
+ expect(text).to.contain('End');
+ expect(text).to.not.match(/End.*---/);
+ });
+ cy.log('Verified: Resolved incident shows both start and end times as timestamps');
+
+ return false;
+ }
+ });
+
+ cy.log('2.1 Multi-severity incident segments');
+ cy.log('2.2 Find monitoring-gradual-alerts-001 incident');
+ incidentsPage.elements.incidentsChartBarsGroups().each(($group, index) => {
+ const groupId = $group.attr('data-test');
+ if (groupId && groupId.includes('monitoring-gradual-alerts-001')) {
+ cy.log(`Found monitoring-gradual-alerts-001 at index ${index}`);
+
+ cy.log('2.3 Verify bar has multiple severity segments');
+ cy.wrap($group)
+ .find('path[role="presentation"]')
+ .should('have.length.greaterThan', 1);
+ cy.log('Verified: Multi-severity incident has multiple colored segments');
+
+ return false;
+ }
+ });
+
+ cy.log('3.1 Date format validation');
+ incidentsPage.hoverOverIncidentBar(0);
+
+ cy.log('3.2 Verify tooltip contains formatted timestamps');
+ incidentsPage.elements.tooltip()
+ .invoke('text')
+ .then((text) => {
+ expect(text).to.match(/\d{1,2}:\d{2}/);
+ });
+ cy.log('Verified: Tooltips display formatted date/time');
+
+ cy.log('4.1 Alert-level time verification in table');
+ incidentsPage.selectIncidentByBarIndex(13);
+
+ cy.log('4.2 Expand all rows to see alert details');
+ incidentsPage.elements.incidentsTable().should('be.visible');
+
+ cy.log('4.3 Get alert information');
+ incidentsPage.getSelectedIncidentAlerts().then((alerts) => {
+ expect(alerts.length).to.be.greaterThan(0);
+
+ cy.log('4.4 Verify each alert has start time');
+ alerts.forEach((alert, index) => {
+ alert.getStartCell().invoke('text').then((startText) => {
+ expect(startText.trim()).to.not.be.empty;
+ expect(startText.trim()).to.not.equal('-');
+ cy.log(`Alert ${index + 1} start time: ${startText.trim()}`);
+ });
+ });
+
+ cy.log('4.5 Verify firing alerts show --- or Firing for end time');
+ alerts.forEach((alert, index) => {
+ alert.getEndCell().invoke('text').then((endText) => {
+ expect(endText.trim()).to.not.be.empty;
+ cy.log(`Alert ${index + 1} end time: ${endText.trim()}`);
+ });
+ });
+ });
+ cy.log('Verified: Alert times in table display correctly with valid timestamps');
+ });
+ });
+
+});
+
diff --git a/web/cypress/e2e/incidents/regression/03.reg_api_calls.cy.ts b/web/cypress/e2e/incidents/regression/03.reg_api_calls.cy.ts
new file mode 100644
index 00000000..01be6ba8
--- /dev/null
+++ b/web/cypress/e2e/incidents/regression/03.reg_api_calls.cy.ts
@@ -0,0 +1,128 @@
+/*
+Regression test for Silences Not Applied Correctly (Section 3.2)
+
+BUG: Silences were being matched by name only, not by name + namespace + severity.
+This test verifies that silence matching uses: alertname + namespace + severity.
+
+While targeting the bug, it verifies the basic Silences Implementation.
+
+Verifies: OU-1020, OU-706
+*/
+
+import { incidentsPage } from '../../../views/incidents-page';
+
+const MCP = {
+ namespace: 'openshift-cluster-observability-operator',
+ packageName: 'cluster-observability-operator',
+ operatorName: 'Cluster Observability Operator',
+ config: {
+ kind: 'UIPlugin',
+ name: 'monitoring',
+ },
+};
+
+const MP = {
+ namespace: 'openshift-monitoring',
+ operatorName: 'Cluster Monitoring Operator',
+};
+
+describe('Regression: Silences Not Applied Correctly', () => {
+
+ before(() => {
+ cy.beforeBlockCOO(MCP, MP);
+ });
+
+ beforeEach(() => {
+ cy.log('Navigate to Observe → Incidents');
+ incidentsPage.goTo();
+ cy.log('Setting up silenced alerts mixed scenario');
+ cy.mockIncidentFixture('incident-scenarios/9-silenced-alerts-mixed-scenario.yaml');
+ });
+
+ it('Silence matching verification flow - opacity and tooltip indicators', () => {
+ const selectIncidentAndWaitForAlerts = (incidentId: string, expectedAlertCount?: number) => {
+ incidentsPage.elements.incidentsChartBar(incidentId).click();
+ cy.wait(2000);
+ incidentsPage.elements.alertsChartContainer().should('be.visible');
+ incidentsPage.elements.alertsChartCard().should('be.visible');
+
+ if (expectedAlertCount !== undefined) {
+ incidentsPage.elements.alertsChartBarsVisiblePaths().should('have.length', expectedAlertCount);
+ }
+ };
+
+ const verifyAlertOpacity = (alertIndex: number, expectedOpacity: number) => {
+ incidentsPage.elements.alertsChartBarsVisiblePaths()
+ .eq(alertIndex)
+ .then(($bar) => {
+ cy.log('Bar: ' + $bar);
+ cy.log('Fill-opacity: ' + $bar.css('fill-opacity'));
+ const opacity = parseFloat($bar.css('fill-opacity') || '1');
+ expect(opacity).to.equal(expectedOpacity);
+ });
+ };
+
+ const verifyAlertTooltip = (alertIndex: number, expectedTexts: string[], shouldBeSilenced: boolean) => {
+ incidentsPage.hoverOverAlertBar(alertIndex);
+ const tooltip = incidentsPage.elements.alertsChartTooltip().should('be.visible');
+ expectedTexts.forEach(text => {
+ tooltip.should('contain.text', text);
+ });
+ tooltip.should(shouldBeSilenced ? 'contain.text' : 'not.contain.text', '(silenced)');
+ };
+
+ cy.log('1.1 Verify all incidents loaded');
+ incidentsPage.clearAllFilters();
+ incidentsPage.elements.incidentsChartBarsGroups().should('have.length.greaterThan', 0);
+
+ cy.log('1.2 Select silenced alert incident (PAIR-2-storage-SILENCED)');
+ selectIncidentAndWaitForAlerts('PAIR-2-shared-alert-name-storage-SILENCED', 1);
+
+ cy.log('1.4 Hover over silenced alert and verify tooltip shows (silenced)');
+ verifyAlertTooltip(0, ['SyntheticSharedFiring002'], true);
+ cy.log('Verified: Silenced alert has opacity 0.3 and tooltip shows (silenced)');
+
+
+ cy.log('1.3 Hover over silenced alert and verify tooltip shows (silenced)');
+ verifyAlertTooltip(0, ['SyntheticSharedFiring002'], true);
+ cy.log('Verified: Silenced alert has opacity 0.3 and tooltip shows (silenced)');
+
+
+ cy.log('2.0 Deselect incident');
+ incidentsPage.deselectIncidentByBar();
+
+ cy.log('2.1 Select non-silenced alert incident with same alert name (PAIR-2-network-UNSILENCED)');
+ selectIncidentAndWaitForAlerts('PAIR-2-shared-alert-name-network-UNSILENCED', 1);
+
+ cy.log('2.2 Verify non-silenced alert has full opacity 1.0');
+ verifyAlertOpacity(0, 1.0);
+
+ cy.log('2.3 Hover over non-silenced alert and verify tooltip does not show (silenced)');
+ verifyAlertTooltip(0, ['SyntheticSharedFiring002'], false);
+ cy.log('Verified: Non-silenced alert has opacity 1.0 and tooltip does not show (silenced)');
+
+
+ cy.log('3.0 Deselect incident');
+ incidentsPage.deselectIncidentByBar();
+
+ cy.log('3.1 Select incident with both silenced and non-silenced alerts (CASE-3)');
+ selectIncidentAndWaitForAlerts('CASE-3-shared-alert-single-incident-storage-network-SILENCED-UNSILENCED', 2);
+
+ cy.log('3.2 Verify first alert (storage namespace / silenced) has opacity 0.3');
+ verifyAlertOpacity(0, 0.3);
+
+ cy.log('3.3 Verify second alert (network namespace / non-silenced) has opacity 1.0');
+ verifyAlertOpacity(1, 1.0);
+
+ cy.log('3.4 Hover over first alert and verify tooltip shows (silenced) with storage');
+ verifyAlertTooltip(0, ['storage'], true);
+
+ cy.log('3.5 Hover over second alert and verify tooltip shows network without (silenced)');
+ verifyAlertTooltip(1, ['network'], false);
+
+ cy.log('Verified: Same alert name with different namespaces handled correctly');
+ cy.log('Verified: Silence matching uses alertname + namespace + severity');
+ });
+});
+
+
diff --git a/web/cypress/e2e/incidents/regression/04.reg_redux_effects.cy.ts b/web/cypress/e2e/incidents/regression/04.reg_redux_effects.cy.ts
index 8f9017fc..ed5d22be 100644
--- a/web/cypress/e2e/incidents/regression/04.reg_redux_effects.cy.ts
+++ b/web/cypress/e2e/incidents/regression/04.reg_redux_effects.cy.ts
@@ -132,6 +132,7 @@ describe('Regression: Redux State Management', () => {
dropdown.listElement().should('be.visible');
deselection.action();
+
cy.wait(2000)
dropdown.listElement().should('not.exist');
diff --git a/web/cypress/fixtures/incident-scenarios/12-charts-ui-comprehensive.yaml b/web/cypress/fixtures/incident-scenarios/12-charts-ui-comprehensive.yaml
new file mode 100644
index 00000000..3a48e7b7
--- /dev/null
+++ b/web/cypress/fixtures/incident-scenarios/12-charts-ui-comprehensive.yaml
@@ -0,0 +1,331 @@
+name: "Charts UI Comprehensive Test Scenarios"
+description: "Combined scenarios for testing tooltip positioning, bar visibility, short duration alerts, varying alert counts (1-6 alerts), and alert name lengths (short to extremely long). Tests Section 2 of TESTING_CHECKLIST.md comprehensively."
+incidents:
+ # SHORT DURATION INCIDENTS (Section 2.2 - Bar Visibility, Section 2.3 - Date/Time Display)
+ # Resolved incident with 5 alerts of varying durations (0-10 minutes)
+ - id: "network-resolved-short-001"
+ component: "network"
+ layer: "core"
+ timeline:
+ start: "50m"
+ end: "40m"
+ alerts:
+ - name: "NetworkInterfaceDown0023"
+ namespace: "openshift-network"
+ severity: "critical"
+ firing: false
+ timeline:
+ start: "50m"
+ end: "40m"
+ - name: "NetworkLatencyHigh0023"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: false
+ timeline:
+ start: "47m"
+ end: "40m"
+ - name: "NetworkBandwidthUtilizationHigh0023"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: false
+ timeline:
+ start: "45m"
+ end: "40m"
+ - name: "NetworkConnectionPoolExhausted0023"
+ namespace: "openshift-network"
+ severity: "info"
+ firing: false
+ timeline:
+ start: "42m"
+ end: "40m"
+ - name: "NetworkDNSResolutionSlow0023"
+ namespace: "openshift-network"
+ severity: "info"
+ firing: false
+ timeline:
+ start: "41m"
+ end: "40m"
+
+ # Firing incident with 5 alerts of short durations
+ - id: "network-firing-short-002"
+ component: "network"
+ layer: "core"
+ timeline:
+ start: "10m"
+ alerts:
+ - name: "NetworkFirewallRuleBlocking0023"
+ namespace: "openshift-network"
+ severity: "critical"
+ firing: true
+ timeline:
+ start: "10m"
+ - name: "NetworkPacketLossHigh0023"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: true
+ timeline:
+ start: "8m"
+ - name: "NetworkRouteTableCorrupted0023"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: true
+ timeline:
+ start: "5m"
+ - name: "NetworkSecurityPolicyViolation0023"
+ namespace: "openshift-network"
+ severity: "info"
+ firing: true
+ timeline:
+ start: "3m"
+ - name: "NetworkLoadBalancerUnhealthy0023"
+ namespace: "openshift-network"
+ severity: "info"
+ firing: true
+ timeline:
+ start: "0m"
+
+ # VARYING ALERT COUNTS (Section 2.1 - Tooltip Positioning)
+ # 1 alert - bottom of chart (oldest) - Section 2.1 tooltip positioning
+ - id: "version-short-name-001"
+ component: "version"
+ layer: "core"
+ timeline:
+ start: "8h"
+ alerts:
+ - name: "Ver001"
+ namespace: "openshift-cluster-version"
+ severity: "info"
+ firing: true
+
+ # 1 alert - middle position
+ - id: "monitoring-one-alert-001"
+ component: "monitoring"
+ layer: "core"
+ timeline:
+ start: "7h"
+ alerts:
+ - name: "Short001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+
+ # 2 alerts
+ - id: "storage-two-alerts-001"
+ component: "storage"
+ layer: "core"
+ timeline:
+ start: "6h30m"
+ alerts:
+ - name: "DiskPressure001"
+ namespace: "openshift-storage"
+ severity: "warning"
+ firing: true
+ - name: "VolumeSpaceLow001"
+ namespace: "openshift-storage"
+ severity: "warning"
+ firing: true
+
+ # 3 alerts - multi-component incident (Section 2.1 tooltip content)
+ - id: "network-three-alerts-001"
+ component: "network"
+ layer: "core"
+ timeline:
+ start: "6h"
+ alerts:
+ - name: "NetworkLatencyHigh001"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: true
+ component: "network"
+ - name: "PacketDropRateElevated001"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: true
+ component: "compute"
+ - name: "ConnectionPoolExhausted001"
+ namespace: "openshift-network"
+ severity: "warning"
+ firing: true
+ component: "storage"
+
+ # 4 alerts - multi-severity (Section 2.3 - Multi-severity segments)
+ - id: "compute-four-alerts-001"
+ component: "compute"
+ layer: "core"
+ timeline:
+ start: "5h"
+ alerts:
+ - name: "NodeMemoryPressureDetected001"
+ namespace: "openshift-monitoring"
+ severity: "critical"
+ firing: true
+ - name: "CPUUtilizationThresholdExceeded001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ - name: "DiskIOSaturation001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ - name: "NetworkBandwidthLimited001"
+ namespace: "openshift-monitoring"
+ severity: "info"
+ firing: true
+
+ # 5 alerts - complex timeline
+ - id: "api-server-five-alerts-001"
+ component: "api-server"
+ layer: "core"
+ timeline:
+ start: "4h"
+ alerts:
+ - name: "APIServerRequestLatencyAboveThresholdForExtendedDuration001"
+ namespace: "openshift-monitoring"
+ severity: "critical"
+ firing: true
+ - name: "APIServerCertificateExpirationWarning001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ - name: "APIServerConnectionPoolUtilizationHigh001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ - name: "APIServerRateLimitingActive001"
+ namespace: "openshift-monitoring"
+ severity: "info"
+ firing: true
+ - name: "APIServerHealthcheckInfo001"
+ namespace: "openshift-monitoring"
+ severity: "info"
+ firing: true
+
+ # 6 alerts - extreme name lengths (Section 2.1 - long names)
+ - id: "etcd-six-alerts-001"
+ component: "etcd"
+ layer: "core"
+ timeline:
+ start: "3h"
+ alerts:
+ - name: "S001"
+ namespace: "openshift-etcd"
+ severity: "critical"
+ firing: true
+ - name: "EtcdBackupOperationFailureDetectedWithRetryAttempts001"
+ namespace: "openshift-etcd"
+ severity: "critical"
+ firing: true
+ - name: "EtcdDatabaseSizeApproachingConfiguredLimitRequiringCompaction001"
+ namespace: "openshift-etcd"
+ severity: "warning"
+ firing: true
+ - name: "EtcdLeaderElectionTimeExceedingNormalThresholdIndicatingPotentialNetworkIssues001"
+ namespace: "openshift-etcd"
+ severity: "warning"
+ firing: true
+ - name: "EtcdMemberCommunicationLatencyHigherThanExpectedBaselinePerformanceMetrics001"
+ namespace: "openshift-etcd"
+ severity: "info"
+ firing: true
+ - name: "EtcdClusterHealthMonitoringSystemReportingNormalOperatingConditionsWithAllMembersRespondingWithinAcceptableTimeframesAndNoOutstandingIssuesDetectedDuringRoutineMaintenanceChecks001"
+ namespace: "openshift-etcd"
+ severity: "info"
+ firing: true
+
+ # ALERT NAME LENGTH VARIATIONS (Section 2.1 - Tooltip content with varying lengths)
+ - id: "monitoring-medium-name-001"
+ component: "monitoring"
+ layer: "core"
+ timeline:
+ start: "2h30m"
+ alerts:
+ - name: "PrometheusTargetScrapeDurationExceeded001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+
+ - id: "storage-long-name-001"
+ component: "storage"
+ layer: "core"
+ timeline:
+ start: "2h"
+ alerts:
+ - name: "PersistentVolumeClaimPendingDueToInsufficientStorageCapacityAvailableInCluster001"
+ namespace: "openshift-storage"
+ severity: "critical"
+ firing: true
+
+ - id: "storage-long-component-name-001"
+ component: "storage-very-very-long-component-name"
+ layer: "core"
+ timeline:
+ start: "1h30m"
+ alerts:
+ - name: "StandardAlert001"
+ namespace: "openshift-storage-very-very-long-namespace-name"
+ severity: "critical"
+ firing: true
+
+ - id: "others-very-long-name-001"
+ component: "Others"
+ layer: "Others"
+ timeline:
+ start: "1h"
+ alerts:
+ - name: "CustomApplicationDatabaseConnectionPoolExhaustedRequiringImmediateAttentionFromOperationsTeamToInvestigateAndResolveUnderlyingInfrastructureLimitationsOrConfigurationIssues001"
+ namespace: "custom-application-namespace"
+ severity: "critical"
+ firing: true
+
+ # GRADUAL ESCALATION (Section 2.1 - Alerts chart tooltip positioning, Section 2.3 - Multi-severity segments)
+ # Tests tooltip positioning for multiple bars in alerts chart
+ - id: "monitoring-gradual-alerts-001"
+ component: "monitoring"
+ layer: "core"
+ timeline:
+ start: "6h"
+ severityChanges:
+ - time: "6h"
+ severity: "info"
+ - time: "4h"
+ severity: "warning"
+ - time: "2h"
+ severity: "critical"
+ alerts:
+ - name: "PrometheusRuleEvaluationSlowFirst001"
+ namespace: "openshift-monitoring"
+ severity: "info"
+ firing: true
+ timeline:
+ start: "6h"
+ - name: "PrometheusQueryLatencyIncreasingSecond001"
+ namespace: "openshift-monitoring"
+ severity: "info"
+ firing: true
+ timeline:
+ start: "5h"
+ - name: "AlertmanagerConfigReloadFailedThird001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ timeline:
+ start: "4h"
+ - name: "PrometheusTargetScrapeFailuresFourth001"
+ namespace: "openshift-monitoring"
+ severity: "warning"
+ firing: true
+ timeline:
+ start: "3h"
+ - name: "AlertmanagerClusterFailedToSendNotificationsFifth001"
+ namespace: "openshift-monitoring"
+ severity: "critical"
+ firing: true
+ timeline:
+ start: "2h"
+ - name: "PrometheusRemoteWriteDesiredShardsSixth001"
+ namespace: "openshift-monitoring"
+ severity: "critical"
+ firing: true
+ timeline:
+ start: "1h"
+
diff --git a/web/cypress/views/incidents-page.ts b/web/cypress/views/incidents-page.ts
index 793c0c08..a2d622d2 100644
--- a/web/cypress/views/incidents-page.ts
+++ b/web/cypress/views/incidents-page.ts
@@ -92,6 +92,24 @@ export const incidentsPage = {
alertsChartCard: () => cy.byTestID(DataTestIDs.AlertsChart.Card),
alertsChartContainer: () => cy.byTestID(DataTestIDs.AlertsChart.ChartContainer),
alertsChartSvg: () => incidentsPage.elements.alertsChartCard().find('svg'),
+ alertsChartBarsGroups: () => incidentsPage.elements.alertsChartSvg().find('g[role="presentation"]'),
+ alertsChartBarsPaths: () => incidentsPage.elements.alertsChartSvg().find('path[role="presentation"]'),
+ alertsChartBarsVisiblePaths: () => {
+ return cy.get('body').then($body => {
+ const exists = $body.find('g[role="presentation"][data-test*="alerts-chart-bar-"]').length > 0;
+ if (exists) {
+ return cy.get('g[role="presentation"][data-test*="alerts-chart-bar-"]')
+ .find('path[role="presentation"]')
+ .filter((index, element) => {
+ const fillOpacity = Cypress.$(element).css('fill-opacity') || Cypress.$(element).attr('fill-opacity');
+ return parseFloat(fillOpacity || '0') > 0;
+ });
+ } else {
+ cy.log('Alert chart bars were not found. Test continues.');
+ return cy.wrap([]);
+ }
+ });
+ },
alertsChartEmptyState: () => cy.byTestID(DataTestIDs.AlertsChart.EmptyState),
// Tables and data
@@ -121,6 +139,13 @@ export const incidentsPage = {
// Days select options
daysSelectList: () => cy.byTestID(DataTestIDs.IncidentsPage.DaysSelectList),
daysSelectOption: (days: string) => cy.byTestID(`${DataTestIDs.IncidentsPage.DaysSelectOption}-${days.replace(' ', '-')}`),
+
+ // Tooltips (custom Victory chart tooltips)
+ tooltip: () => cy.get('.incidents__tooltip'),
+ tooltipWrap: () => cy.get('.incidents__tooltip-wrap'),
+ tooltipArrow: () => cy.get('.incidents__tooltip-arrow'),
+
+ alertsChartTooltip: () => incidentsPage.elements.alertsChartCard().find('.incidents__tooltip'),
},
@@ -244,6 +269,95 @@ export const incidentsPage = {
.click({ force: true });
},
+ waitForTooltip: () => {
+ cy.log('incidentsPage.waitForTooltip');
+ return incidentsPage.elements.tooltip().should('be.visible');
+ },
+
+ hoverOverIncidentBar: (index: number) => {
+ cy.log(`incidentsPage.hoverOverIncidentBar: ${index}`);
+ incidentsPage.elements.incidentsChartBarsGroups()
+ .eq(index)
+ .find('path[role="presentation"]')
+ .then(($paths) => {
+ const visiblePath = $paths.filter((i, el) => {
+ const fillOpacity = Cypress.$(el).css('fill-opacity') || Cypress.$(el).attr('fill-opacity');
+ return parseFloat(fillOpacity || '0') > 0;
+ }).first();
+
+ if (visiblePath.length > 0) {
+ const rect = visiblePath[0].getBoundingClientRect();
+ const x = rect.left + rect.width / 2;
+ const y = rect.top + rect.height / 2;
+
+ cy.log(`Hovering at coordinates: x=${x}, y=${y}`);
+
+ incidentsPage.elements.incidentsChartSvg()
+ .first()
+ .trigger('mousemove', { clientX: x, clientY: y, force: true })
+ .wait(100);
+ } else {
+ throw new Error(`No visible paths found in bar group ${index}`);
+ }
+ });
+ return incidentsPage.waitForTooltip();
+ },
+
+ getIncidentBarRect: (index: number) => {
+ cy.log(`incidentsPage.getIncidentBarRect: ${index}`);
+ return incidentsPage.elements.incidentsChartBarsGroups()
+ .eq(index)
+ .then(($group) => {
+ const rect = $group[0].getBoundingClientRect();
+ return cy.wrap({
+ top: rect.top,
+ bottom: rect.bottom,
+ left: rect.left,
+ right: rect.right,
+ width: rect.width,
+ height: rect.height,
+ x: rect.x,
+ y: rect.y
+ });
+ });
+ },
+
+ getAlertBarRect: (index: number) => {
+ cy.log(`incidentsPage.getAlertBarRect: ${index}`);
+ return incidentsPage.elements.alertsChartBarsPaths()
+ .eq(index)
+ .then(($bar) => {
+ const rect = $bar[0].getBoundingClientRect();
+ return cy.wrap({
+ top: rect.top,
+ bottom: rect.bottom,
+ left: rect.left,
+ right: rect.right,
+ width: rect.width,
+ height: rect.height,
+ x: rect.x,
+ y: rect.y
+ });
+ });
+ },
+
+ hoverOverAlertBar: (index: number) => {
+ cy.log(`incidentsPage.hoverOverAlertBar: ${index}`);
+ incidentsPage.elements.alertsChartBarsPaths()
+ .eq(index)
+ .then(($bar) => {
+ const rect = $bar[0].getBoundingClientRect();
+ const x = rect.left + rect.width / 2;
+ const y = rect.top + rect.height / 2;
+
+ incidentsPage.elements.alertsChartSvg()
+ .first()
+ .trigger('mousemove', { clientX: x, clientY: y, force: true })
+ .wait(100);
+ });
+ return incidentsPage.waitForTooltip();
+ },
+
// Constants for search configuration
SEARCH_CONFIG: {
CHART_LOAD_WAIT: 1000,
@@ -424,6 +538,8 @@ export const incidentsPage = {
*/
getSelectedIncidentAlerts: () => {
cy.log('incidentsPage.getSelectedIncidentAlerts: Collecting alert information from selected incident');
+
+ incidentsPage.elements.incidentsDetailsTable().should('exist');
return incidentsPage.elements.incidentsTableRows()
.then(($rows) => {
diff --git a/web/src/components/Incidents/AlertsChart/AlertsChart.tsx b/web/src/components/Incidents/AlertsChart/AlertsChart.tsx
index b1737714..917eede6 100644
--- a/web/src/components/Incidents/AlertsChart/AlertsChart.tsx
+++ b/web/src/components/Incidents/AlertsChart/AlertsChart.tsx
@@ -215,13 +215,14 @@ const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => {
}
/>
-
+
{chartData.map((bar, index) => {
return (
//we have several arrays and for each array we make a ChartBar
datum.fill,
diff --git a/web/src/components/data-test.ts b/web/src/components/data-test.ts
index 0b8223cd..12ff1cad 100644
--- a/web/src/components/data-test.ts
+++ b/web/src/components/data-test.ts
@@ -116,6 +116,7 @@ export const DataTestIDs = {
Title: 'alerts-chart-title',
EmptyState: 'alerts-chart-empty-state',
ChartContainer: 'alerts-chart-container',
+ ChartBar: 'alerts-chart-bar',
},
// Incidents Table Test IDs