Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ X Y coordinate space, based on the provided screenshot.
- Parameters:
- `element` (string): Human-readable element description used to obtain permission to interact with the element
- `ref` (string): Exact target element reference from the page snapshot
- `doubleClick` (boolean, optional): Whether to perform a double click instead of a single click
- Read-only: **false**

<!-- NOTE: This has been generated via update-readme.js -->
Expand Down
20 changes: 14 additions & 6 deletions src/tools/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,36 @@ const elementSchema = z.object({
ref: z.string().describe('Exact target element reference from the page snapshot'),
});

const clickSchema = elementSchema.extend({
doubleClick: z.boolean().optional().describe('Whether to perform a double click instead of a single click'),
});

const click = defineTool({
capability: 'core',
schema: {
name: 'browser_click',
title: 'Click',
description: 'Perform click on a web page',
inputSchema: elementSchema,
inputSchema: clickSchema,
type: 'destructive',
},

handle: async (context, params) => {
const tab = context.currentTabOrDie();
const locator = tab.snapshotOrDie().refLocator(params);

const code = [
`// Click ${params.element}`,
`await page.${await generateLocator(locator)}.click();`
];
const code: string[] = [];
if (params.doubleClick) {
code.push(`// Double click ${params.element}`);
code.push(`await page.${await generateLocator(locator)}.dblclick();`);
} else {
code.push(`// Click ${params.element}`);
code.push(`await page.${await generateLocator(locator)}.click();`);
}

return {
code,
action: () => locator.click(),
action: () => params.doubleClick ? locator.dblclick() : locator.click(),
captureSnapshot: true,
waitForNetwork: true,
};
Expand Down
39 changes: 39 additions & 0 deletions tests/core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,45 @@ await page.getByRole('button', { name: 'Submit' }).click();
`);
});

test('browser_click (double)', async ({ client, server }) => {
server.setContent('/', `
<title>Title</title>
<script>
function handle() {
document.querySelector('h1').textContent = 'Double clicked';
}
</script>
<h1 ondblclick="handle()">Click me</h1>
`, 'text/html');

await client.callTool({
name: 'browser_navigate',
arguments: { url: server.PREFIX },
});

expect(await client.callTool({
name: 'browser_click',
arguments: {
element: 'Click me',
ref: 'e2',
doubleClick: true,
},
})).toHaveTextContent(`
- Ran Playwright code:
\`\`\`js
// Double click Click me
await page.getByRole('heading', { name: 'Click me' }).dblclick();
\`\`\`

- Page URL: ${server.PREFIX}
- Page Title: Title
- Page Snapshot
\`\`\`yaml
- heading "Double clicked" [level=1] [ref=e3]
\`\`\`
`);
});

test('browser_select_option', async ({ client, server }) => {
server.setContent('/', `
<title>Title</title>
Expand Down
Loading