"""Tests for the controlBrowser tool's action surface. These are deterministic schema/summary checks — they do not drive a real browser. The actual cursor movement is exercised live on the browser host (xdotool + CDP), which these tests cannot reach. """ import pytest from jarvis.tools.builtin.control_browser import ControlBrowserTool @pytest.fixture def tool(): return ControlBrowserTool() def test_movemouse_is_an_exposed_action(tool): # A weak model confabulated "moved the mouse" because no move/hover action # existed to call. The cursor-move capability must be a real action so the # request "move the mouse to the search box" maps to a tool call. enum = tool.inputSchema["properties"]["action"]["enum"] assert "moveMouse" in enum def test_movemouse_summary_reports_the_target(tool): summary = tool._summarise("moveMouse", {"site": "naver"}, {"ok": True, "target": "naver"}) assert "마우스" in summary and "naver" in summary def test_description_distinguishes_cursor_paths(tool): # The model must know navigate is address-bar only (no mouse) while # search/type/moveMouse move the real cursor — that distinction is the # whole point of the fix. desc = tool.description assert "moveMouse" in desc assert "address bar" in desc # navigate is described as address-bar typing def test_search_summary_flags_cursorless_fallback(tool): # When the real xdotool cursor path didn't run, the summary must say so # rather than implying a human-like search happened. human = tool._summarise("search", {"query": "날씨"}, {"ok": True, "site": "naver", "query": "날씨", "input": "human"}) assert "참고: 실제 마우스" not in human fell_back = tool._summarise("search", {"query": "날씨"}, {"ok": True, "site": "naver", "query": "날씨", "input": "api-fallback"}) assert "실제 마우스 커서 이동 없이" in fell_back def test_movemouse_summary_only_runs_on_success(tool): # _summarise is only called on ok:true; an ok:false (target not found / no # xdotool) is handled by run() as a failure reply, so a failed move can no # longer be reported as "moved". Sanity-check the success summary names a # target rather than a placeholder when one is present. summary = tool._summarise("moveMouse", {"selector": "#query"}, {"ok": True, "target": "#query"}) assert "#query" in summary