Skip to content

Commit dba8b3c

Browse files
feat: improve tools with awaiting common events (#10)
1 parent 939813c commit dba8b3c

File tree

4 files changed

+48
-29
lines changed

4 files changed

+48
-29
lines changed

src/tools/input.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import z from 'zod';
88
import {defineTool} from './ToolDefinition.js';
99
import {ElementHandle} from 'puppeteer-core';
1010
import {ToolCategories} from './categories.js';
11-
import {performAction} from '../performAction.js';
11+
import {waitForEventsAfterAction} from '../waitForHelpers.js';
1212

1313
export const click = defineTool({
1414
name: 'click',
@@ -32,7 +32,7 @@ export const click = defineTool({
3232
const uid = request.params.uid;
3333
const handle = await context.getElementByUid(uid);
3434
try {
35-
await performAction(handle.frame.page(), async () => {
35+
await waitForEventsAfterAction(handle.frame.page(), async () => {
3636
await handle.asLocator().click({
3737
count: request.params.dblClick ? 2 : 1,
3838
});
@@ -67,7 +67,9 @@ export const hover = defineTool({
6767
const uid = request.params.uid;
6868
const handle = await context.getElementByUid(uid);
6969
try {
70-
await handle.asLocator().hover();
70+
await waitForEventsAfterAction(handle.frame.page(), async () => {
71+
await handle.asLocator().hover();
72+
});
7173
response.appendResponseLine(`Successfully hovered over the element`);
7274
response.setIncludeSnapshot(true);
7375
} finally {
@@ -92,10 +94,11 @@ export const fill = defineTool({
9294
value: z.string().describe('The value to fill in'),
9395
},
9496
handler: async (request, response, context) => {
95-
const uid = request.params.uid;
96-
const handle = await context.getElementByUid(uid);
97+
const handle = await context.getElementByUid(request.params.uid);
9798
try {
98-
await handle.asLocator().fill(request.params.value);
99+
await waitForEventsAfterAction(handle.frame.page(), async () => {
100+
await handle.asLocator().fill(request.params.value);
101+
});
99102
response.appendResponseLine(`Successfully filled out the element`);
100103
response.setIncludeSnapshot(true);
101104
} finally {
@@ -119,9 +122,11 @@ export const drag = defineTool({
119122
const fromHandle = await context.getElementByUid(request.params.from_uid);
120123
const toHandle = await context.getElementByUid(request.params.to_uid);
121124
try {
122-
await fromHandle.drag(toHandle);
123-
await new Promise(resolve => setTimeout(resolve, 50));
124-
await toHandle.drop(fromHandle);
125+
await waitForEventsAfterAction(fromHandle.frame.page(), async () => {
126+
await fromHandle.drag(toHandle);
127+
await new Promise(resolve => setTimeout(resolve, 50));
128+
await toHandle.drop(fromHandle);
129+
});
125130
response.appendResponseLine(`Successfully dragged an element`);
126131
response.setIncludeSnapshot(true);
127132
} finally {
@@ -152,7 +157,9 @@ export const fillForm = defineTool({
152157
for (const element of request.params.elements) {
153158
const handle = await context.getElementByUid(element.uid);
154159
try {
155-
await handle.asLocator().fill(element.value);
160+
await waitForEventsAfterAction(handle.frame.page(), async () => {
161+
await handle.asLocator().fill(element.value);
162+
});
156163
} finally {
157164
handle.dispose();
158165
}

src/tools/pages.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import z from 'zod';
88
import {defineTool} from './ToolDefinition.js';
99
import {ToolCategories} from './categories.js';
10+
import {waitForEventsAfterAction} from '../waitForHelpers.js';
1011

1112
export const listPages = defineTool({
1213
name: 'list_pages',
@@ -77,7 +78,11 @@ export const newPage = defineTool({
7778
},
7879
handler: async (request, response, context) => {
7980
const page = await context.newPage();
80-
await page.goto(request.params.url);
81+
82+
await waitForEventsAfterAction(page, async () => {
83+
await page.goto(request.params.url);
84+
});
85+
8186
response.setIncludePages(true);
8287
},
8388
});
@@ -95,7 +100,9 @@ export const navigatePage = defineTool({
95100
handler: async (request, response, context) => {
96101
const page = context.getSelectedPage();
97102

98-
await page.goto(request.params.url);
103+
await waitForEventsAfterAction(page, async () => {
104+
await page.goto(request.params.url);
105+
});
99106

100107
response.setIncludePages(true);
101108
},

src/tools/script.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import z from 'zod';
77
import {defineTool} from './ToolDefinition.js';
88
import {ToolCategories} from './categories.js';
9+
import {waitForEventsAfterAction} from '../waitForHelpers.js';
910

1011
export const evaluateScript = defineTool({
1112
name: 'evaluate_script',
@@ -26,13 +27,14 @@ export const evaluateScript = defineTool({
2627

2728
const script = `(async () => {
2829
return JSON.stringify(await (${request.params.function})());
29-
})()`;
30+
})()`;
3031

31-
const result = await page.evaluate(script);
32-
33-
response.appendResponseLine('Script ran on page and returned:');
34-
response.appendResponseLine('```json');
35-
response.appendResponseLine(`${result}`);
36-
response.appendResponseLine('```');
32+
await waitForEventsAfterAction(page, async () => {
33+
const result = await page.evaluate(script);
34+
response.appendResponseLine('Script ran on page and returned:');
35+
response.appendResponseLine('```json');
36+
response.appendResponseLine(`${result}`);
37+
response.appendResponseLine('```');
38+
});
3739
},
3840
});

src/performAction.ts renamed to src/waitForHelpers.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,14 @@ async function waitForStableDom(
4949
}
5050
});
5151

52-
return stableDomObserver.evaluate(async observer => {
53-
return await observer.resolver.promise;
54-
});
52+
return Promise.race([
53+
stableDomObserver.evaluate(async observer => {
54+
return await observer.resolver.promise;
55+
}),
56+
timeout(3000, signal).then(() => {
57+
throw new Error('Timeout');
58+
}),
59+
]);
5560
}
5661

5762
async function waitForNavigationStarted(page: CdpPage, signal: AbortSignal) {
@@ -101,7 +106,10 @@ function timeout(time: number, signal?: AbortSignal): Promise<void> {
101106
* a potential navigation, after which it waits
102107
* for the DOM to be stable before returning.
103108
*/
104-
export async function performAction(page: Page, callback: () => Promise<void>) {
109+
export async function waitForEventsAfterAction(
110+
page: Page,
111+
callback: () => Promise<void>,
112+
) {
105113
const controller = new AbortController();
106114

107115
const navigationStartedPromise = waitForNavigationStarted(
@@ -122,12 +130,7 @@ export async function performAction(page: Page, callback: () => Promise<void>) {
122130

123131
// Wait for stable dom after navigation so we execute in
124132
// the correct context
125-
await Promise.race([
126-
waitForStableDom(page, controller.signal),
127-
timeout(3000, controller.signal).then(() => {
128-
throw new Error('Timeout');
129-
}),
130-
]);
133+
await waitForStableDom(page, controller.signal);
131134
} catch (error) {
132135
logger(error);
133136
} finally {

0 commit comments

Comments
 (0)