chat-browser-bridge / test_keyboard_javascript_pointer.py
ifroyou's picture
Add isolated browser JavaScript, keyboard and pointer tools (#1)
6e4eb30
Raw History Blame Contribute Delete
5.26 kB
from __future__ import annotations
import asyncio
import json
import pytest
from browser_core import BrowserBridgeError
from javascript_core import run_javascript
from keyboard_core import press_key
from pointer_core import mouse_click, mouse_down, mouse_up
class _FakeKeyboard:
def __init__(self) -> None:
self.pressed: list[str] = []
async def press(self, key: str) -> None:
self.pressed.append(key)
class _FakeMouse:
def __init__(self) -> None:
self.events: list[tuple[object, ...]] = []
async def move(self, x: int, y: int) -> None:
self.events.append(("move", x, y))
async def down(self, *, button: str) -> None:
self.events.append(("down", button))
async def up(self, *, button: str) -> None:
self.events.append(("up", button))
async def click(self, x: int, y: int, *, button: str, click_count: int) -> None:
self.events.append(("click", x, y, button, click_count))
class _FakeLocator:
async def count(self) -> int:
return 0
class _FakePage:
def __init__(self, target: dict[str, object], js_result: object = None) -> None:
self.target = target
self.js_result = {"answer": 42} if js_result is None else js_result
self.url = "https://example.com/test"
self.viewport_size = {"width": 1280, "height": 900}
self.keyboard = _FakeKeyboard()
self.mouse = _FakeMouse()
async def evaluate(self, source: str, _arg: object = None) -> object:
if "document.activeElement" in source:
return self.target
encoded = json.dumps(self.js_result)
return {"ok": True, "result_json": encoded, "result_bytes": len(encoded.encode("utf-8"))}
def locator(self, _selector: str) -> _FakeLocator:
return _FakeLocator()
class _FakeSession:
def __init__(self, page: _FakePage) -> None:
self.session_id = "session-1"
self.page = page
self.lock = asyncio.Lock()
class _FakeManager:
def __init__(self, page: _FakePage) -> None:
self.session = _FakeSession(page)
async def get(self, _owner: str, _session_id: str, *, touch: bool = False) -> _FakeSession:
return self.session
async def revalidate_locked_session(self, _owner: str, _session_id: str, _session: _FakeSession) -> None:
return None
async def _sync_page(self, session: _FakeSession) -> _FakePage:
return session.page
def _target(*, tag: str, type: str = "", role: str = "", label: str = "", content_editable: bool = False) -> dict[str, object]:
return {
"present": True,
"tag": tag,
"type": type,
"role": role,
"label": label,
"autocomplete": "",
"content_editable": content_editable,
}
def test_enter_is_available_for_search_but_not_generic_text_input() -> None:
async def exercise() -> None:
search_page = _FakePage(_target(tag="input", type="search", label="Search"))
search_result = await press_key(_FakeManager(search_page), "owner", "session-1", "Enter")
assert search_result["status"] == "BROWSER_KEY_PRESSED"
assert search_page.keyboard.pressed == ["Enter"]
text_page = _FakePage(_target(tag="input", type="text", label="Message"))
with pytest.raises(BrowserBridgeError) as raised:
await press_key(_FakeManager(text_page), "owner", "session-1", "Enter")
assert raised.value.code == "KEY_ENTER_TARGET_BLOCKED"
asyncio.run(exercise())
def test_javascript_requires_action_confirmation_and_redacts_secret_api_path() -> None:
async def exercise() -> None:
page = _FakePage(_target(tag="textarea", label="Note"))
manager = _FakeManager(page)
with pytest.raises(BrowserBridgeError) as unconfirmed:
await run_javascript(manager, "owner", "session-1", "return { answer: 42 };", user_confirmed_javascript_action=False)
assert unconfirmed.value.code == "JAVASCRIPT_ACTION_CONFIRMATION_REQUIRED"
with pytest.raises(BrowserBridgeError) as secret:
await run_javascript(manager, "owner", "session-1", "return document.cookie;", user_confirmed_javascript_action=True)
assert secret.value.code == "JAVASCRIPT_SECRET_OR_NETWORK_API_BLOCKED"
result = await run_javascript(manager, "owner", "session-1", "return { answer: 42 };", user_confirmed_javascript_action=True)
assert result["status"] == "BROWSER_JAVASCRIPT_COMPLETED"
assert result["result"] == {"answer": 42}
assert result["user_edge_profile_used"] is False
asyncio.run(exercise())
def test_raw_canvas_pointer_no_longer_needs_repeated_confirmation() -> None:
async def exercise() -> None:
page = _FakePage(_target(tag="canvas", label="Chart"))
manager = _FakeManager(page)
clicked = await mouse_click(manager, "owner", "session-1", 100, 80)
assert clicked["status"] == "BROWSER_POINTER_CLICKED"
assert page.mouse.events == [("click", 100, 80, "left", 1)]
await mouse_down(manager, "owner", "session-1")
await mouse_up(manager, "owner", "session-1")
assert page.mouse.events[-2:] == [("down", "left"), ("up", "left")]
asyncio.run(exercise())