Skip to content

Commit 99b1341

Browse files
committed
fix: time out stalled devtools probes
1 parent 15ce618 commit 99b1341

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

src/McpContext.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,10 +707,26 @@ export class McpContext implements Context {
707707
// Some Electron apps still use older version
708708
// Fall back to not exposing DevTools at all.
709709
try {
710-
if (await page.hasDevTools()) {
711-
mcpPage.devToolsPage = await page.openDevTools();
712-
} else {
710+
const hasDevTools = await resolveWithTimeout(
711+
page.hasDevTools(),
712+
DEFAULT_TIMEOUT,
713+
);
714+
if (!hasDevTools) {
715+
if (hasDevTools === undefined) {
716+
this.logger(`Timed out detecting DevTools for ${page.url()}`);
717+
}
713718
mcpPage.devToolsPage = undefined;
719+
return;
720+
}
721+
const devToolsPage = await resolveWithTimeout(
722+
page.openDevTools(),
723+
DEFAULT_TIMEOUT,
724+
);
725+
if (devToolsPage === undefined) {
726+
this.logger(`Timed out opening DevTools for ${page.url()}`);
727+
mcpPage.devToolsPage = undefined;
728+
} else {
729+
mcpPage.devToolsPage = devToolsPage;
714730
}
715731
} catch {
716732
mcpPage.devToolsPage = undefined;

tests/McpContext.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,38 @@ describe('McpContext', () => {
171171
});
172172
});
173173

174+
it('skips pages whose DevTools state does not resolve', async () => {
175+
await withMcpContext(async (_response, context) => {
176+
const page = context.getSelectedPptrPage();
177+
const target = context.browser.targets().find(target => {
178+
return target.type() === 'page';
179+
});
180+
assert.ok(target);
181+
sinon.stub(target, 'page').resolves(page);
182+
sinon.stub(context.browser, 'targets').returns([target]);
183+
const hasDevTools = sinon.stub(page, 'hasDevTools').returns(
184+
new Promise<boolean>(() => {
185+
// Intentionally never resolves to mimic a stalled DevTools probe.
186+
}),
187+
);
188+
const clock = sinon.useFakeTimers();
189+
190+
const detectionPromise = context.detectOpenDevToolsWindows();
191+
for (let attempt = 0; attempt < 10 && !hasDevTools.called; attempt++) {
192+
await clock.tickAsync(0);
193+
}
194+
assert.ok(hasDevTools.called);
195+
let resolved = false;
196+
void detectionPromise.then(() => {
197+
resolved = true;
198+
});
199+
await clock.tickAsync(5_000);
200+
await clock.tickAsync(0);
201+
202+
assert.strictEqual(resolved, true);
203+
});
204+
});
205+
174206
it('resolves uid from a non-selected page snapshot', async () => {
175207
await withMcpContext(async (_response, context) => {
176208
// Page 1: set content and snapshot

0 commit comments

Comments
 (0)