mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +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 contextlib import asynccontextmanager, contextmanager, suppress
|
||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
import functools as ft
|
import functools as ft
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@ -108,7 +108,7 @@ from homeassistant.util.json import (
|
|||||||
from homeassistant.util.signal_type import SignalType
|
from homeassistant.util.signal_type import SignalType
|
||||||
import homeassistant.util.ulid as ulid_util
|
import homeassistant.util.ulid as ulid_util
|
||||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
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 (
|
from .testing_config.custom_components.test_constant_deprecation import (
|
||||||
import_deprecated_constant,
|
import_deprecated_constant,
|
||||||
@ -122,6 +122,14 @@ CLIENT_ID = "https://example.com/app"
|
|||||||
CLIENT_REDIRECT_URI = "https://example.com/app/callback"
|
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(
|
async def async_get_device_automations(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
automation_type: device_automation.DeviceAutomationType,
|
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 loaded_components in loaded_categories.values():
|
||||||
for component_to_unload in components:
|
for component_to_unload in components:
|
||||||
loaded_components.pop(component_to_unload, None)
|
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 functools import lru_cache
|
||||||
from importlib.util import find_spec
|
from importlib.util import find_spec
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
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.helpers.translation import async_get_translations
|
||||||
from homeassistant.util import yaml
|
from homeassistant.util import yaml
|
||||||
|
|
||||||
|
from tests.common import QualityScaleStatus, get_quality_scale
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from homeassistant.components.hassio import AddonManager
|
from homeassistant.components.hassio import AddonManager
|
||||||
|
|
||||||
@ -51,6 +54,9 @@ if TYPE_CHECKING:
|
|||||||
from .sensor.common import MockSensor
|
from .sensor.common import MockSensor
|
||||||
from .switch.common import MockSwitch
|
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)
|
@pytest.fixture(scope="session", autouse=find_spec("zeroconf") is not None)
|
||||||
def patch_zeroconf_multiple_catcher() -> Generator[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(
|
async def _check_exception_translation(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
exception: HomeAssistantError,
|
exception: HomeAssistantError,
|
||||||
translation_errors: dict[str, str],
|
translation_errors: dict[str, str],
|
||||||
|
request: pytest.FixtureRequest,
|
||||||
) -> None:
|
) -> None:
|
||||||
if exception.translation_key is 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
|
return
|
||||||
await _validate_translation(
|
await _validate_translation(
|
||||||
hass,
|
hass,
|
||||||
@ -823,13 +846,14 @@ async def _check_exception_translation(
|
|||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
async def check_translations(
|
async def check_translations(
|
||||||
ignore_translations: str | list[str],
|
ignore_translations: str | list[str], request: pytest.FixtureRequest
|
||||||
) -> AsyncGenerator[None]:
|
) -> AsyncGenerator[None]:
|
||||||
"""Check that translation requirements are met.
|
"""Check that translation requirements are met.
|
||||||
|
|
||||||
Current checks:
|
Current checks:
|
||||||
- data entry flow results (ConfigFlow/OptionsFlow/RepairFlow)
|
- data entry flow results (ConfigFlow/OptionsFlow/RepairFlow)
|
||||||
- issue registry entries
|
- issue registry entries
|
||||||
|
- action (service) exceptions
|
||||||
"""
|
"""
|
||||||
if not isinstance(ignore_translations, list):
|
if not isinstance(ignore_translations, list):
|
||||||
ignore_translations = [ignore_translations]
|
ignore_translations = [ignore_translations]
|
||||||
@ -887,7 +911,9 @@ async def check_translations(
|
|||||||
)
|
)
|
||||||
except HomeAssistantError as err:
|
except HomeAssistantError as err:
|
||||||
translation_coros.add(
|
translation_coros.add(
|
||||||
_check_exception_translation(self._hass, err, translation_errors)
|
_check_exception_translation(
|
||||||
|
self._hass, err, translation_errors, request
|
||||||
|
)
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user