Skip to content

Commit 131f5ab

Browse files
committed
fix: remove special handling for comboboxes
1 parent 5e5b746 commit 131f5ab

2 files changed

Lines changed: 36 additions & 51 deletions

File tree

src/tools/input.ts

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import {logger} from '../logger.js';
8-
import type {McpContext, TextSnapshotNode} from '../McpContext.js';
8+
import type {McpContext} from '../McpContext.js';
99
import {zod} from '../third_party/index.js';
1010
import type {ElementHandle} from '../third_party/index.js';
1111
import {parseKey} from '../utils/keyboard.js';
@@ -140,61 +140,19 @@ export const hover = defineTool({
140140
},
141141
});
142142

143-
// The AXNode for an option doesn't contain its `value`. We set text content of the option as value.
144-
// If the form is a combobox, we need to find the correct option by its text value.
145-
// To do that, loop through the children while checking which child's text matches the requested value (requested value is actually the text content).
146-
// When the correct option is found, use the element handle to get the real value.
147-
async function selectOption(
148-
handle: ElementHandle,
149-
aXNode: TextSnapshotNode,
150-
value: string,
151-
) {
152-
let optionFound = false;
153-
for (const child of aXNode.children) {
154-
if (child.role === 'option' && child.name === value && child.value) {
155-
optionFound = true;
156-
const childHandle = await child.elementHandle();
157-
if (childHandle) {
158-
try {
159-
const childValueHandle = await childHandle.getProperty('value');
160-
try {
161-
const childValue = await childValueHandle.jsonValue();
162-
if (childValue) {
163-
await handle.asLocator().fill(childValue.toString());
164-
}
165-
} finally {
166-
void childValueHandle.dispose();
167-
}
168-
break;
169-
} finally {
170-
void childHandle.dispose();
171-
}
172-
}
173-
}
174-
}
175-
if (!optionFound) {
176-
throw new Error(`Could not find option with text "${value}"`);
177-
}
178-
}
179-
180143
async function fillFormElement(
181144
uid: string,
182145
value: string,
183146
context: McpContext,
184147
) {
185148
const handle = await context.getElementByUid(uid);
186149
try {
187-
const aXNode = context.getAXNodeByUid(uid);
188-
if (aXNode && aXNode.role === 'combobox') {
189-
await selectOption(handle, aXNode, value);
190-
} else {
191-
// Increase timeout for longer input values.
192-
const timeoutPerChar = 10; // ms
193-
const fillTimeout =
194-
context.getSelectedPage().getDefaultTimeout() +
195-
value.length * timeoutPerChar;
196-
await handle.asLocator().setTimeout(fillTimeout).fill(value);
197-
}
150+
// Increase timeout for longer input values.
151+
const timeoutPerChar = 10; // ms
152+
const fillTimeout =
153+
context.getSelectedPage().getDefaultTimeout() +
154+
value.length * timeoutPerChar;
155+
await handle.asLocator().setTimeout(fillTimeout).fill(value);
198156
} catch (error) {
199157
handleActionError(error, uid);
200158
} finally {

tests/tools/input.test.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,40 @@ describe('input', () => {
352352
});
353353
});
354354

355+
it('fills out a textarea marked as combobox', async () => {
356+
await withMcpContext(async (response, context) => {
357+
const page = context.getSelectedPage();
358+
await page.setContent(html`<textarea role="combobox" />`);
359+
await context.createTextSnapshot();
360+
await fill.handler(
361+
{
362+
params: {
363+
uid: '1_1',
364+
value: '1',
365+
},
366+
},
367+
response,
368+
context,
369+
);
370+
assert.strictEqual(
371+
response.responseLines[0],
372+
'Successfully filled out the element',
373+
);
374+
assert.ok(response.includeSnapshot);
375+
assert.ok(
376+
await page.evaluate(() => {
377+
return document.body.querySelector('textarea')?.value === '1';
378+
}),
379+
);
380+
});
381+
});
382+
355383
it('fills out a textarea with long text', async () => {
356384
await withMcpContext(async (response, context) => {
357385
const page = context.getSelectedPage();
358386
await page.setContent(html`<textarea />`);
359-
await page.focus('textarea');
360387
await context.createTextSnapshot();
361-
await page.setDefaultTimeout(1000);
388+
page.setDefaultTimeout(1000);
362389
await fill.handler(
363390
{
364391
params: {

0 commit comments

Comments
 (0)