Catch more invalid themes in validation (#151719)

This commit is contained in:
karwosts
2025-09-05 11:09:33 -07:00
committed by GitHub
parent 1728c577f7
commit 8ecf5a98a5
2 changed files with 66 additions and 20 deletions

View File

@@ -75,39 +75,29 @@ PRIMARY_COLOR = "primary-color"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
EXTENDED_THEME_SCHEMA = vol.Schema( THEME_SCHEMA = vol.Schema(
{ {
# Theme variables that apply to all modes # Theme variables that apply to all modes
cv.string: cv.string, cv.string: cv.string,
# Mode specific theme variables # Mode specific theme variables
vol.Optional(CONF_THEMES_MODES): vol.Schema( vol.Optional(CONF_THEMES_MODES): vol.All(
{ {
vol.Optional(CONF_THEMES_LIGHT): vol.Schema({cv.string: cv.string}), vol.Optional(CONF_THEMES_LIGHT): vol.Schema({cv.string: cv.string}),
vol.Optional(CONF_THEMES_DARK): vol.Schema({cv.string: cv.string}), vol.Optional(CONF_THEMES_DARK): vol.Schema({cv.string: cv.string}),
} },
cv.has_at_least_one_key(CONF_THEMES_LIGHT, CONF_THEMES_DARK),
), ),
} }
) )
THEME_SCHEMA = vol.Schema( THEMES_SCHEMA = vol.Schema({cv.string: THEME_SCHEMA})
{
cv.string: (
vol.Any(
# Legacy theme scheme
{cv.string: cv.string},
# New extended schema with mode support
EXTENDED_THEME_SCHEMA,
)
)
}
)
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: vol.Schema( DOMAIN: vol.Schema(
{ {
vol.Optional(CONF_FRONTEND_REPO): cv.isdir, vol.Optional(CONF_FRONTEND_REPO): cv.isdir,
vol.Optional(CONF_THEMES): THEME_SCHEMA, vol.Optional(CONF_THEMES): THEMES_SCHEMA,
vol.Optional(CONF_EXTRA_MODULE_URL): vol.All( vol.Optional(CONF_EXTRA_MODULE_URL): vol.All(
cv.ensure_list, [cv.string] cv.ensure_list, [cv.string]
), ),
@@ -546,7 +536,7 @@ async def _async_setup_themes(
new_themes = config.get(DOMAIN, {}).get(CONF_THEMES, {}) new_themes = config.get(DOMAIN, {}).get(CONF_THEMES, {})
try: try:
THEME_SCHEMA(new_themes) THEMES_SCHEMA(new_themes)
except vol.Invalid as err: except vol.Invalid as err:
raise HomeAssistantError(f"Failed to reload themes: {err}") from err raise HomeAssistantError(f"Failed to reload themes: {err}") from err

View File

@@ -410,8 +410,64 @@ async def test_themes_reload_themes(
@pytest.mark.usefixtures("frontend") @pytest.mark.usefixtures("frontend")
@pytest.mark.parametrize(
("invalid_theme", "error"),
[
(
{
"invalid0": "blue",
},
"expected a dictionary",
),
(
{
"invalid1": {
"primary-color": "black",
"modes": "light:{} dark:{}",
}
},
"expected a dictionary.*modes",
),
(
{
"invalid2": None,
},
"expected a dictionary",
),
(
{
"invalid3": {
"primary-color": "black",
"modes": {},
}
},
"at least one of light, dark.*modes",
),
(
{
"invalid4": {
"primary-color": "black",
"modes": None,
}
},
"expected a dictionary.*modes",
),
(
{
"invalid5": {
"primary-color": "black",
"modes": {"light": {}, "dank": {}},
}
},
"extra keys not allowed.*dank",
),
],
)
async def test_themes_reload_invalid( async def test_themes_reload_invalid(
hass: HomeAssistant, themes_ws_client: MockHAClientWebSocket hass: HomeAssistant,
themes_ws_client: MockHAClientWebSocket,
invalid_theme: dict,
error: str,
) -> None: ) -> None:
"""Test frontend.reload_themes service with an invalid theme.""" """Test frontend.reload_themes service with an invalid theme."""
@@ -424,9 +480,9 @@ async def test_themes_reload_invalid(
with ( with (
patch( patch(
"homeassistant.components.frontend.async_hass_config_yaml", "homeassistant.components.frontend.async_hass_config_yaml",
return_value={DOMAIN: {CONF_THEMES: {"sad": "blue"}}}, return_value={DOMAIN: {CONF_THEMES: invalid_theme}},
), ),
pytest.raises(HomeAssistantError, match="Failed to reload themes"), pytest.raises(HomeAssistantError, match=rf"Failed to reload themes.*{error}"),
): ):
await hass.services.async_call(DOMAIN, "reload_themes", blocking=True) await hass.services.async_call(DOMAIN, "reload_themes", blocking=True)