Files
clide/tests/harnesses/app_harness.py
T
Jeroen SchweitzerandClaude Opus 4.5 1ed21d2a0f Restructure project and add user settings persistence
- Move src/clide/ to clide/ (flatten directory structure)
- Add SettingsService for persistent user preferences
- Settings stored in ~/.clide/settings.json
- Persist theme, panel visibility, and compact mode state
- Fix terminal rendering glitch with throttled refresh (~60fps)
- Update pyproject.toml, Makefile, and tests for new structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 00:42:46 +01:00

84 lines
2.6 KiB
Python

"""Full application test harness for Clide."""
from pathlib import Path
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: ClideSettings | None = None,
extension_manager: ExtensionManager | None = None,
) -> None:
self.workdir = workdir
self.settings = settings or ClideSettings()
self.extension_manager = extension_manager or ExtensionManager()
self._app: ClideApp | None = None
self._pilot: Pilot | None = 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()