Skip to content

Commit 1ccfc3f

Browse files
Lightning00Bladegoogle-labs-jules[bot]
authored andcommitted
feat: auto handle dialogs during script evaluation
Added a new option `dialog` to `waitForEventsAfterAction` to automatically handle dialogs that trigger during execution. We hardcode it to `'accept'` for the `evaluate_script` tool so that `alert()` or `confirm()` don't block and timeout script evaluation. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 0a6aaa5 commit 1ccfc3f

5 files changed

Lines changed: 42 additions & 5 deletions

File tree

src/McpPage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class McpPage implements ContextPage {
117117

118118
waitForEventsAfterAction(
119119
action: () => Promise<unknown>,
120-
options?: {timeout?: number},
120+
options?: {timeout?: number; dialog?: 'accept' | 'dismiss'},
121121
): Promise<void> {
122122
const helper = this.createWaitForHelper(
123123
this.cpuThrottlingRate,

src/WaitForHelper.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,19 @@ export class WaitForHelper {
126126

127127
async waitForEventsAfterAction(
128128
action: () => Promise<unknown>,
129-
options?: {timeout?: number},
129+
options?: {timeout?: number; dialog?: 'accept' | 'dismiss'},
130130
): Promise<void> {
131+
const dialogHandler = (dialog: any) => {
132+
if (options?.dialog === 'dismiss') {
133+
void dialog.dismiss();
134+
} else {
135+
void dialog.accept();
136+
}
137+
};
138+
if (options?.dialog) {
139+
this.#page.on('dialog', dialogHandler);
140+
}
141+
131142
const navigationFinished = this.waitForNavigationStarted()
132143
.then(navigationStated => {
133144
if (navigationStated) {
@@ -158,6 +169,9 @@ export class WaitForHelper {
158169
logger(error);
159170
} finally {
160171
this.#abortController.abort();
172+
if (options?.dialog) {
173+
this.#page.off('dialog', dialogHandler);
174+
}
161175
}
162176
}
163177
}

src/tools/ToolDefinition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ export type ContextPage = Readonly<{
247247
clearDialog(): void;
248248
waitForEventsAfterAction(
249249
action: () => Promise<unknown>,
250-
options?: {timeout?: number},
250+
options?: {timeout?: number; dialog?: 'accept' | 'dismiss'},
251251
): Promise<void>;
252252
getInPageTools(): ToolGroup<InPageToolDefinition> | undefined;
253253
}>;

src/tools/script.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Example with arguments: \`(el) => {
8181
.getSelectedMcpPage()
8282
.waitForEventsAfterAction(async () => {
8383
await performEvaluation(worker, fnString, [], response);
84-
});
84+
}, {dialog: 'accept'});
8585
return;
8686
}
8787

@@ -103,7 +103,7 @@ Example with arguments: \`(el) => {
103103

104104
await mcpPage.waitForEventsAfterAction(async () => {
105105
await performEvaluation(evaluatable, fnString, args, response);
106-
});
106+
}, {dialog: 'accept'});
107107
} finally {
108108
void Promise.allSettled(args.map(arg => arg.dispose()));
109109
}

tests/tools/script.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,29 @@ describe('script', () => {
9898
});
9999
});
100100

101+
it('work for scripts that trigger dialogs', async () => {
102+
await withMcpContext(async (response, context) => {
103+
const page = context.getSelectedPptrPage();
104+
105+
await page.setContent(html`<button id="test">test</button>`);
106+
107+
await evaluateScript().handler(
108+
{
109+
params: {
110+
function: String(() => {
111+
alert('hello');
112+
return 'Works';
113+
}),
114+
},
115+
},
116+
response,
117+
context,
118+
);
119+
const lineEvaluation = response.responseLines.at(2)!;
120+
assert.strictEqual(JSON.parse(lineEvaluation), 'Works');
121+
});
122+
});
123+
101124
it('work for async functions', async () => {
102125
await withMcpContext(async (response, context) => {
103126
const page = context.getSelectedPptrPage();

0 commit comments

Comments
 (0)