@@ -7,6 +7,12 @@ import { DataTestIDs } from '../../src/components/data-test';
77let _findIncidentSearchStart : number | null = null ;
88const _FIND_INCIDENT_HARD_TIMEOUT_MS = 35 * 60 * 1000 ; // 35 minutes
99
10+ // When true, search methods suppress Cypress command logging to prevent
11+ // DOM snapshot accumulation that causes OOM (exit 137) in CI containers.
12+ // Toggled by findIncidentWithAlert during its retry loop.
13+ let _quietSearch = false ;
14+ const _qLog = ( ) : { log : false } | Record < string , never > => _quietSearch ? { log : false } : { } ;
15+
1016export const incidentsPage = {
1117
1218 // Centralized element selectors - all selectors defined in one place
@@ -60,22 +66,22 @@ export const incidentsPage = {
6066 incidentsChartBars : ( ) => cy . byTestID ( DataTestIDs . IncidentsChart . ChartBars ) ,
6167 incidentsChartBar : ( groupId : string ) => cy . byTestID ( `${ DataTestIDs . IncidentsChart . ChartBar } -${ groupId } ` ) ,
6268 incidentsChartBarsVisiblePaths : ( ) => {
63- return cy . get ( 'body' ) . then ( $body => {
69+ return cy . get ( 'body' , _qLog ( ) ) . then ( $body => {
6470 // There is a delay between the element being rendered and the paths being visible.
6571 // The case when no paths are visible is valid, so we can not use should or conditional testing semantics.
66- cy . wait ( 500 ) ;
72+ cy . wait ( 500 , _qLog ( ) ) ;
6773 // We need to use the $body as both cases when the element is there or not are valid.
6874 const exists = $body . find ( 'g[role="presentation"][data-test*="incidents-chart-bar-"]' ) . length > 0 ;
6975 if ( exists ) {
70- return cy . get ( 'g[role="presentation"][data-test*="incidents-chart-bar-"]' )
76+ return cy . get ( 'g[role="presentation"][data-test*="incidents-chart-bar-"]' , _qLog ( ) )
7177 . find ( 'path[role="presentation"]' )
7278 . filter ( ( index , element ) => {
7379 const fillOpacity = Cypress . $ ( element ) . css ( 'fill-opacity' ) || Cypress . $ ( element ) . attr ( 'fill-opacity' ) ;
7480 return parseFloat ( fillOpacity || '0' ) > 0 ;
7581 } ) ;
7682 } else {
77- cy . log ( 'Chart bars were not found. Test continues.' ) ;
78- return cy . wrap ( [ ] ) ;
83+ if ( ! _quietSearch ) cy . log ( 'Chart bars were not found. Test continues.' ) ;
84+ return cy . wrap ( [ ] , _qLog ( ) ) ;
7985 }
8086 } ) ;
8187 } ,
@@ -320,35 +326,35 @@ export const incidentsPage = {
320326 * @returns Promise that resolves when the incidents table is visible
321327 */
322328 selectIncidentByBarIndex : ( index = 0 ) => {
323- cy . log ( `incidentsPage.selectIncidentByBarIndex: ${ index } (clicking visible path elements)` ) ;
324-
329+ if ( ! _quietSearch ) cy . log ( `incidentsPage.selectIncidentByBarIndex: ${ index } (clicking visible path elements)` ) ;
330+
325331 return incidentsPage . elements . incidentsChartBarsVisiblePaths ( )
326332 . should ( 'have.length.greaterThan' , index )
327333 . then ( ( $paths ) => {
328334 if ( index >= $paths . length ) {
329335 throw new Error ( `Index ${ index } exceeds available paths (${ $paths . length } )` ) ;
330336 }
331-
332- return cy . wrap ( $paths . eq ( index ) )
333- . click ( { force : true } ) ;
337+
338+ return cy . wrap ( $paths . eq ( index ) , _qLog ( ) )
339+ . click ( { force : true , ... _qLog ( ) } ) ;
334340 } )
335341 . then ( ( ) => {
336- cy . wait ( 2000 ) ;
342+ cy . wait ( 2000 , _qLog ( ) ) ;
337343 return incidentsPage . elements . incidentsTable ( )
338344 . scrollIntoView ( )
339345 . should ( 'exist' ) ;
340346 } ) ;
341347 } ,
342348
343349 deselectIncidentByBar : ( ) => {
344- cy . log ( 'incidentsPage.deselectIncidentByBar' ) ;
350+ if ( ! _quietSearch ) cy . log ( 'incidentsPage.deselectIncidentByBar' ) ;
345351 return incidentsPage . elements . incidentsChartBarsVisiblePaths ( )
346352 . then ( ( $paths ) => {
347353 if ( $paths . length === 0 ) {
348354 throw new Error ( 'No paths found in incidents chart' ) ;
349355 }
350- return cy . wrap ( $paths . eq ( 0 ) )
351- . click ( { force : true } ) ;
356+ return cy . wrap ( $paths . eq ( 0 ) , _qLog ( ) )
357+ . click ( { force : true , ... _qLog ( ) } ) ;
352358 } )
353359 . then ( ( ) => {
354360 return incidentsPage . elements . incidentsTable ( )
@@ -403,9 +409,9 @@ export const incidentsPage = {
403409 } ,
404410
405411 expandRow : ( rowIndex = 0 ) => {
406- cy . log ( 'incidentsPage.expandRow' ) ;
412+ if ( ! _quietSearch ) cy . log ( 'incidentsPage.expandRow' ) ;
407413 incidentsPage . elements . incidentsTableExpandButton ( rowIndex )
408- . click ( { force : true } ) ;
414+ . click ( { force : true , ... _qLog ( ) } ) ;
409415 } ,
410416
411417 waitForTooltip : ( ) => {
@@ -555,15 +561,13 @@ export const incidentsPage = {
555561 } ,
556562
557563 prepareIncidentsPageForSearch : ( ) => {
558- cy . log ( 'incidentsPage.prepareIncidentsPageForSearch: Setting up page...' ) ;
559- // Force a hard page reload to release DOM memory from previous search iterations.
560- // Without this, repeated searches within waitUntil accumulate Cypress command
561- // snapshots and browser DOM nodes, causing OOM (exit 137) in CI containers.
564+ if ( ! _quietSearch ) cy . log ( 'incidentsPage.prepareIncidentsPageForSearch: Setting up page...' ) ;
565+ // Force a hard page reload to release browser DOM memory from previous search iterations.
562566 cy . reload ( { log : false } ) ;
563567 incidentsPage . goTo ( ) ;
564568 incidentsPage . setDays ( incidentsPage . SEARCH_CONFIG . DEFAULT_DAYS ) ;
565569 incidentsPage . elements . incidentsChartContainer ( ) . should ( 'be.visible' ) ;
566- cy . wait ( incidentsPage . SEARCH_CONFIG . CHART_LOAD_WAIT ) ;
570+ cy . wait ( incidentsPage . SEARCH_CONFIG . CHART_LOAD_WAIT , _qLog ( ) ) ;
567571 } ,
568572
569573 /**
@@ -577,10 +581,9 @@ export const incidentsPage = {
577581 return incidentsPage . elements . incidentsTable ( ) . invoke ( 'text' ) . then ( ( text ) => {
578582 if ( String ( text ) . includes ( alertName ) ) {
579583 cy . log ( `Found alert "${ alertName } " in incident ${ incidentIndex + 1 } table content` ) ;
580- cy . log ( text ) ;
581- return cy . wrap ( true ) ;
584+ return cy . wrap ( true , _qLog ( ) ) ;
582585 }
583- return cy . wrap ( false ) ;
586+ return cy . wrap ( false , _qLog ( ) ) ;
584587 } ) ;
585588 } ,
586589
@@ -596,18 +599,18 @@ export const incidentsPage = {
596599 */
597600 checkComponentInIncident : ( alertName : string , incidentIndex : number , totalRows : number , currentRowIndex : number = 0 ) : Cypress . Chainable < boolean > => {
598601 if ( currentRowIndex >= totalRows ) {
599- cy . log ( `Checked all ${ totalRows } rows in incident ${ incidentIndex + 1 } , alert not found` ) ;
600- return cy . wrap ( false ) ;
602+ if ( ! _quietSearch ) cy . log ( `Checked all ${ totalRows } rows in incident ${ incidentIndex + 1 } , alert not found` ) ;
603+ return cy . wrap ( false , _qLog ( ) ) ;
601604 }
602-
603- cy . log ( `Expanding and checking row ${ currentRowIndex } in incident ${ incidentIndex + 1 } ` ) ;
605+
606+ if ( ! _quietSearch ) cy . log ( `Expanding and checking row ${ currentRowIndex } in incident ${ incidentIndex + 1 } ` ) ;
604607 incidentsPage . expandRow ( currentRowIndex ) ;
605-
608+
606609 return incidentsPage . checkComponentRowInIncidentTableForAlert ( alertName , incidentIndex )
607610 . then ( ( found ) => {
608611 if ( found ) {
609612 cy . log ( `Found alert "${ alertName } " in expanded row ${ currentRowIndex } of incident ${ incidentIndex + 1 } ` ) ;
610- return cy . wrap ( true ) ;
613+ return cy . wrap ( true , _qLog ( ) ) ;
611614 }
612615 return incidentsPage . checkComponentInIncident ( alertName , incidentIndex , totalRows , currentRowIndex + 1 ) ;
613616 } ) ;
@@ -622,24 +625,24 @@ export const incidentsPage = {
622625 * @returns Promise resolving to true if alert is found anywhere in the incident
623626 */
624627 searchAllComponentsInIncident : ( alertName : string , incidentIndex : number ) : Cypress . Chainable < boolean > => {
625- cy . log ( `incidentsPage.searchAllRowsInIncident: Checking all rows in incident ${ incidentIndex + 1 } for alert "${ alertName } "` ) ;
626-
628+ if ( ! _quietSearch ) cy . log ( `incidentsPage.searchAllRowsInIncident: Checking all rows in incident ${ incidentIndex + 1 } for alert "${ alertName } "` ) ;
629+
627630 return incidentsPage . checkComponentRowInIncidentTableForAlert ( alertName , incidentIndex )
628631 . then ( ( foundInMain ) => {
629632 if ( foundInMain ) {
630- return cy . wrap ( true ) ;
633+ return cy . wrap ( true , _qLog ( ) ) ;
631634 }
632-
635+
633636 return incidentsPage . elements . incidentsTable ( )
634637 . find ( 'tbody[data-test*="incidents-table-row-"]' )
635638 . then ( ( $rows ) => {
636639 const totalRows = $rows . length ;
637640 if ( totalRows === 0 ) {
638- cy . log ( `No rows found in incident ${ incidentIndex + 1 } ` ) ;
639- return cy . wrap ( false ) ;
641+ if ( ! _quietSearch ) cy . log ( `No rows found in incident ${ incidentIndex + 1 } ` ) ;
642+ return cy . wrap ( false , _qLog ( ) ) ;
640643 }
641-
642- cy . log ( `Found ${ totalRows } incident rows to check in incident ${ incidentIndex + 1 } ` ) ;
644+
645+ if ( ! _quietSearch ) cy . log ( `Found ${ totalRows } incident rows to check in incident ${ incidentIndex + 1 } ` ) ;
643646 return incidentsPage . checkComponentInIncident ( alertName , incidentIndex , totalRows ) ;
644647 } ) ;
645648 } ) ;
@@ -654,10 +657,10 @@ export const incidentsPage = {
654657 * @returns Promise resolving to true if alert is found in the incident
655658 */
656659 searchForAlertInIncident : ( alertName : string , incidentIndex : number ) : Cypress . Chainable < boolean > => {
657- cy . log ( `incidentsPage.searchForAlertInIncident: Checking incident ${ incidentIndex + 1 } for alert "${ alertName } "` ) ;
658-
660+ if ( ! _quietSearch ) cy . log ( `incidentsPage.searchForAlertInIncident: Checking incident ${ incidentIndex + 1 } for alert "${ alertName } "` ) ;
661+
659662 return cy
660- . wrap ( null )
663+ . wrap ( null , _qLog ( ) )
661664 . then ( ( ) => {
662665 incidentsPage . selectIncidentByBarIndex ( incidentIndex ) ;
663666 return null ;
@@ -674,25 +677,21 @@ export const incidentsPage = {
674677 * @returns Promise resolving to true if alert is found in any incident
675678 */
676679 traverseAllIncidentsBars : ( alertName : string , totalIncidents : number ) : Cypress . Chainable < boolean > => {
677- cy . log ( `incidentsPage.searchAllIncidents: Searching ${ totalIncidents } incidents for alert "${ alertName } "` ) ;
678-
680+ if ( ! _quietSearch ) cy . log ( `incidentsPage.searchAllIncidents: Searching ${ totalIncidents } incidents for alert "${ alertName } "` ) ;
681+
679682 const searchNextIncidentBar = ( currentIndex : number ) : Cypress . Chainable < boolean > => {
680683 if ( currentIndex >= totalIncidents ) {
681- cy . log ( `Checked all ${ totalIncidents } incidents, alert "${ alertName } " not found` ) ;
682- return cy . wrap ( false ) ;
684+ if ( ! _quietSearch ) cy . log ( `Checked all ${ totalIncidents } incidents, alert "${ alertName } " not found` ) ;
685+ return cy . wrap ( false , _qLog ( ) ) ;
683686 }
684687
685688 return incidentsPage . searchForAlertInIncident ( alertName , currentIndex )
686689 . then ( ( found ) => {
687690 if ( found ) {
688- return cy . wrap ( true ) ;
691+ return cy . wrap ( true , _qLog ( ) ) ;
689692 }
690693 incidentsPage . deselectIncidentByBar ( ) ;
691- // Wait for the incident to be deselected
692- // Quick workaround, could be improved by waiting for the number of paths to change, but it
693- // does not has to if 1 initially. The check for the alert table non existance is already implemented,
694- // but there seems to be a short delay between the alert table closing and new bars rendering.
695- cy . wait ( 500 )
694+ cy . wait ( 500 , _qLog ( ) ) ;
696695 return searchNextIncidentBar ( currentIndex + 1 ) ;
697696 } ) ;
698697 } ;
@@ -716,28 +715,35 @@ export const incidentsPage = {
716715 const elapsed = Date . now ( ) - _findIncidentSearchStart ;
717716 if ( elapsed > _FIND_INCIDENT_HARD_TIMEOUT_MS ) {
718717 _findIncidentSearchStart = null ;
718+ _quietSearch = false ;
719719 throw new Error (
720720 `findIncidentWithAlert: hard timeout after ${ Math . round ( elapsed / 60000 ) } minutes searching for "${ alertName } "`
721721 ) ;
722722 }
723723
724724 cy . log ( `incidentsPage.findIncidentWithAlert: Starting search for alert "${ alertName } "` ) ;
725725
726+ // Enable quiet mode to suppress Cypress DOM snapshots during the search.
727+ // Each snapshot stores a serialized copy of the DOM (~1-5 MB). Without this,
728+ // ~40 snapshots per search iteration * 15+ iterations = OOM in CI containers.
729+ _quietSearch = true ;
730+
726731 incidentsPage . prepareIncidentsPageForSearch ( ) ;
727732
728733 return incidentsPage . elements . incidentsChartBarsVisiblePaths ( )
729734 . then ( ( $paths ) => {
730735 const totalPaths = $paths . length ;
731736 if ( totalPaths === 0 ) {
732737 cy . log ( 'No visible incident bar paths found in chart' ) ;
733- return cy . wrap ( false ) ;
738+ return cy . wrap ( false , { log : false } ) ;
734739 }
735740
736741 return incidentsPage . traverseAllIncidentsBars ( alertName , totalPaths ) ;
737742 } )
738743 . then ( ( found : boolean ) => {
744+ _quietSearch = false ;
739745 if ( found ) {
740- _findIncidentSearchStart = null ; // Reset on success
746+ _findIncidentSearchStart = null ;
741747 }
742748 return found ;
743749 } ) ;
0 commit comments