Skip to content

Commit 5f9ee9d

Browse files
committed
fix: add proactive tool rejection when dialog is open
1 parent f1afe84 commit 5f9ee9d

12 files changed

Lines changed: 210 additions & 0 deletions

src/tools/emulation.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ export const emulate = definePageTool({
6868
},
6969
handler: async (request, _response, context) => {
7070
const page = request.page;
71+
const dialog = page.getDialog();
72+
if (dialog) {
73+
throw new Error(
74+
`A dialog is open (${dialog.type()}: ${dialog.message()}).`,
75+
);
76+
}
7177
await context.emulate(request.params, page.pptrPage);
7278
},
7379
});

src/tools/memory.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export const takeMemorySnapshot = definePageTool({
2525
handler: async (request, response, context) => {
2626
const page = request.page;
2727
context.validatePath(request.params.filePath);
28+
const dialog = request.page.getDialog();
29+
if (dialog) {
30+
throw new Error(
31+
`A dialog is open (${dialog.type()}: ${dialog.message()}).`,
32+
);
33+
}
2834

2935
await page.pptrPage.captureHeapSnapshot({
3036
path: ensureExtension(request.params.filePath, '.heapsnapshot'),

src/tools/screenshot.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ export const screenshot = definePageTool({
5252
},
5353
handler: async (request, response, context) => {
5454
context.validatePath(request.params.filePath);
55+
const dialog = request.page.getDialog();
56+
if (dialog) {
57+
throw new Error(
58+
`A dialog is open (${dialog.type()}: ${dialog.message()}).`,
59+
);
60+
}
5561
if (request.params.uid && request.params.fullPage) {
5662
throw new Error('Providing both "uid" and "fullPage" is not allowed.');
5763
}

src/tools/snapshot.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ in the DevTools Elements panel (if any).`,
3535
},
3636
handler: async (request, response, context) => {
3737
context.validatePath(request.params.filePath);
38+
const dialog = request.page.getDialog();
39+
if (dialog) {
40+
throw new Error(
41+
`A dialog is open (${dialog.type()}: ${dialog.message()}).`,
42+
);
43+
}
3844
response.includeSnapshot({
3945
verbose: request.params.verbose ?? false,
4046
filePath: request.params.filePath,
@@ -60,6 +66,12 @@ export const waitFor = definePageTool({
6066
},
6167
handler: async (request, response, context) => {
6268
const page = request.page;
69+
const dialog = page.getDialog();
70+
if (dialog) {
71+
throw new Error(
72+
`A dialog is open (${dialog.type()}: ${dialog.message()}).`,
73+
);
74+
}
6375
await context.waitForTextOnPage(
6476
request.params.text,
6577
request.params.timeout,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exports[`emulation > when dialog is open 1`] = `
2+
{"content":[{"type":"text","text":"# Open dialog\\nalert: test dialog.\\nCall handle_dialog to handle it before continuing."}],"structuredContent":{"dialog":{"type":"alert","message":"test dialog","defaultValue":""}}}
3+
`;

tests/tools/emulation.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import assert from 'node:assert';
88
import {beforeEach, describe, it} from 'node:test';
99

10+
import type {Dialog} from 'puppeteer-core';
11+
1012
import {emulate} from '../../src/tools/emulation.js';
1113
import {
1214
geolocationTransform,
@@ -680,4 +682,35 @@ describe('emulation', () => {
680682
});
681683
});
682684
});
685+
686+
it('when dialog is open', async t => {
687+
await withMcpContext(async (response, context) => {
688+
const page = context.getSelectedPptrPage();
689+
await page.setContent('<h1>Test</h1>');
690+
const dialogPromise = new Promise<Dialog>(resolve => {
691+
page.on('dialog', dialog => resolve(dialog));
692+
});
693+
page.evaluate(() => {
694+
alert('test dialog');
695+
});
696+
const dialog = await dialogPromise;
697+
698+
await assert.rejects(
699+
emulate.handler(
700+
{
701+
params: {
702+
userAgent: 'Test Agent',
703+
},
704+
page: context.getSelectedMcpPage(),
705+
},
706+
response,
707+
context,
708+
),
709+
);
710+
const result = await response.handle('emulate', context);
711+
712+
t.assert.snapshot?.(JSON.stringify(result));
713+
await dialog.dismiss();
714+
});
715+
});
683716
});

tests/tools/memory.test.js.snapshot

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,7 @@ Static Data: {
198198
"maxJSObjectId": 54005
199199
}
200200
`;
201+
202+
exports[`memory > take_memory_snapshot > when dialog is open 1`] = `
203+
{"content":[{"type":"text","text":"# Open dialog\\nalert: test dialog.\\nCall handle_dialog to handle it before continuing."}],"structuredContent":{"dialog":{"type":"alert","message":"test dialog","defaultValue":""}}}
204+
`;

tests/tools/memory.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {tmpdir} from 'node:os';
1111
import {join} from 'node:path';
1212
import {describe, it} from 'node:test';
1313

14+
import type {Dialog} from 'puppeteer-core';
15+
1416
import {
1517
takeMemorySnapshot,
1618
exploreMemorySnapshot,
@@ -40,6 +42,34 @@ describe('memory', () => {
4042
}
4143
});
4244
});
45+
46+
it('when dialog is open', async t => {
47+
await withMcpContext(async (response, context) => {
48+
const page = context.getSelectedPptrPage();
49+
await page.setContent('<h1>Test</h1>');
50+
const dialogPromise = new Promise<Dialog>(resolve => {
51+
page.on('dialog', dialog => resolve(dialog));
52+
});
53+
page.evaluate(() => {
54+
alert('test dialog');
55+
});
56+
const dialog = await dialogPromise;
57+
const filePath = join(tmpdir(), 'test-dialog.heapsnapshot');
58+
59+
await assert.rejects(
60+
takeMemorySnapshot.handler(
61+
{params: {filePath}, page: context.getSelectedMcpPage()},
62+
response,
63+
context,
64+
),
65+
);
66+
await rm(filePath, {force: true});
67+
const result = await response.handle('take_memory_snapshot', context);
68+
69+
t.assert.snapshot?.(JSON.stringify(result));
70+
await dialog.dismiss();
71+
});
72+
});
4373
});
4474

4575
describe('load_memory_snapshot', () => {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exports[`screenshot > browser_take_screenshot > when dialog is open 1`] = `
2+
{"content":[{"type":"text","text":"# Open dialog\\nalert: test dialog.\\nCall handle_dialog to handle it before continuing."}],"structuredContent":{"dialog":{"type":"alert","message":"test dialog","defaultValue":""}}}
3+
`;

tests/tools/screenshot.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {tmpdir} from 'node:os';
1010
import {join} from 'node:path';
1111
import {describe, it} from 'node:test';
1212

13+
import type {Dialog} from 'puppeteer-core';
14+
1315
import {TextSnapshot} from '../../src/TextSnapshot.js';
1416
import {screenshot} from '../../src/tools/screenshot.js';
1517
import {screenshots} from '../snapshot.js';
@@ -297,5 +299,36 @@ describe('screenshot', () => {
297299
);
298300
});
299301
});
302+
303+
it('when dialog is open', async t => {
304+
await withMcpContext(async (response, context) => {
305+
const page = context.getSelectedPptrPage();
306+
await page.setContent('<h1>Test</h1>');
307+
const dialogPromise = new Promise<Dialog>(resolve => {
308+
page.on('dialog', dialog => {
309+
resolve(dialog);
310+
});
311+
});
312+
page.evaluate(() => {
313+
alert('test dialog');
314+
});
315+
const dialog = await dialogPromise;
316+
317+
await assert.rejects(
318+
screenshot.handler(
319+
{
320+
params: {format: 'png'},
321+
page: context.getSelectedMcpPage(),
322+
},
323+
response,
324+
context,
325+
),
326+
);
327+
const result = await response.handle('take_screenshot', context);
328+
329+
t.assert.snapshot?.(JSON.stringify(result));
330+
await dialog.dismiss();
331+
});
332+
});
300333
});
301334
});

0 commit comments

Comments
 (0)