Skip to content

Commit 2904428

Browse files
[8.x] [FTR] Added pause on error when error happens !! (elastic#216165) (elastic#218086)
# Backport This will backport the following commits from `main` to `8.x`: - [[FTR] Added pause on error when error happens !! (elastic#216165)](elastic#216165) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Shahzad","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-04-14T11:02:16Z","message":"[FTR] Added pause on error when error happens !! (elastic#216165)\n\n## Summary\n\nAdded pause on error when error happens !!\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/1d649224-64a0-4a22-b526-9fdbd348efb0\"\n/>\n\n---------\n\nCo-authored-by: Dzmitry Lemechko <[email protected]>","sha":"92556a37a70de82a09276187b24f21354e823165","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","ci:project-deploy-observability","backport:version","v9.1.0","v8.19.0","v9.0.1"],"title":"[FTR] Added pause on error when error happens !!","number":216165,"url":"https://github.com/elastic/kibana/pull/216165","mergeCommit":{"message":"[FTR] Added pause on error when error happens !! (elastic#216165)\n\n## Summary\n\nAdded pause on error when error happens !!\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/1d649224-64a0-4a22-b526-9fdbd348efb0\"\n/>\n\n---------\n\nCo-authored-by: Dzmitry Lemechko <[email protected]>","sha":"92556a37a70de82a09276187b24f21354e823165"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","9.0"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/216165","number":216165,"mergeCommit":{"message":"[FTR] Added pause on error when error happens !! (elastic#216165)\n\n## Summary\n\nAdded pause on error when error happens !!\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/1d649224-64a0-4a22-b526-9fdbd348efb0\"\n/>\n\n---------\n\nCo-authored-by: Dzmitry Lemechko <[email protected]>","sha":"92556a37a70de82a09276187b24f21354e823165"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Shahzad <[email protected]>
1 parent 004aeae commit 2904428

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

src/platform/packages/shared/kbn-test/src/functional_test_runner/cli/ftr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export function runFtrCli() {
145145
'throttle',
146146
'headless',
147147
'dry-run',
148+
'pauseOnError',
148149
],
149150
help: `
150151
--config=path path to a config file (either this or --journey is required)
@@ -170,6 +171,7 @@ export function runFtrCli() {
170171
--throttle enable network throttling in Chrome browser
171172
--headless run browser in headless mode
172173
--dry-run report tests without executing them
174+
--pauseOnError pause test runner on error
173175
`,
174176
},
175177
}

src/platform/packages/shared/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
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';
1010
import { relative } from 'path';
1111
import { REPO_ROOT } from '@kbn/repo-info';
1212
import { createAssignmentProxy } from './assignment_proxy';
1313
import { wrapFunction } from './wrap_function';
1414
import { wrapRunnableArgs } from './wrap_runnable_args';
1515

16+
// Add a global configuration for pauseOnError
17+
const testConfig = {
18+
pauseOnError: process.argv.includes('--pauseOnError'),
19+
};
20+
1621
const allTestsSkippedCache = new WeakMap();
1722
function 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

Comments
 (0)