Ignore incorrect themes (#151794)

This commit is contained in:
Artur Pragacz
2025-09-06 13:49:03 +02:00
committed by GitHub
parent a328b23437
commit 3187506eb9
2 changed files with 61 additions and 11 deletions

View File

@@ -38,6 +38,8 @@ from homeassistant.util.hass_dict import HassKey
from .storage import async_setup_frontend_storage
_LOGGER = logging.getLogger(__name__)
DOMAIN = "frontend"
CONF_THEMES = "themes"
CONF_THEMES_MODES = "modes"
@@ -73,7 +75,23 @@ VALUE_NO_THEME = "none"
PRIMARY_COLOR = "primary-color"
_LOGGER = logging.getLogger(__name__)
LEGACY_THEME_SCHEMA = vol.Any(
# Legacy theme scheme
{cv.string: cv.string},
# New extended schema with mode support
{
# Theme variables that apply to all modes
cv.string: cv.string,
# Mode specific theme variables
vol.Optional(CONF_THEMES_MODES): vol.Schema(
{
vol.Optional(CONF_THEMES_LIGHT): vol.Schema({cv.string: cv.string}),
vol.Optional(CONF_THEMES_DARK): vol.Schema({cv.string: cv.string}),
}
),
},
)
THEME_SCHEMA = vol.Schema(
{
@@ -90,14 +108,28 @@ THEME_SCHEMA = vol.Schema(
}
)
THEMES_SCHEMA = vol.Schema({cv.string: THEME_SCHEMA})
def _validate_themes(themes: dict) -> dict[str, Any]:
"""Validate themes."""
validated_themes = {}
for theme_name, theme in themes.items():
theme_name = cv.string(theme_name)
LEGACY_THEME_SCHEMA(theme)
try:
validated_themes[theme_name] = THEME_SCHEMA(theme)
except vol.Invalid as err:
_LOGGER.error("Theme %s is invalid: %s", theme_name, err)
return validated_themes
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_FRONTEND_REPO): cv.isdir,
vol.Optional(CONF_THEMES): THEMES_SCHEMA,
vol.Optional(CONF_THEMES): vol.All(dict, _validate_themes),
vol.Optional(CONF_EXTRA_MODULE_URL): vol.All(
cv.ensure_list, [cv.string]
),
@@ -536,7 +568,7 @@ async def _async_setup_themes(
new_themes = config.get(DOMAIN, {}).get(CONF_THEMES, {})
try:
THEMES_SCHEMA(new_themes)
new_themes = _validate_themes(new_themes)
except vol.Invalid as err:
raise HomeAssistantError(f"Failed to reload themes: {err}") from err

View File

@@ -1,6 +1,7 @@
"""The tests for Home Assistant frontend."""
from collections.abc import Generator
from contextlib import nullcontext
from http import HTTPStatus
from pathlib import Path
import re
@@ -411,13 +412,14 @@ async def test_themes_reload_themes(
@pytest.mark.usefixtures("frontend")
@pytest.mark.parametrize(
("invalid_theme", "error"),
("invalid_theme", "error", "log"),
[
(
{
"invalid0": "blue",
},
"expected a dictionary",
None,
),
(
{
@@ -426,13 +428,15 @@ async def test_themes_reload_themes(
"modes": "light:{} dark:{}",
}
},
"expected a dictionary.*modes",
None,
"expected a dictionary",
),
(
{
"invalid2": None,
},
"expected a dictionary",
None,
),
(
{
@@ -441,7 +445,8 @@ async def test_themes_reload_themes(
"modes": {},
}
},
"at least one of light, dark.*modes",
None,
"must contain at least one of light, dark",
),
(
{
@@ -450,7 +455,8 @@ async def test_themes_reload_themes(
"modes": None,
}
},
"expected a dictionary.*modes",
"string value is None for dictionary value",
None,
),
(
{
@@ -460,6 +466,7 @@ async def test_themes_reload_themes(
}
},
"extra keys not allowed.*dank",
None,
),
],
)
@@ -467,7 +474,9 @@ async def test_themes_reload_invalid(
hass: HomeAssistant,
themes_ws_client: MockHAClientWebSocket,
invalid_theme: dict,
error: str,
error: str | None,
log: str | None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test frontend.reload_themes service with an invalid theme."""
@@ -482,15 +491,24 @@ async def test_themes_reload_invalid(
"homeassistant.components.frontend.async_hass_config_yaml",
return_value={DOMAIN: {CONF_THEMES: invalid_theme}},
),
pytest.raises(HomeAssistantError, match=rf"Failed to reload themes.*{error}"),
pytest.raises(HomeAssistantError, match=rf"Failed to reload themes.*{error}")
if error is not None
else nullcontext(),
):
await hass.services.async_call(DOMAIN, "reload_themes", blocking=True)
if log is not None:
assert log in caplog.text
await themes_ws_client.send_json({"id": 5, "type": "frontend/get_themes"})
msg = await themes_ws_client.receive_json()
assert msg["result"]["themes"] == {"happy": {"primary-color": "pink"}}
expected_themes = {"happy": {"primary-color": "pink"}}
if error is None:
expected_themes = {}
assert msg["result"]["themes"] == expected_themes
assert msg["result"]["default_theme"] == "default"