"""Headless smoke tests for the snotes TUI (Textual pilot).""" from __future__ import annotations import importlib.util import sys from pathlib import Path import pytest textual = pytest.importorskip("textual") # noqa: F841 def _load_cli(): path = Path(__file__).parent.parent / "cli" / "snotes.py" spec = importlib.util.spec_from_file_location("snotes_cli", path) mod = importlib.util.module_from_spec(spec) sys.modules[spec.name] = mod spec.loader.exec_module(mod) return mod cli = _load_cli() NOTES = [ {"id": "a1", "title": "Shopping list", "updated_at": "2026-09-26 09:00:00", "tags": ["home"]}, { "id": "b2", "title": "Journal for 2026-09-26", "updated_at": "2026-09-25 22:00:00", "tags": ["journal"], }, ] BODIES = { "a1": "# Shopping list\n\n- milk\n- eggs\n", "b2": "# Journal for 2026-09-26\n\nA fine day.\n", } class FakeApi: def __init__(self): self.cfg = cli.Config(url="http://test", user="schmeeve") self.deleted: list[str] = [] def recent(self, limit: int = 30) -> list[dict]: return NOTES[:limit] def search(self, q: str) -> list[dict]: return [n for n in NOTES if q.lower() in n["title"].lower()] def get_note(self, note_id: str) -> dict: return {"id": note_id, "title": "t", "body": BODIES[note_id]} def create(self, body: str) -> dict: # pragma: no cover - not exercised return {"id": "new", "title": body} def update(self, note_id: str, body: str) -> dict: # pragma: no cover return {"id": note_id} def delete(self, note_id: str) -> None: self.deleted.append(note_id) async def test_tui_boots_with_recent_notes(): app = cli.make_app(FakeApi(), None) async with app.run_test() as pilot: await pilot.pause() from textual.widgets import ListView lv = app.query_one("#notes", ListView) assert len(lv.children) == 2 assert app.title == "snotes" async def test_tui_initial_query_filters(): app = cli.make_app(FakeApi(), "journal") async with app.run_test() as pilot: await pilot.pause() await pilot.pause(0.4) # debounce from textual.widgets import Input, ListView assert app.query_one("#search", Input).value == "journal" assert len(app.query_one("#notes", ListView).children) == 1 async def test_tui_preview_shows_note_body(): app = cli.make_app(FakeApi(), None) async with app.run_test() as pilot: await pilot.pause() from textual.widgets import Markdown preview = app.query_one("#preview", Markdown) await pilot.pause(0.2) assert "Shopping list" in preview.source or "milk" in preview.source async def test_tui_delete_flow(): fake = FakeApi() app = cli.make_app(fake, None) async with app.run_test() as pilot: await pilot.pause() await pilot.press("d") await pilot.pause() await pilot.press("y") await pilot.pause(0.3) assert fake.deleted == ["a1"]