"""Offscreen functional check for the GUI retheme registry. Single thread (Qt widgets must be driven on their creating thread): - Patch the modal startup dialog to return the config immediately. - Patch ``qasync.QEventLoop.run_forever`` to grab the top-level window and raise a sentinel BEFORE the GUI loop actually runs. - Call ``run_gui``, catch the sentinel (window is now fully constructed), then read a registered themed widget's style in dark, trigger the REAL light/dark toggle (``_theme_act`` -> ``_on_theme_toggle`` -> ``_apply_theme`` -> ``retheme``), and assert the style changed and round-trips. Run: QT_QPA_PLATFORM=offscreen .\\.venv\\Scripts\\python.exe tests\\_retheme_check.py """ from __future__ import annotations import os import sys from pathlib import Path os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6 import QtWidgets # noqa: E402 import qasync # noqa: E402 from javis.core.config import load_config # noqa: E402 import javis.gui.main_window as mw # noqa: E402 class _Stop(Exception): pass STATE: dict = {"win": None} def _probe(win) -> None: """Drive the window: capture dark, toggle to light, assert it changed.""" if not getattr(win, "_themed", None): raise AssertionError("retheme registry (_themed) is empty!") sample = None for w, factory in win._themed: try: factory() except Exception: continue sample = (w, factory) break if sample is None: w, factory = win._themed[0] sample = (w, factory) w, factory = sample app = QtWidgets.QApplication.instance() app.processEvents() dark_style = w.styleSheet() print(f"[probe] themed widget: {type(w).__name__}") print(f"[probe] dark style = {dark_style!r}") act = win._theme_act act.setChecked(True) # -> light app.processEvents() light_style = w.styleSheet() print(f"[probe] light style = {light_style!r}") assert light_style != dark_style, ( "BUG: toggling theme did not change the registered widget's stylesheet. " f"dark={dark_style!r} light={light_style!r}" ) act.setChecked(False) # -> dark app.processEvents() back_dark = w.styleSheet() print(f"[probe] dark (again) = {back_dark!r}") assert back_dark == dark_style, ( f"BUG: toggling back did not restore the dark style. " f"{back_dark!r} != {dark_style!r}" ) print("\n[probe] OK — retheme registry is theme-reactive (dark->light->dark round-trip).") # GUI-01 regression: voice-profile cards used undefined names. card = win._make_voice_card( {"name": "Alice", "clip_count": 3, "enrolled_at": "2026-01-01T00:00:00"}) assert card.layout() is not None and card.layout().count() == 2, "voice card layout broken" print("[probe] OK — voice-profile card renders.") def main() -> int: app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([]) config = load_config(Path("config.yaml")) mw.show_startup_dialog = lambda cfg: cfg # type: ignore[assignment] def _run_forever(self): tops = [w for w in QtWidgets.QApplication.topLevelWidgets() if isinstance(w, QtWidgets.QMainWindow) and getattr(w, "retheme", None)] if tops: STATE["win"] = tops[-1] raise _Stop() qasync.QEventLoop.run_forever = _run_forever # type: ignore[assignment] try: mw.run_gui(config) except _Stop: pass if STATE["win"] is None: print("[FAIL] could not capture the main window") return 1 try: _probe(STATE["win"]) except Exception as e: # noqa: BLE001 print(f"[FAIL] {e!r}") return 1 print("\nRESULT: PASS") return 0 if __name__ == "__main__": sys.exit(main())