mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add exception-translations rule to quality_scale pytest validation (#131914)
* Add exception-translations rule to quality_scale pytest validation * Adjust * Return empty dict if file is missing * Fix * Improve typing * Address comments * Update tests/components/conftest.py * Update tests/components/conftest.py * Update tests/components/conftest.py --------- Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
parent
dd57c75e64
commit
ee865d2f0f
@ -15,7 +15,7 @@ from collections.abc import (
|
||||
)
|
||||
from contextlib import asynccontextmanager, contextmanager, suppress
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from enum import Enum
|
||||
from enum import Enum, StrEnum
|
||||
import functools as ft
|
||||
from functools import lru_cache
|
||||
from io import StringIO
|
||||
@ -108,7 +108,7 @@ from homeassistant.util.json import (
|
||||
from homeassistant.util.signal_type import SignalType
|
||||
import homeassistant.util.ulid as ulid_util
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
import homeassistant.util.yaml.loader as yaml_loader
|
||||
from homeassistant.util.yaml import load_yaml_dict, loader as yaml_loader
|
||||
|
||||
from .testing_config.custom_components.test_constant_deprecation import (
|
||||
import_deprecated_constant,
|
||||
@ -122,6 +122,14 @@ CLIENT_ID = "https://example.com/app"
|
||||
CLIENT_REDIRECT_URI = "https://example.com/app/callback"
|
||||
|
||||
|
||||
class QualityScaleStatus(StrEnum):
|
||||
"""Source of core configuration."""
|
||||
|
||||
DONE = "done"
|
||||
EXEMPT = "exempt"
|
||||
TODO = "todo"
|
||||
|
||||
|
||||
async def async_get_device_automations(
|
||||
hass: HomeAssistant,
|
||||
automation_type: device_automation.DeviceAutomationType,
|
||||
@ -1832,3 +1840,22 @@ def reset_translation_cache(hass: HomeAssistant, components: list[str]) -> None:
|
||||
for loaded_components in loaded_categories.values():
|
||||
for component_to_unload in components:
|
||||
loaded_components.pop(component_to_unload, None)
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_quality_scale(integration: str) -> dict[str, QualityScaleStatus]:
|
||||
"""Load quality scale for integration."""
|
||||
quality_scale_file = pathlib.Path(
|
||||
f"homeassistant/components/{integration}/quality_scale.yaml"
|
||||
)
|
||||
if not quality_scale_file.exists():
|
||||
return {}
|
||||
raw = load_yaml_dict(quality_scale_file)
|
||||
return {
|
||||
rule: (
|
||||
QualityScaleStatus(details)
|
||||
if isinstance(details, str)
|
||||
else QualityScaleStatus(details["status"])
|
||||
)
|
||||
for rule, details in raw["rules"].items()
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ from collections.abc import AsyncGenerator, Callable, Generator
|
||||
from functools import lru_cache
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
import re
|
||||
import string
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
@ -42,6 +43,8 @@ from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.translation import async_get_translations
|
||||
from homeassistant.util import yaml
|
||||
|
||||
from tests.common import QualityScaleStatus, get_quality_scale
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.components.hassio import AddonManager
|
||||
|
||||
@ -51,6 +54,9 @@ if TYPE_CHECKING:
|
||||
from .sensor.common import MockSensor
|
||||
from .switch.common import MockSwitch
|
||||
|
||||
# Regex for accessing the integration name from the test path
|
||||
RE_REQUEST_DOMAIN = re.compile(r".*tests\/components\/([^/]+)\/.*")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=find_spec("zeroconf") is not None)
|
||||
def patch_zeroconf_multiple_catcher() -> Generator[None]:
|
||||
@ -804,12 +810,29 @@ async def _check_create_issue_translations(
|
||||
)
|
||||
|
||||
|
||||
def _get_request_quality_scale(
|
||||
request: pytest.FixtureRequest, rule: str
|
||||
) -> QualityScaleStatus:
|
||||
if not (match := RE_REQUEST_DOMAIN.match(str(request.path))):
|
||||
return QualityScaleStatus.TODO
|
||||
integration = match.groups(1)[0]
|
||||
return get_quality_scale(integration).get(rule, QualityScaleStatus.TODO)
|
||||
|
||||
|
||||
async def _check_exception_translation(
|
||||
hass: HomeAssistant,
|
||||
exception: HomeAssistantError,
|
||||
translation_errors: dict[str, str],
|
||||
request: pytest.FixtureRequest,
|
||||
) -> None:
|
||||
if exception.translation_key is None:
|
||||
if (
|
||||
_get_request_quality_scale(request, "exception-translations")
|
||||
is QualityScaleStatus.DONE
|
||||
):
|
||||
translation_errors["quality_scale"] = (
|
||||
f"Found untranslated {type(exception).__name__} exception: {exception}"
|
||||
)
|
||||
return
|
||||
await _validate_translation(
|
||||
hass,
|
||||
@ -823,13 +846,14 @@ async def _check_exception_translation(
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def check_translations(
|
||||
ignore_translations: str | list[str],
|
||||
ignore_translations: str | list[str], request: pytest.FixtureRequest
|
||||
) -> AsyncGenerator[None]:
|
||||
"""Check that translation requirements are met.
|
||||
|
||||
Current checks:
|
||||
- data entry flow results (ConfigFlow/OptionsFlow/RepairFlow)
|
||||
- issue registry entries
|
||||
- action (service) exceptions
|
||||
"""
|
||||
if not isinstance(ignore_translations, list):
|
||||
ignore_translations = [ignore_translations]
|
||||
@ -887,7 +911,9 @@ async def check_translations(
|
||||
)
|
||||
except HomeAssistantError as err:
|
||||
translation_coros.add(
|
||||
_check_exception_translation(self._hass, err, translation_errors)
|
||||
_check_exception_translation(
|
||||
self._hass, err, translation_errors, request
|
||||
)
|
||||
)
|
||||
raise
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user