"""Full application test harness for Clide.""" from pathlib import Path from typing import Optional from textual.pilot import Pilot from clide.app import ClideApp from clide.extensions.manager import ExtensionManager from clide.models.config import ClideSettings class AppHarness: """Test harness for running the full Clide application. Provides a controlled environment for integration testing with mocked services and isolated file systems. Usage: harness = AppHarness(workdir=tmp_path) app, pilot = await harness.start() await pilot.press("ctrl+q") await harness.stop() """ def __init__( self, workdir: Path, settings: Optional[ClideSettings] = None, extension_manager: Optional[ExtensionManager] = None, ) -> None: self.workdir = workdir self.settings = settings or ClideSettings() self.extension_manager = extension_manager or ExtensionManager() self._app: Optional[ClideApp] = None self._pilot: Optional[Pilot] = None async def start(self) -> tuple[ClideApp, Pilot]: """Start the application and return app and pilot for testing. Returns: Tuple of (ClideApp instance, Pilot for simulating input) """ self._app = ClideApp(workdir=self.workdir) # Inject test dependencies self._app.extension_manager = self.extension_manager # Start app in test mode async with self._app.run_test() as pilot: self._pilot = pilot return self._app, pilot async def stop(self) -> None: """Clean shutdown of the application.""" if self._app: await self._app.action_quit() self._app = None self._pilot = None @property def app(self) -> ClideApp: """Get the running app instance.""" if self._app is None: raise RuntimeError("App not started. Call start() first.") return self._app @property def pilot(self) -> Pilot: """Get the pilot for simulating user input.""" if self._pilot is None: raise RuntimeError("App not started. Call start() first.") return self._pilot async def press_keys(self, *keys: str) -> None: """Simulate pressing a sequence of keys.""" await self.pilot.press(*keys) async def click(self, selector: str) -> None: """Click on a widget by CSS selector.""" await self.pilot.click(selector) async def wait_for_animation(self) -> None: """Wait for any running animations to complete.""" await self.pilot.pause()