"""app.theme_tokens -- resolve_theme()'s fallback logic (does "classic" stay a byte-identical no-op vs. each widget kind's pre-theme-system look?) and the font table every preset resolves against. No rendering here -- see test_html_render.py for ordered_dither_regions and each widget's own test_widgets_*.py for dispatch-level "does a theme actually change the output" coverage.""" from __future__ import annotations from pathlib import Path from app import panel_style, theme_tokens def test_classic_weather_matches_historical_fixed_gradient(): """Weather's modern style never went through panel_style.THEME -- its header was always this fixed blue gradient (see html_render.py's removed ACCENT_START/ACCENT_END). "classic" must reproduce it byte-for-byte so themes are additive, not a silent regression.""" resolved = theme_tokens.resolve_theme("classic", "weather", None) assert resolved["accent_hex"] == "#1c4fd6" assert resolved["accent_hex_dark"] == "#6fa8ff" def test_classic_tasks_and_calendar_use_flat_theme_ink(): """Tasks/calendar's classic accent was always a flat single ink (no gradient) resolved through panel_style.THEME -- both ends of the "gradient" must be that same ink, not two different shades.""" tasks = theme_tokens.resolve_theme("classic", "tasks", None) calendar = theme_tokens.resolve_theme("classic", "calendar", None) assert tasks["accent_hex"] == tasks["accent_hex_dark"] assert calendar["accent_hex"] == calendar["accent_hex_dark"] from app.image_pipeline import DEFAULT_PALETTE_RGB expected_tasks = "#%02x%02x%02x" % tuple(DEFAULT_PALETTE_RGB[panel_style.THEME_TASKS]) expected_calendar = "#%02x%02x%02x" % tuple(DEFAULT_PALETTE_RGB[panel_style.THEME_CALENDAR]) assert tasks["accent_hex"] == expected_tasks assert calendar["accent_hex"] == expected_calendar def test_classic_unmapped_widget_kind_falls_back_to_black(): resolved = theme_tokens.resolve_theme("classic", "battery", None) from app.image_pipeline import DEFAULT_PALETTE_RGB assert resolved["accent_hex"] == "#%02x%02x%02x" % tuple(DEFAULT_PALETTE_RGB[panel_style.BLACK]) def test_unknown_theme_name_falls_back_to_classic(): assert theme_tokens.resolve_theme("not-a-real-theme", "weather", None) == \ theme_tokens.resolve_theme("classic", "weather", None) assert theme_tokens.resolve_theme(None, "weather", None) == \ theme_tokens.resolve_theme("classic", "weather", None) def test_every_preset_resolves_without_error_for_every_widget_kind(): widget_kinds = ["weather", "tasks", "calendar", "battery", "text", "static", "whiteboard"] for theme_name in theme_tokens.THEMES: for kind in widget_kinds: resolved = theme_tokens.resolve_theme(theme_name, kind, None) assert resolved["accent_hex"].startswith("#") and len(resolved["accent_hex"]) == 7 assert resolved["accent_hex_dark"].startswith("#") and len(resolved["accent_hex_dark"]) == 7 assert resolved["font_family"] in theme_tokens.FONT_FAMILIES assert Path(resolved["font_regular"]).exists() assert Path(resolved["font_bold"]).exists() def test_non_gradient_theme_has_equal_accent_stops(): """"moss" is configured with gradient=False -- its two CSS gradient stops must be identical (a flat fill), unlike a gradient theme's.""" resolved = theme_tokens.resolve_theme("moss", "tasks", None) assert resolved["gradient"] is False assert resolved["accent_hex"] == resolved["accent_hex_dark"] def test_gradient_theme_has_a_darker_second_stop(): resolved = theme_tokens.resolve_theme("terracotta", "tasks", None) assert resolved["gradient"] is True assert resolved["accent_hex"] == "#a84b2a" assert resolved["accent_hex_dark"] != resolved["accent_hex"] def test_font_path_covers_every_family_and_style_combination(): for family in theme_tokens.FONT_FAMILIES: for bold in (False, True): for italic in (False, True): assert theme_tokens.font_path(family, bold, italic).exists() def test_font_path_falls_back_to_default_family_for_unknown_name(): assert theme_tokens.font_path("not-a-real-family", False, False) == \ theme_tokens.font_path(theme_tokens.DEFAULT_FONT_FAMILY, False, False)