fix: sticky launch errors (#324)

This fixes an issue that there were sticky launch errors. When the
[following code
path](a15f0f301b/src/context.ts (L307-L339))
was throwing, the Error was stored in the Promise and not cleared
afterwards, this meant:

- If a browser was not there and the user tried to install it via
`browser_install` it was never working since the error was sticky.
- If other errors like CDP is not available yet etc. error appear a
re-connect would not work - the MCP server would require a restart.

Test plan: Since we don't have any `browser_install` tests I added a CDP
test for now to cover this bug.
This commit is contained in:
Max Schmitt
2025-05-02 15:32:37 +02:00
committed by GitHub
parent a713300c5b
commit 062cdd0704
4 changed files with 54 additions and 26 deletions

View File

@@ -22,7 +22,7 @@ import { chromium } from 'playwright';
import { test as baseTest, expect as baseExpect } from '@playwright/test';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { spawn } from 'child_process';
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import { TestServer } from './testserver/index.ts';
import type { Config } from '../config';
@@ -36,7 +36,7 @@ type TestFixtures = {
visionClient: Client;
startClient: (options?: { args?: string[], config?: Config }) => Promise<Client>;
wsEndpoint: string;
cdpEndpoint: string;
cdpEndpoint: (port?: number) => Promise<string>;
server: TestServer;
httpsServer: TestServer;
mcpHeadless: boolean;
@@ -95,27 +95,33 @@ export const test = baseTest.extend<TestFixtures & TestOptions, WorkerFixtures>(
},
cdpEndpoint: async ({ }, use, testInfo) => {
const port = 3200 + (+process.env.TEST_PARALLEL_INDEX!);
const executablePath = chromium.executablePath();
const browserProcess = spawn(executablePath, [
`--user-data-dir=${testInfo.outputPath('user-data-dir')}`,
`--remote-debugging-port=${port}`,
`--no-first-run`,
`--no-sandbox`,
`--headless`,
'--use-mock-keychain',
`data:text/html,hello world`,
], {
stdio: 'pipe',
});
await new Promise<void>(resolve => {
browserProcess.stderr.on('data', data => {
if (data.toString().includes('DevTools listening on '))
resolve();
let browserProcess: ChildProcessWithoutNullStreams | undefined;
await use(async port => {
if (!port)
port = 3200 + test.info().parallelIndex;
if (browserProcess)
return `http://localhost:${port}`;
browserProcess = spawn(chromium.executablePath(), [
`--user-data-dir=${testInfo.outputPath('user-data-dir')}`,
`--remote-debugging-port=${port}`,
`--no-first-run`,
`--no-sandbox`,
`--headless`,
'--use-mock-keychain',
`data:text/html,hello world`,
], {
stdio: 'pipe',
});
await new Promise<void>(resolve => {
browserProcess!.stderr.on('data', data => {
if (data.toString().includes('DevTools listening on '))
resolve();
});
});
return `http://localhost:${port}`;
});
await use(`http://localhost:${port}`);
browserProcess.kill();
browserProcess?.kill();
},
mcpHeadless: async ({ headless }, use) => {