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())