281 lines
7.8 KiB
Python
281 lines
7.8 KiB
Python
"""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"]) |