diff --git a/README.md b/README.md index c8ae99d..028290f 100644 --- a/README.md +++ b/README.md @@ -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** diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index 49aaf52..7d1ef32 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -46,13 +46,17 @@ 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', }, @@ -60,14 +64,18 @@ const click = defineTool({ 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, }; diff --git a/tests/core.spec.ts b/tests/core.spec.ts index a35e7ed..93a81b4 100644 --- a/tests/core.spec.ts +++ b/tests/core.spec.ts @@ -70,6 +70,45 @@ await page.getByRole('button', { name: 'Submit' }).click(); `); }); +test('browser_click (double)', async ({ client, server }) => { + server.setContent('/', ` + Title + +

Click me

+ `, '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