66 * your election, the "Elastic License 2.0", the "GNU Affero General Public
77 * License v3.0 only", or the "Server Side Public License, v 1".
88 */
9-
9+ import chalk from 'chalk' ;
1010import { relative } from 'path' ;
1111import { REPO_ROOT } from '@kbn/repo-info' ;
1212import { createAssignmentProxy } from './assignment_proxy' ;
1313import { wrapFunction } from './wrap_function' ;
1414import { wrapRunnableArgs } from './wrap_runnable_args' ;
1515
16+ // Add a global configuration for pauseOnError
17+ const testConfig = {
18+ pauseOnError : process . argv . includes ( '--pauseOnError' ) ,
19+ } ;
20+
1621const allTestsSkippedCache = new WeakMap ( ) ;
1722function allTestsAreSkipped ( suite ) {
1823 // cache result for each suite so we don't have to traverse over and over
@@ -39,6 +44,63 @@ function allTestsAreSkipped(suite) {
3944 return childrenSkipped ;
4045}
4146
47+ function createErrorPauseHandler ( ) {
48+ return async ( err , test , callback ) => {
49+ // Check if pauseOnError is enabled globally or for this specific test
50+ if ( testConfig . pauseOnError ) {
51+ const originalTimeout = test . timeout ( ) ;
52+ // Set minimum pause timeout to 10 minutes (600000 ms)
53+ const minPauseTimeout = 600000 ;
54+ // Extend timeout if it's less than 10 minutes
55+ if ( originalTimeout < minPauseTimeout ) {
56+ test . timeout ( minPauseTimeout ) ;
57+ }
58+
59+ // Create a more informative pause message
60+ const pauseMessage = chalk . bold . yellow ( `
61+ !!!!! ${ chalk . red ( 'TEST PAUSED ON ERROR' ) } !!!!!
62+ ${ chalk . blue ( 'File:' ) } ${ test . file }
63+ ${ chalk . blue ( 'Test:' ) } ${ test . title }
64+ ${ chalk . red ( 'Error:' ) } ${ err . message }
65+
66+ ${ chalk . yellow ( 'Pausing test execution. Press Ctrl+C to exit.' ) }
67+
68+ ` ) ;
69+
70+ // Use console.error to ensure the message is visible
71+ console . error ( pauseMessage ) ;
72+
73+ return new Promise ( ( resolve ) => {
74+ // Set a timeout to automatically resume after 10 minutes
75+ const pauseTimeout = setTimeout ( ( ) => {
76+ console . error ( 'Pause timeout exceeded. Resuming test execution.' ) ;
77+ resolve ( ) ;
78+ // Restore the original timeout
79+ test . timeout ( originalTimeout ) ;
80+ // Clear the timeout to prevent memory leaks
81+ clearTimeout ( pauseTimeout ) ;
82+
83+ // call the callback to continue the test run
84+ callback ( ) ;
85+ } , minPauseTimeout ) ;
86+
87+ // Set up a way to manually interrupt
88+ const interruptHandler = ( ) => {
89+ clearTimeout ( pauseTimeout ) ;
90+ console . error ( chalk . bold . red ( '\nTest run interrupted by user.' ) ) ;
91+ process . exit ( 1 ) ;
92+ } ;
93+
94+ // Attach the interrupt handler
95+ process . once ( 'SIGINT' , interruptHandler ) ;
96+ } ) ;
97+ }
98+
99+ // Always trigger the existing test failure lifecycle hook
100+ await callback ( ) ;
101+ } ;
102+ }
103+
42104/**
43105 * @param {import('../lifecycle').Lifecycle } lifecycle
44106 * @param {any } context
@@ -54,6 +116,9 @@ export function decorateMochaUi(lifecycle, context, { rootTags }) {
54116 // suite is not the first suite
55117 let suiteCount = 0 ;
56118
119+ // Create a error pause handler specific to this lifecycle
120+ const errorPauseHandler = createErrorPauseHandler ( lifecycle ) ;
121+
57122 /**
58123 * Wrap the describe() function in the mocha UI to ensure
59124 * that the first call made when defining a test file is a
@@ -136,7 +201,9 @@ export function decorateMochaUi(lifecycle, context, { rootTags }) {
136201 return wrapNonSuiteFunction (
137202 name ,
138203 wrapRunnableArgs ( fn , lifecycle , async ( err , test ) => {
139- await lifecycle . testFailure . trigger ( err , test ) ;
204+ await errorPauseHandler ( err , test , async ( ) => {
205+ await lifecycle . testFailure . trigger ( err , test ) ;
206+ } ) ;
140207 } )
141208 ) ;
142209 }
@@ -154,7 +221,9 @@ export function decorateMochaUi(lifecycle, context, { rootTags }) {
154221 return wrapNonSuiteFunction (
155222 name ,
156223 wrapRunnableArgs ( fn , lifecycle , async ( err , test ) => {
157- await lifecycle . testHookFailure . trigger ( err , test ) ;
224+ await errorPauseHandler ( err , test , async ( ) => {
225+ await lifecycle . testHookFailure . trigger ( err , test ) ;
226+ } ) ;
158227 } )
159228 ) ;
160229 }
0 commit comments