"""Integration tests for external editor GUI functionality.""" import pytest import time import sys import os import tempfile import subprocess from pathlib import Path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from src import api_hook_client def get_vscode_processes(): try: result = subprocess.run( ["powershell", "-Command", "Get-Process Code* -ErrorAction SilentlyContinue | Format-Table -AutoSize"], capture_output=True, text=True, timeout=5 ) return result.stdout except: return "" @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_vscode_launches_with_diff_view(live_gui): """Test that clicking external editor button launches VSCode.""" proc, _ = live_gui client = api_hook_client.ApiHookClient() if not client.wait_for_server(timeout=15): pytest.skip("GUI server not available") subprocess.run( ["powershell", "-Command", "Stop-Process -Name 'Code*' -Force -ErrorAction SilentlyContinue"], timeout=10 ) time.sleep(1) before = get_vscode_processes() print(f"\n=== VSCODE LAUNCH TEST ===") print(f"VSCode before:\n{before if before.strip() else 'not running'}") sample_patch = """--- a/test.py +++ b/test.py @@ -1,2 +1,3 @@ HELLO_WORLD = "original" -HELLO_WORLD = "modified" +HELLO_WORLD = "changed" +NEW_LINE = "added" def main(): pass""" client.push_event("show_patch_modal", { "patch_text": sample_patch, "file_paths": ["test.py"] }) time.sleep(2) state = client.get_gui_state() print(f"Patch modal visible: {state.get('_show_patch_modal')}") print("Clicking 'Open in External Editor' button...") result = client.click("btn_open_external_editor") print(f"Click API result: {result}") time.sleep(1) state_after = client.get_gui_state() error_msg = state_after.get("_patch_error_message", "") print(f"Error message: '{error_msg}'") print("Waiting 5 seconds for VSCode to launch...") time.sleep(5) after = get_vscode_processes() print(f"VSCode after:\n{after if after.strip() else 'not running'}") client.push_event("hide_patch_modal", {}) time.sleep(1) @pytest.mark.integration @pytest.mark.timeout(30) def test_verify_command_format(): """Verify command format without 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 ) print(f"\nCommand: {cmd}") assert "--diff" in cmd assert "--new-window" in cmd assert "Code.exe" in cmd[0] print("Format: CORRECT") finally: os.unlink(orig) os.unlink(mod) @pytest.mark.integration @pytest.mark.timeout(120) def test_patch_modal_shows_with_configured_editor(live_gui, monkeypatch): """Test that when external editor is configured, the patch modal shows properly.""" proc, _ = live_gui client = api_hook_client.ApiHookClient() if not client.wait_for_server(timeout=15): pytest.skip("GUI server not available") import src.models as models_module monkeypatch.setattr(models_module, 'load_config', lambda: test_external_editor_config()) sample_patch = """--- a/test.py +++ b/test.py @@ -1,2 +1,3 @@ HELLO_WORLD = "original" -HELLO_WORLD = "modified" +HELLO_WORLD = "changed" +NEW_LINE = "added" def main(): pass""" client.push_event("show_patch_modal", { "patch_text": sample_patch, "file_paths": ["test.py"] }) time.sleep(2) state = client.get_gui_state() assert state.get("_show_patch_modal") == True print("\n=== PATCH MODAL TEST ===") print("Patch modal visible with external editor config") print("===================") client.push_event("hide_patch_modal", {}) time.sleep(1) @pytest.mark.integration @pytest.mark.timeout(120) def test_button_click_is_received(live_gui, monkeypatch): """Test that btn_open_external_editor button click is received.""" proc, _ = live_gui client = api_hook_client.ApiHookClient() if not client.wait_for_server(timeout=15): pytest.skip("GUI server not available") import src.models as models_module monkeypatch.setattr(models_module, 'load_config', lambda: test_external_editor_config()) sample_patch = """--- a/test.py +++ b/test.py @@ -1,2 +1,3 @@ HELLO_WORLD = "original" -HELLO_WORLD = "modified" +HELLO_WORLD = "changed" def main(): pass""" client.push_event("show_patch_modal", { "patch_text": sample_patch, "file_paths": ["test.py"] }) time.sleep(2) state = client.get_gui_state() assert state.get("_show_patch_modal") == True print("\n=== BUTTON CLICK TEST ===") print("Sending client.click('btn_open_external_editor')...") client.click("btn_open_external_editor") time.sleep(2) state = client.get_gui_state() error = state.get("_patch_error_message", "") print(f"Error after click: '{error}'") print("Button click sent.") print("===================") client.push_event("hide_patch_modal", {}) time.sleep(1) @pytest.mark.integration @pytest.mark.timeout(30) def test_verify_vscode_command_format(): """Direct verification of VSCode command format.""" 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 ) print(f"\n=== COMMAND FORMAT ===") print(f"Launches: {cmd[0]}") print(f"Args: {cmd[1:]}") assert "--diff" in cmd assert "--new-window" in cmd assert "Code.exe" in cmd[0] print("Format: CORRECT") print("==================") finally: os.unlink(orig) os.unlink(mod) if __name__ == "__main__": pytest.main([__file__, "-v", "-s"])