Some checks failed
Release / semantic-release (push) Successful in 19s
tests / Unit tests (Linux, Python 3.11) (push) Failing after 5m15s
Release / build-linux (push) Failing after 7m7s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
moveMouse returned ok:true even when humanHover did nothing (no on-screen box) or the selector never matched — recreating the "claims it moved but didn't" bug. Now: humanHover returns a boolean (and brings the element into view first); moveMouse returns ok:false when the target isn't found or has no on-screen box, and when site=naver/... whose box isn't on the current page it navigates to the site home first before hovering. search now reports input=human|api-fallback|api so a silent fallback to cursor-less DOM input is visible, and the tool surfaces that note in the reply instead of implying a human-like search happened.
57 lines
2.4 KiB
Python
57 lines
2.4 KiB
Python
"""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
|