from __future__ import annotations import logging from pathlib import Path from PIL import Image import src.agent as agent_module from src.models import RunArtifacts, RuntimeOptions class _DummyPyAutoGUI: FAILSAFE = True PAUSE = 0.0 def __init__(self) -> None: self.last_move_to: tuple[int, int] | None = None self.last_click: tuple[int, int] | None = None self.last_hotkey: tuple[str, ...] | None = None def screenshot(self) -> Image.Image: return Image.new("RGB", (1280, 720), color=(24, 24, 24)) def size(self) -> tuple[int, int]: return (1280, 720) def moveTo(self, x: int, y: int, duration: float = 0.0) -> None: # noqa: N802 self.last_move_to = (x, y) def click(self, x: int, y: int) -> None: self.last_click = (x, y) def write(self, _: str, interval: float = 0.0) -> None: return None def press(self, _: str) -> None: return None def hotkey(self, *keys: str) -> None: self.last_hotkey = tuple(keys) def _build_agent(tmp_path: Path, monkeypatch) -> agent_module.ScreenJobAgent: dummy_gui = _DummyPyAutoGUI() monkeypatch.setattr(agent_module, "pyautogui", dummy_gui) monkeypatch.setattr(agent_module.time, "sleep", lambda _: None) run_dir = tmp_path / "run" run_dir.mkdir(parents=True, exist_ok=True) artifacts = RunArtifacts( run_id="test_run", root_dir=run_dir, logs_dir=run_dir / "logs", shots_dir=run_dir / "shots", enhance_dir=run_dir / "enhance", log_file=run_dir / "screenjob.log", ) options = RuntimeOptions(model="gpt-5.4-mini") logger = logging.getLogger("screenjob-test-agent") return agent_module.ScreenJobAgent( client=object(), # type: ignore[arg-type] logger=logger, artifacts=artifacts, options=options, ) def test_task_complete_captures_return_and_data(tmp_path: Path, monkeypatch) -> None: agent = _build_agent(tmp_path, monkeypatch) result = agent._tool_task_complete({"return": "Task completed successfully", "data": "file1\nfile2"}) assert result["ok"] is True assert result["return"] == "Task completed successfully" assert result["data"] == "file1\nfile2" assert result["verification"]["ok"] is True verification_path = Path(result["verification"]["path"]) assert verification_path.exists() assert verification_path.name.startswith("screen_final_verification_step_") assert agent.final_result == "Task completed successfully" assert agent.final_data == "file1\nfile2" assert agent.final_verification is not None def test_click_supports_directional_offsets(tmp_path: Path, monkeypatch) -> None: agent = _build_agent(tmp_path, monkeypatch) click_result = agent._tool_click( { "coordinate": {"x": 100, "y": 100}, "offset_up": "2px", "offset_right": 7, "offset": {"x": 3, "y": 4}, "sleep_after_seconds": 0, } ) assert click_result["ok"] is True assert click_result["clicked"] == {"x": 110, "y": 102} def test_press_key_supports_hotkey_combo(tmp_path: Path, monkeypatch) -> None: agent = _build_agent(tmp_path, monkeypatch) result = agent._tool_press_key({"key": "meta+r"}) assert result["ok"] is True assert result["key"] == "win+r" assert result["message"] == "Key combo executed." assert agent_module.pyautogui.last_hotkey == ("win", "r")