119 lines
3.4 KiB
Python
119 lines
3.4 KiB
Python
"""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).")
|
|
|
|
|
|
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())
|