Skip to content

Commit c263347

Browse files
committed
server: Disable automatic retry when exiting from debug mode
* In debug mode, never retry (maxTries = 1). * Fix "TypeError: Cannot read properties of undefined 'clientId'" at the end of certain debugging scenarios. When a browser is unable to complete a test run (i.e. some files don't load the the test gets stuck), the server will relay the console error messages, and eventually declare an idle timeout and stop the browser. In debug mode, we intercept the browser.stop() and leave it open. This works by swapping out the signal for a dummy signal. However, this meant that at the end of launchBrowserAttempt, where we check `signal.reason` that we have no signal.reason, and thus we try to return `result` instead of throwing. First: Remove the possiblity of this happening by not checking `signal.reason`. If we have no result, just throw. Returning undefined is not allowed under our contract and needlessly complicates the return handling for eventbus and reporters. Second: Fix the throw to throw the original signal's reason. The dummy signal should only affect what we send to the browser process in order to keep it open, we should not dummy ourselves as well. This means that if the test run finished normally then, once the developer ends their debug session we will return the result. Otherwise, we throw whatever the orignal reason was before we overrode it to keep the browser open (e.g. idle timeout). While at it, remove debug statements from LocalBrowser.spawn that are redundant with server.js/launchBrowser (browser_launch_exit, and browser_launch_error). This was moved there so that browser plugins for QTap won't need to worry about this.
1 parent e897467 commit c263347

File tree

2 files changed

+9
-9
lines changed

2 files changed

+9
-9
lines changed

src/server.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class ControlServer {
185185
}
186186
const url = await this.getProxyBase() + qtapUrlPath;
187187

188-
const maxTries = browserFn.allowRetries === false ? 1 : 3;
188+
const maxTries = (browserFn.allowRetries === false || this.debugMode) ? 1 : 3;
189189
let i = 1;
190190
while (true) {
191191
try {
@@ -237,15 +237,14 @@ class ControlServer {
237237

238238
async launchBrowserAttempt (browserFn, browserName, globalSignal, clientId, url, logger) {
239239
const controller = new AbortController();
240-
let signal = controller.signal;
240+
const signals = { browser: controller.signal, global: globalSignal };
241241
if (this.debugMode) {
242242
// Replace with a dummy signal that we never invoke
243-
signal = (new AbortController()).signal;
243+
signals.browser = (new AbortController()).signal;
244244
controller.signal.addEventListener('abort', () => {
245245
logger.warning('browser_signal_debugging', 'Keeping browser open for debugging');
246246
});
247247
}
248-
const signals = { browser: signal, global: globalSignal };
249248

250249
let clientIdleTimer;
251250

@@ -374,16 +373,16 @@ class ControlServer {
374373
browser.stop(new util.BrowserStopSignal('Browser ended unexpectedly'));
375374
} catch (e) {
376375
// Silence any errors from browserFn that happen after we called browser.stop().
377-
if (!signal.aborted) {
376+
if (!controller.signal.aborted) {
378377
logger.warning('browser_launch_error', e);
379378
browser.stop(new util.BrowserStopSignal('Browser ended unexpectedly'));
380379
throw e;
381380
}
382381
}
383382

384-
if (!result && signal.reason) {
383+
if (!result) {
385384
// Throw BrowserConnectTimeout for retry purposes.
386-
throw signal.reason;
385+
throw controller.signal.reason;
387386
}
388387

389388
return result;

src/util.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import fs from 'node:fs';
55
import os from 'node:os';
66
import path from 'node:path';
77

8+
/** @import { Logger } from './qtap.js' */
9+
810
export const MIME_TYPES = {
911
bin: 'application/octet-stream',
1012
css: 'text/css; charset=utf-8',
@@ -109,6 +111,7 @@ export const LocalBrowser = {
109111
* @param {Array<string>} args List of string arguments, passed to child_process.spawn()
110112
* which will automatically quote and escape these.
111113
* @param {Object<string,AbortSignal>} signals
114+
* @param {Logger} logger
112115
* @return {Promise<void>}
113116
*/
114117
async spawn (paths, args, signals, logger) {
@@ -154,14 +157,12 @@ export const LocalBrowser = {
154157
spawned.on('exit', (code, sig) => {
155158
const indent = (str) => str.trim().split('\n').map(line => ' ' + line).join('\n');
156159
if (!code) {
157-
logger.debug('browser_spawn_exit', `Process exited with code ${code}`);
158160
resolve();
159161
} else {
160162
const details = `Process exited with code ${code}`
161163
+ (sig ? `\n signal: ${sig}` : '')
162164
+ (stderr ? `\n stderr:\n${indent(stderr)}` : '')
163165
+ (stdout ? `\n stdout:\n${indent(stdout)}` : '');
164-
logger.debug('browser_spawn_exit', details);
165166
reject(new Error(details));
166167
}
167168
});

0 commit comments

Comments
 (0)