@@ -212,8 +212,18 @@ export const incidentsPage = {
212212 } ,
213213
214214 goTo : ( ) => {
215- cy . log ( 'incidentsPage.goTo' ) ;
215+ if ( ! _quietSearch ) cy . log ( 'incidentsPage.goTo' ) ;
216216 nav . sidenav . clickNavLink ( [ 'Observe' , 'Alerting' ] ) ;
217+ // Wait for the Incidents tab to be registered by the dynamic plugin.
218+ // After session restore the plugin may need up to 2 min to re-register.
219+ cy . waitUntil (
220+ ( ) =>
221+ Cypress . $ (
222+ '.pf-v6-c-tabs__item:contains("Incidents"), ' +
223+ '.co-m-horizontal-nav__menu-item:contains("Incidents")' ,
224+ ) . length > 0 ,
225+ { interval : 2000 , timeout : 180000 } ,
226+ ) ;
217227 nav . tabs . switchTab ( 'Incidents' ) ;
218228 incidentsPage . elements . daysSelectToggle ( ) . should ( 'be.visible' ) ;
219229 } ,
@@ -399,42 +409,37 @@ export const incidentsPage = {
399409 } ,
400410
401411 /**
402- * Selects an incident from the chart by clicking on a bar at the specified index.
403- * BUG: Problems with multi-severity incidents (multiple paths in a single incident bar)
412+ * Selects an incident from the chart by clicking on a bar group at the
413+ * specified index. Uses bar groups (one per incident) instead of flattened
414+ * paths to correctly handle multi-severity incidents.
404415 *
405416 * @param index - Zero-based index of the incident bar to click (default: 0)
406417 * @returns Promise that resolves when the incidents table is visible
407418 */
408419 selectIncidentByBarIndex : ( index = 0 ) => {
409- if ( ! _quietSearch )
410- cy . log ( `incidentsPage.selectIncidentByBarIndex: ${ index } (clicking visible path elements)` ) ;
420+ if ( ! _quietSearch ) cy . log ( `incidentsPage.selectIncidentByBarIndex: ${ index } ` ) ;
411421
412422 return incidentsPage . elements
413- . incidentsChartBarsVisiblePaths ( )
423+ . incidentsChartBarsGroups ( )
414424 . should ( 'have.length.greaterThan' , index )
415- . then ( ( $paths ) => {
416- if ( index >= $paths . length ) {
417- throw new Error ( `Index ${ index } exceeds available paths (${ $paths . length } )` ) ;
418- }
419-
420- return cy . wrap ( $paths . eq ( index ) , _qLog ( ) ) . click ( { force : true , ..._qLog ( ) } ) ;
421- } )
425+ . eq ( index )
426+ . find ( 'path[role="presentation"]' )
427+ . first ( )
428+ . click ( { force : true , ..._qLog ( ) } )
422429 . then ( ( ) => {
423430 cy . wait ( 2000 , _qLog ( ) ) ;
424431 return incidentsPage . elements . incidentsTable ( ) . scrollIntoView ( ) . should ( 'exist' ) ;
425432 } ) ;
426433 } ,
427434
428- deselectIncidentByBar : ( ) => {
435+ deselectIncidentByBar : ( index = 0 ) => {
429436 if ( ! _quietSearch ) cy . log ( 'incidentsPage.deselectIncidentByBar' ) ;
430437 return incidentsPage . elements
431- . incidentsChartBarsVisiblePaths ( )
432- . then ( ( $paths ) => {
433- if ( $paths . length === 0 ) {
434- throw new Error ( 'No paths found in incidents chart' ) ;
435- }
436- return cy . wrap ( $paths . eq ( 0 ) , _qLog ( ) ) . click ( { force : true , ..._qLog ( ) } ) ;
437- } )
438+ . incidentsChartBarsGroups ( )
439+ . eq ( index )
440+ . find ( 'path[role="presentation"]' )
441+ . first ( )
442+ . click ( { force : true , ..._qLog ( ) } )
438443 . then ( ( ) => {
439444 return incidentsPage . elements . incidentsTable ( ) . should ( 'not.exist' ) ;
440445 } ) ;
@@ -653,8 +658,10 @@ export const incidentsPage = {
653658
654659 prepareIncidentsPageForSearch : ( ) => {
655660 if ( ! _quietSearch ) cy . log ( 'incidentsPage.prepareIncidentsPageForSearch: Setting up page...' ) ;
656- // Force a hard page reload to release browser DOM memory from previous search iterations.
657- cy . reload ( { log : false } ) ;
661+ // Use SPA navigation instead of cy.reload() — the Incidents component is a
662+ // dynamic plugin chunk, and cy.reload() causes the Console to re-resolve all
663+ // plugins from scratch, which silently fails in headless CI (blank page).
664+ // OOM is handled by _quietSearch suppressing DOM snapshots, not by reload.
658665 incidentsPage . goTo ( ) ;
659666 incidentsPage . setDays ( incidentsPage . SEARCH_CONFIG . DEFAULT_DAYS ) ;
660667 incidentsPage . elements . incidentsChartContainer ( ) . should ( 'be.visible' ) ;
@@ -821,7 +828,7 @@ export const incidentsPage = {
821828 if ( found ) {
822829 return cy . wrap ( true , _qLog ( ) ) ;
823830 }
824- incidentsPage . deselectIncidentByBar ( ) ;
831+ incidentsPage . deselectIncidentByBar ( currentIndex ) ;
825832 cy . wait ( 500 , _qLog ( ) ) ;
826833 return searchNextIncidentBar ( currentIndex + 1 ) ;
827834 } ) ;
@@ -859,16 +866,19 @@ export const incidentsPage = {
859866
860867 incidentsPage . prepareIncidentsPageForSearch ( ) ;
861868
862- return incidentsPage . elements
863- . incidentsChartBarsVisiblePaths ( )
864- . then ( ( $paths ) => {
865- const totalPaths = $paths . length ;
866- if ( totalPaths === 0 ) {
867- cy . log ( 'No visible incident bar paths found in chart' ) ;
869+ // Check for bar groups without asserting existence — an empty chart is
870+ // valid (e.g. when mocking empty incidents or before detection fires).
871+ return cy
872+ . get ( 'body' , _qLog ( ) )
873+ . then ( ( $body ) => {
874+ const barSelector = 'g[role="presentation"][data-test*="incidents-chart-bar-"]' ;
875+ const totalIncidents = $body . find ( barSelector ) . length ;
876+ if ( totalIncidents === 0 ) {
877+ if ( ! _quietSearch ) cy . log ( 'No incident bar groups found in chart' ) ;
868878 return cy . wrap ( false , { log : false } ) ;
869879 }
870880
871- return incidentsPage . traverseAllIncidentsBars ( alertName , totalPaths ) ;
881+ return incidentsPage . traverseAllIncidentsBars ( alertName , totalIncidents ) ;
872882 } )
873883 . then ( ( found : boolean ) => {
874884 if ( found ) {
0 commit comments