chore: allow reading stderr of the browser process (#9813)

Co-authored-by: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com>
This commit is contained in:
Alex Rudenko 2023-03-09 15:00:25 +01:00 committed by GitHub
parent 2335770aee
commit a17b7ffaf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 1 deletions

View File

@ -17,6 +17,7 @@
import childProcess from 'child_process';
import os from 'os';
import path from 'path';
import readline from 'readline';
import {
Browser,
@ -88,6 +89,9 @@ export function launch(opts: LaunchOptions): Process {
return new Process(opts);
}
export const CDP_WEBSOCKET_ENDPOINT_REGEX =
/^DevTools listening on (ws:\/\/.*)$/;
class Process {
#executablePath;
#args: string[];
@ -245,6 +249,66 @@ class Process {
}
this.#clearListeners();
}
waitForLineOutput(regex: RegExp, timeout?: number): Promise<string> {
if (!this.#browserProcess.stderr) {
throw new Error('`browserProcess` does not have stderr.');
}
const rl = readline.createInterface(this.#browserProcess.stderr);
let stderr = '';
return new Promise((resolve, reject) => {
rl.on('line', onLine);
rl.on('close', onClose);
this.#browserProcess.on('exit', onClose);
this.#browserProcess.on('error', onClose);
const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;
const cleanup = (): void => {
if (timeoutId) {
clearTimeout(timeoutId);
}
rl.off('line', onLine);
rl.off('close', onClose);
this.#browserProcess.off('exit', onClose);
this.#browserProcess.off('error', onClose);
};
function onClose(error?: Error): void {
cleanup();
reject(
new Error(
[
`Failed to launch the browser process!${
error ? ' ' + error.message : ''
}`,
stderr,
].join('\n')
)
);
}
function onTimeout(): void {
cleanup();
reject(
new Error(
`Timed out after ${timeout} ms while waiting for the WS endpoint URL to appear in stdout!`
)
);
}
function onLine(line: string): void {
stderr += line + '\n';
const match = line.match(regex);
if (!match) {
return;
}
cleanup();
// The RegExp matches, so this will obviously exist.
resolve(match[1]!);
}
});
}
}
const PROCESS_ERROR_EXPLANATION = `Puppeteer was unable to kill the process which ran the browser binary.

View File

@ -21,7 +21,11 @@ import path from 'path';
import {Browser, BrowserPlatform} from '../../lib/cjs/browsers/browsers.js';
import {fetch} from '../../lib/cjs/fetch.js';
import {computeExecutablePath, launch} from '../../lib/cjs/launcher.js';
import {
CDP_WEBSOCKET_ENDPOINT_REGEX,
computeExecutablePath,
launch,
} from '../../lib/cjs/launcher.js';
import {testChromeBuildId, testFirefoxBuildId} from './versions.js';
@ -91,6 +95,7 @@ describe('launcher', () => {
const process = launch({
executablePath,
args: [
'--headless=new',
'--use-mock-keychain',
'--disable-features=DialMediaRouteProvider',
`--user-data-dir=${path.join(tmpDir, 'profile')}`,
@ -98,6 +103,27 @@ describe('launcher', () => {
});
await process.close();
});
it('should allow parsing stderr output of the browser process', async () => {
const executablePath = computeExecutablePath({
cacheDir: tmpDir,
browser: Browser.CHROME,
buildId: testChromeBuildId,
});
const process = launch({
executablePath,
args: [
'--headless=new',
'--use-mock-keychain',
'--disable-features=DialMediaRouteProvider',
'--remote-debugging-port=9222',
`--user-data-dir=${path.join(tmpDir, 'profile')}`,
],
});
const url = await process.waitForLineOutput(CDP_WEBSOCKET_ENDPOINT_REGEX);
await process.close();
assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser'));
});
});
describe('Firefox', function () {