From 153b790f319c7c706be79a50d2aed3d4a61f3adb Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 7 May 2026 20:27:16 -0400 Subject: [PATCH] test: Add GUI integration tests for external editor --- tests/test_external_editor_gui.py | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tests/test_external_editor_gui.py diff --git a/tests/test_external_editor_gui.py b/tests/test_external_editor_gui.py new file mode 100644 index 0000000..64ed8b5 --- /dev/null +++ b/tests/test_external_editor_gui.py @@ -0,0 +1,170 @@ +"""Integration test for external editor GUI functionality. + +These tests verify that the external editor configuration is properly +loaded and the patch modal shows the option to open in external editor. + +Manual verification: +1. Run: uv run sloppy.py +2. Ensure config.toml has external editor configured +3. Have an agent/Tier 4 generate a patch +4. Click "Open in External Editor" in the patch modal +5. VSCode should open with diff view showing original vs modified +""" +import pytest +import time +import sys +import os +import tempfile + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src"))) + +from src import api_hook_client + + +@pytest.fixture +def test_external_editor_config(): + return { + "ai": {"provider": "gemini", "model": "gemini-2.5-flash-lite"}, + "projects": {"paths": [], "active": ""}, + "paths": {"logs_dir": "logs", "scripts_dir": "scripts"}, + "tools": { + "text_editors": { + "vscode": { + "path": "C:\\apps\\Microsoft VS Code\\Code.exe", + "diff_args": ["--new-window", "--diff"] + } + }, + "default_editor": {"default_editor": "vscode"} + } + } + + +@pytest.mark.integration +@pytest.mark.timeout(120) +def test_external_editor_config_shows_in_panel(live_gui, monkeypatch): + proc, _ = live_gui + client = api_hook_client.ApiHookClient() + + if not client.wait_for_server(timeout=15): + pytest.skip("GUI server not available") + + test_config = { + "ai": {"provider": "gemini", "model": "gemini-2.5-flash-lite"}, + "projects": {"paths": [], "active": ""}, + "paths": {"logs_dir": "logs", "scripts_dir": "scripts"}, + "tools": { + "text_editors": { + "vscode": { + "path": "C:\\apps\\Microsoft VS Code\\Code.exe", + "diff_args": ["--new-window", "--diff"] + } + }, + "default_editor": {"default_editor": "vscode"} + } + } + + import src.models as models_module + monkeypatch.setattr(models_module, 'load_config', lambda: test_config) + + time.sleep(1) + + state = client.get_gui_state() + print(f"GUI state keys: {list(state.keys())[:10]}...") + + +@pytest.mark.integration +@pytest.mark.timeout(120) +def test_patch_modal_appears_with_external_editor(live_gui, monkeypatch): + proc, _ = live_gui + client = api_hook_client.ApiHookClient() + + if not client.wait_for_server(timeout=15): + pytest.skip("GUI server not available") + + test_config = { + "ai": {"provider": "gemini", "model": "gemini-2.5-flash-lite"}, + "projects": {"paths": [], "active": ""}, + "paths": {"logs_dir": "logs", "scripts_dir": "scripts"}, + "tools": { + "text_editors": { + "vscode": { + "path": "C:\\apps\\Microsoft VS Code\\Code.exe", + "diff_args": ["--new-window", "--diff"] + } + }, + "default_editor": {"default_editor": "vscode"} + } + } + + import src.models as models_module + monkeypatch.setattr(models_module, 'load_config', lambda: test_config) + + sample_patch = """--- a/test_file.py ++++ b/test_file.py +@@ -1,3 +1,4 @@ + def hello(): +- print("old") ++ print("new") ++ print("extra") + return True""" + + client.push_event("show_patch_modal", { + "patch_text": sample_patch, + "file_paths": ["test_file.py"] + }) + + time.sleep(2) + + state = client.get_gui_state() + assert state.get("_show_patch_modal") == True, f"Patch modal should be visible: {state}" + assert state.get("_pending_patch_text") is not None, "Pending patch text should be set" + + print("Patch modal visible with external editor configured") + print("To manually test: Click 'Open in External Editor' button to launch VSCode") + + client.push_event("hide_patch_modal", {}) + time.sleep(1) + + +@pytest.mark.integration +@pytest.mark.timeout(120) +def test_verify_vscode_diff_command_format(live_gui): + from src.external_editor import ExternalEditorLauncher, ExternalEditorConfig, TextEditorConfig + + config = ExternalEditorConfig( + editors={ + "vscode": TextEditorConfig( + name="vscode", + path="C:\\apps\\Microsoft VS Code\\Code.exe", + diff_args=["--new-window", "--diff"] + ) + }, + default_editor="vscode" + ) + launcher = ExternalEditorLauncher(config) + + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding="utf-8") as f: + f.write("original\n") + orig = f.name + + with tempfile.NamedTemporaryFile(mode="w", suffix="_modified.txt", delete=False, encoding="utf-8") as f: + f.write("modified\n") + mod = f.name + + try: + cmd = launcher.build_diff_command( + launcher.config.editors["vscode"], + orig, + mod + ) + assert "--diff" in cmd, f"VSCode command should include --diff: {cmd}" + assert "Code.exe" in cmd[0], f"Should launch Code.exe: {cmd}" + print(f"VSCode diff command correctly formatted: {cmd}") + finally: + os.unlink(orig) + os.unlink(mod) + + +if __name__ == "__main__": + pytest.main([__file__, "-v", "-s"])