"""Ticket #22: niri + Dank Material Shell integration pack (docs/keybind seam). A niri user following only the new documentation gets a working toggle bind (fires once per press) with no compositor-internal APIs. The tray-watcher question stays code-untouched until hardware observation (ticket #32); the probe assertion below pins the current watcher name so a silent widening fails loudly. When #32 records DMS evidence, update the doc and this assertion together. """ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] NIRI_DOC = REPO_ROOT / "docs" / "NIRI.md" NIRI_INCLUDE = REPO_ROOT / "resources" / "niri" / "voculinux.kdl" DOCS_INDEX = REPO_ROOT / "docs" / "README.md" USER_GUIDE = REPO_ROOT / "docs" / "USER_GUIDE.md" TRAY_INDICATOR = REPO_ROOT / "src" / "vocalinux" / "ui" / "tray_indicator.py" TOGGLE_SPAWN = 'spawn "voculinux" "--toggle"' CURRENT_WATCHER = "org.kde.StatusNotifierWatcher" def _read(path: Path) -> str: assert path.is_file(), f"{path.relative_to(REPO_ROOT)} is missing" return path.read_text(encoding="utf-8") def _active_lines(text: str) -> list[str]: """Non-blank lines that are not niri // comments.""" return [ line.strip() for line in text.splitlines() if line.strip() and not line.strip().startswith("//") ] def test_include_file_spawns_renamed_toggle_trigger() -> None: """The shipped bind drives the renamed CLI trigger with split argv.""" assert any(TOGGLE_SPAWN in line for line in _active_lines(_read(NIRI_INCLUDE))) def test_include_bind_is_non_repeating() -> None: """Key repeat must not re-fire the toggle while held.""" assert "repeat=false" in _read(NIRI_INCLUDE) def test_include_file_uses_no_compositor_internals() -> None: """Only plain spawn actions; no compositor message APIs.""" assert "niri msg" not in _read(NIRI_INCLUDE).lower() def test_doc_shows_include_and_once_per_press() -> None: """Doc presents the optional include file and the once-per-press promise.""" doc = _read(NIRI_DOC).lower() assert "include" in doc assert "repeat=false" in doc assert "once per press" in doc def test_doc_warns_compositor_bind_xor_in_app_shortcut() -> None: """One key is a compositor bind or an in-app shortcut, never both.""" doc = _read(NIRI_DOC).lower() assert "never both" in doc assert "external activation" in doc def test_doc_keeps_hold_to_talk_in_app() -> None: """A press-only bind cannot do push-to-talk; the release never arrives.""" doc = _read(NIRI_DOC).lower() assert "push-to-talk" in doc or "hold-to-talk" in doc assert "release" in doc def test_doc_records_tray_watcher_gap_without_probe_change() -> None: """DMS observation is deferred to #32; the probe stays as-is meanwhile.""" doc = _read(NIRI_DOC) assert CURRENT_WATCHER in doc assert "#32" in doc probe = _read(TRAY_INDICATOR) assert CURRENT_WATCHER in probe assert "org.freedesktop.StatusNotifierWatcher" not in probe def test_doc_linked_from_index_and_user_guide() -> None: """The niri page is reachable from the docs index and the user guide.""" assert "NIRI.md" in _read(DOCS_INDEX) assert "NIRI.md" in _read(USER_GUIDE)