128 lines
5.1 KiB
Python
128 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from govoplan_core.core.appearance import (
|
|
normalize_appearance_overrides,
|
|
resolve_effective_appearance,
|
|
update_appearance_custom_overrides_policy,
|
|
update_appearance_settings,
|
|
)
|
|
|
|
|
|
def _overrides() -> dict[str, object]:
|
|
return {
|
|
"schema_version": "1",
|
|
"light": {
|
|
"accent": "#245f91", "accent_foreground": "#ffffff",
|
|
"surface": "#ffffff", "surface_foreground": "#303135",
|
|
"success": "#d8eee8", "success_foreground": "#315f55",
|
|
"info": "#dce9f3", "info_foreground": "#294a61",
|
|
"warning": "#ffe1a3", "warning_foreground": "#593700",
|
|
"danger": "#f8d1cc", "danger_foreground": "#873c35",
|
|
},
|
|
"dark": {
|
|
"accent": "#7ea6c5", "accent_foreground": "#242424",
|
|
"surface": "#262724", "surface_foreground": "#f1f1f1",
|
|
"success": "#24473f", "success_foreground": "#d8eee8",
|
|
"info": "#243d4e", "info_foreground": "#dce9f3",
|
|
"warning": "#5a431f", "warning_foreground": "#ffe1a3",
|
|
"danger": "#4f2d2a", "danger_foreground": "#f8d1cc",
|
|
},
|
|
}
|
|
|
|
|
|
def test_appearance_precedence_and_inheritance() -> None:
|
|
decision = resolve_effective_appearance(
|
|
system_settings={"appearance": {"default_palette": "civic_blue"}},
|
|
tenant_settings={"appearance": {"default_palette": "forest"}},
|
|
user_settings={"ui": {"palette": "plum"}},
|
|
)
|
|
assert (decision.palette, decision.source, decision.locked) == ("plum", "user", False)
|
|
|
|
inherited = resolve_effective_appearance(
|
|
system_settings={"appearance": {"default_palette": "civic_blue"}},
|
|
tenant_settings={"appearance": {"default_palette": "forest"}},
|
|
user_settings={"ui": {"palette": None}},
|
|
)
|
|
assert (inherited.palette, inherited.source) == ("forest", "tenant")
|
|
|
|
|
|
def test_system_lock_wins_and_invalid_values_fail_safe() -> None:
|
|
decision = resolve_effective_appearance(
|
|
system_settings={"appearance": {"default_palette": "civic_blue", "palette_locked": True}},
|
|
tenant_settings={"appearance": {"default_palette": "forest", "palette_locked": True}},
|
|
user_settings={"ui": {"palette": "plum"}},
|
|
)
|
|
assert decision.as_dict() == {
|
|
"palette": "civic_blue",
|
|
"source": "system_lock",
|
|
"locked": True,
|
|
"system_default_palette": "civic_blue",
|
|
"tenant_default_palette": "forest",
|
|
"inherited_palette": "civic_blue",
|
|
"custom_overrides": None,
|
|
"custom_overrides_allowed": False,
|
|
}
|
|
fallback = resolve_effective_appearance(
|
|
system_settings={"appearance": {"default_palette": "unsafe"}},
|
|
tenant_settings={},
|
|
user_settings={"ui": {"palette": "unknown"}},
|
|
)
|
|
assert (fallback.palette, fallback.source) == ("default", "system")
|
|
|
|
|
|
def test_appearance_settings_reset_without_touching_neighbors() -> None:
|
|
configured = update_appearance_settings(
|
|
{"neighbor": {"kept": True}},
|
|
default_palette="forest",
|
|
palette_locked=True,
|
|
)
|
|
assert configured["neighbor"] == {"kept": True}
|
|
assert configured["appearance"] == {"default_palette": "forest", "palette_locked": True}
|
|
assert update_appearance_settings(configured, default_palette=None, palette_locked=False) == {
|
|
"neighbor": {"kept": True}
|
|
}
|
|
|
|
|
|
def test_custom_overrides_require_system_and_tenant_policy_and_validate_both_modes() -> None:
|
|
document = _overrides()
|
|
normalized = normalize_appearance_overrides(document)
|
|
assert normalized == document
|
|
decision = resolve_effective_appearance(
|
|
system_settings={"appearance": {"allow_custom_overrides": True}},
|
|
tenant_settings={"appearance": {"allow_custom_overrides": True}},
|
|
user_settings={"ui": {"appearance_overrides": document}},
|
|
)
|
|
assert decision.custom_overrides_allowed is True
|
|
assert decision.custom_overrides == document
|
|
|
|
blocked = resolve_effective_appearance(
|
|
system_settings={"appearance": {"allow_custom_overrides": True}},
|
|
tenant_settings={"appearance": {"allow_custom_overrides": False}},
|
|
user_settings={"ui": {"appearance_overrides": document}},
|
|
)
|
|
assert blocked.custom_overrides_allowed is False
|
|
assert blocked.custom_overrides is None
|
|
|
|
invalid = _overrides()
|
|
invalid["dark"]["danger"] = invalid["dark"]["warning"] # type: ignore[index]
|
|
with pytest.raises(ValueError, match="visibly distinct"):
|
|
normalize_appearance_overrides(invalid)
|
|
|
|
low_contrast = _overrides()
|
|
low_contrast["light"]["accent_foreground"] = "#245f91" # type: ignore[index]
|
|
with pytest.raises(ValueError, match="WCAG AA"):
|
|
normalize_appearance_overrides(low_contrast)
|
|
|
|
|
|
def test_custom_override_policy_update_preserves_neighboring_appearance_settings() -> None:
|
|
configured = update_appearance_custom_overrides_policy(
|
|
{"appearance": {"default_palette": "forest"}},
|
|
allowed=True,
|
|
)
|
|
assert configured == {"appearance": {"default_palette": "forest", "allow_custom_overrides": True}}
|
|
assert update_appearance_custom_overrides_policy(configured, allowed=None) == {
|
|
"appearance": {"default_palette": "forest"}
|
|
}
|