zapret-kvn/tests/test_app_fluent_dialog.py

101 lines
3.8 KiB
Python

"""FluentDialog theming tests (AC3 of ui-theme-layout-architecture).
NOTE on the module name: unittest discover imports test modules in
alphabetical order, and ``tests/test_engine_process_stop.py`` (and others)
create a bare ``QCoreApplication`` at import time, which cannot host
widgets and cannot be upgraded in-process. Keep the ``test_app_*`` prefix
so widget test modules sort before any ``QCoreApplication``-creating
module (see tests/test_app_nodes_page_view.py).
"""
from __future__ import annotations
import os
import unittest
from collections import deque
from pathlib import Path
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import QApplication
_existing = QApplication.instance()
if _existing is not None and not isinstance(_existing, QApplication):
raise RuntimeError(
"A bare QCoreApplication was created before test_app_fluent_dialog "
"was imported; widget tests need a QApplication."
)
app = _existing or QApplication([])
from qfluentwidgets import Theme, qconfig, setTheme
from xray_fluent.ui.fluent_dialog import FluentDialog
from xray_fluent.ui.lock_dialog import PasswordDialog
from xray_fluent.ui.subscriptions_page import SubscriptionDeleteDialog
from xray_fluent.ui.theme import surface_color
from xray_fluent.ui.traffic_graph import TrafficGraphDialog
UI_DIR = Path(__file__).resolve().parent.parent / "xray_fluent" / "ui"
class FluentDialogThemeTest(unittest.TestCase):
def setUp(self) -> None:
self._saved_theme = qconfig.themeMode.value
def tearDown(self) -> None:
setTheme(self._saved_theme)
QApplication.processEvents()
def test_remaining_dialogs_inherit_fluent_dialog(self) -> None:
"""Only short prompts stay modal; large forms are breadcrumb sub-pages."""
dialogs = [
PasswordDialog(),
TrafficGraphDialog(deque(), deque()),
SubscriptionDeleteDialog("x"),
]
for dialog in dialogs:
with self.subTest(dialog=type(dialog).__name__):
self.assertIsInstance(dialog, FluentDialog)
dialog.deleteLater()
def test_dialog_surface_and_palette_follow_theme(self) -> None:
setTheme(Theme.DARK)
dialog = PasswordDialog()
try:
dark_surface = surface_color().name()
self.assertIn(dark_surface, dialog.styleSheet())
dark_placeholder = dialog.palette().color(
QPalette.ColorRole.PlaceholderText
).getRgb()
dark_text = dialog.palette().color(QPalette.ColorRole.Text).getRgb()
setTheme(Theme.LIGHT)
QApplication.processEvents()
light_surface = surface_color().name()
self.assertNotEqual(dark_surface, light_surface)
self.assertIn(light_surface, dialog.styleSheet())
self.assertNotIn(dark_surface, dialog.styleSheet())
light_placeholder = dialog.palette().color(
QPalette.ColorRole.PlaceholderText
).getRgb()
light_text = dialog.palette().color(QPalette.ColorRole.Text).getRgb()
self.assertNotEqual(dark_placeholder, light_placeholder)
self.assertNotEqual(dark_text, light_text)
finally:
dialog.deleteLater()
def test_no_hardcoded_dialog_backgrounds_left(self) -> None:
offenders: list[str] = []
for path in sorted(UI_DIR.glob("*.py")):
if path.name == "theme.py":
continue # theme.py legitimately owns the surface token values
text = path.read_text(encoding="utf-8")
if "#2b2b2b" in text or "#f3f3f3" in text:
offenders.append(path.name)
self.assertEqual(offenders, [])
if __name__ == "__main__":
unittest.main()