fix: js statement wrapping + click auto-routes option to selectOption

Bug 1: js command wrapped all code as expressions — const, semicolons,
and multi-line code broke with SyntaxError. Added needsBlockWrapper()
and wrapForEvaluate() helpers (shared with eval) to detect statements
and use block wrapper {…} instead of expression wrapper (…).

Bug 2: clicking <option> refs hung forever because Playwright can't
.click() native select UI. Click handler now checks ARIA role + DOM
tagName and auto-routes to selectOption() via parent <select>.

Bug 3: click timeouts on <option> elements gave no guidance. Now
throws helpful error: "Use browse select instead of click."
This commit is contained in:
Garry Tan
2026-03-16 19:23:27 -07:00
parent c86faa7968
commit 75a91eebb2
4 changed files with 122 additions and 13 deletions
+51
View File
@@ -198,6 +198,27 @@ describe('Inspection', () => {
}
});
test('js handles multi-line with await', async () => {
const code = 'const x = await Promise.resolve(42);\nreturn x;';
const result = await handleReadCommand('js', [code], bm);
expect(result).toBe('42');
});
test('js handles await with semicolons', async () => {
const result = await handleReadCommand('js', ['const x = await Promise.resolve(5); return x + 1;'], bm);
expect(result).toBe('6');
});
test('js handles await with statement keywords', async () => {
const result = await handleReadCommand('js', ['const res = await Promise.resolve("ok"); return res;'], bm);
expect(result).toBe('ok');
});
test('js still works for simple expressions', async () => {
const result = await handleReadCommand('js', ['1 + 2'], bm);
expect(result).toBe('3');
});
test('css returns computed property', async () => {
const result = await handleReadCommand('css', ['h1', 'color'], bm);
// Navy color
@@ -247,6 +268,36 @@ describe('Interaction', () => {
expect(val).toBe('admin');
});
test('click on option ref auto-routes to selectOption', async () => {
await handleWriteCommand('goto', [baseUrl + '/forms.html'], bm);
// Reset select to default
await handleReadCommand('js', ['document.querySelector("#role").value = ""'], bm);
const snap = await handleMetaCommand('snapshot', [], bm, async () => {});
// Find an option ref (e.g., "Admin" option)
const optionLine = snap.split('\n').find((l: string) => l.includes('[option]') && l.includes('"Admin"'));
expect(optionLine).toBeDefined();
const refMatch = optionLine!.match(/@(e\d+)/);
expect(refMatch).toBeDefined();
const ref = `@${refMatch![1]}`;
const result = await handleWriteCommand('click', [ref], bm);
expect(result).toContain('auto-routed');
expect(result).toContain('Selected');
// Verify the select value actually changed
const val = await handleReadCommand('js', ['document.querySelector("#role").value'], bm);
expect(val).toBe('admin');
});
test('click CSS selector on option gives helpful error', async () => {
await handleWriteCommand('goto', [baseUrl + '/forms.html'], bm);
try {
await handleWriteCommand('click', ['option[value="admin"]'], bm);
expect(true).toBe(false); // Should not reach here
} catch (err: any) {
expect(err.message).toContain('select');
expect(err.message).toContain('option');
}
}, 15000);
test('hover works', async () => {
const result = await handleWriteCommand('hover', ['h1'], bm);
expect(result).toContain('Hovered');