Compare commits

..

6 commits

Author SHA1 Message Date
epenet
f4d1eafb20 Ignore repairs 2024-11-14 15:30:21 +00:00
epenet
b79fd62a14 Ignore test translations 2024-11-14 15:30:21 +00:00
epenet
26685cd75c Adjust comment 2024-11-14 15:30:21 +00:00
epenet
1539f19a10 Fix reference to task 2024-11-14 15:30:21 +00:00
epenet
cecf5d02ae description is only required on non-fixable flows 2024-11-14 15:30:21 +00:00
epenet
4ca3d7a1f8 Add translation checks for issue registry 2024-11-14 15:30:21 +00:00
9 changed files with 208 additions and 59 deletions

View file

@ -515,7 +515,7 @@ async def async_from_config_dict(
issue_registry.async_create_issue(
hass,
core.DOMAIN,
f"python_version_{required_python_version}",
"python_version",
is_fixable=False,
severity=issue_registry.IssueSeverity.WARNING,
breaks_in_ha_version=REQUIRED_NEXT_PYTHON_HA_RELEASE,

View file

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_buttons[button.lunar_ddeeff_reset_timer-entry]
# name: test_buttons[entry_button_reset_timer]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -32,20 +32,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_buttons[button.lunar_ddeeff_reset_timer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Reset timer',
}),
'context': <ANY>,
'entity_id': 'button.lunar_ddeeff_reset_timer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[button.lunar_ddeeff_start_stop_timer-entry]
# name: test_buttons[entry_button_start_stop_timer]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -78,20 +65,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_buttons[button.lunar_ddeeff_start_stop_timer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Start/stop timer',
}),
'context': <ANY>,
'entity_id': 'button.lunar_ddeeff_start_stop_timer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[button.lunar_ddeeff_tare-entry]
# name: test_buttons[entry_button_tare]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -124,7 +98,33 @@
'unit_of_measurement': None,
})
# ---
# name: test_buttons[button.lunar_ddeeff_tare-state]
# name: test_buttons[state_button_reset_timer]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Reset timer',
}),
'context': <ANY>,
'entity_id': 'button.lunar_ddeeff_reset_timer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[state_button_start_stop_timer]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Start/stop timer',
}),
'context': <ANY>,
'entity_id': 'button.lunar_ddeeff_start_stop_timer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[state_button_tare]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Tare',

View file

@ -1,24 +1,21 @@
"""Tests for the acaia buttons."""
from datetime import timedelta
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.const import (
ATTR_ENTITY_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
Platform,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import async_fire_time_changed
pytestmark = pytest.mark.usefixtures("init_integration")
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
BUTTONS = (
"tare",
@ -31,25 +28,24 @@ async def test_buttons(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_scale: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the acaia buttons."""
for button in BUTTONS:
state = hass.states.get(f"button.lunar_ddeeff_{button}")
assert state
assert state == snapshot(name=f"state_button_{button}")
with patch("homeassistant.components.acaia.PLATFORMS", [Platform.BUTTON]):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
entry = entity_registry.async_get(state.entity_id)
assert entry
assert entry == snapshot(name=f"entry_button_{button}")
async def test_button_presses(
hass: HomeAssistant,
mock_scale: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the acaia button presses."""
await setup_integration(hass, mock_config_entry)
for button in BUTTONS:
await hass.services.async_call(
BUTTON_DOMAIN,
@ -67,13 +63,10 @@ async def test_button_presses(
async def test_buttons_unavailable_on_disconnected_scale(
hass: HomeAssistant,
mock_scale: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the acaia buttons are unavailable when the scale is disconnected."""
await setup_integration(hass, mock_config_entry)
for button in BUTTONS:
state = hass.states.get(f"button.lunar_ddeeff_{button}")
assert state

View file

@ -2,7 +2,8 @@
from __future__ import annotations
from collections.abc import Callable, Generator
import asyncio
from collections.abc import AsyncGenerator, Callable, Generator
from importlib.util import find_spec
from pathlib import Path
import string
@ -32,6 +33,7 @@ from homeassistant.data_entry_flow import (
FlowManager,
FlowResultType,
)
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.translation import async_get_translations
if TYPE_CHECKING:
@ -670,20 +672,53 @@ async def _check_config_flow_result_translations(
)
async def _check_create_issue_translations(
issue_registry: ir.IssueRegistry,
result: ir.IssueEntry,
ignore_translations: dict[str, str],
) -> None:
if result.translation_key is None:
return
await _validate_translation(
issue_registry.hass,
ignore_translations,
"issues",
result.domain,
f"{result.translation_key}.title",
result.translation_placeholders,
)
if result.is_fixable:
return
await _validate_translation(
issue_registry.hass,
ignore_translations,
"issues",
result.domain,
f"{result.translation_key}.description",
result.translation_placeholders,
)
@pytest.fixture(autouse=True)
def check_translations(ignore_translations: str | list[str]) -> Generator[None]:
async def check_translations(
ignore_translations: str | list[str],
) -> AsyncGenerator[None]:
"""Check that translation requirements are met.
Current checks:
- data entry flow results (ConfigFlow/OptionsFlow)
- issue registry entries
"""
if not isinstance(ignore_translations, list):
ignore_translations = [ignore_translations]
_ignore_translations = {k: "unused" for k in ignore_translations}
translation_tasks = set[asyncio.Task[None]]()
# Keep reference to original functions
_original_flow_manager_async_handle_step = FlowManager._async_handle_step
_original_issue_registry_async_create_issue = ir.IssueRegistry.async_get_or_create
# Prepare override functions
async def _flow_manager_async_handle_step(
@ -695,13 +730,36 @@ def check_translations(ignore_translations: str | list[str]) -> Generator[None]:
)
return result
def _issue_registry_async_create_issue(
self: ir.IssueRegistry, domain: str, issue_id: str, *args, **kwargs
) -> None:
result = _original_issue_registry_async_create_issue(
self, domain, issue_id, *args, **kwargs
)
translation_tasks.add(
self.hass.async_create_task_internal(
_check_create_issue_translations(self, result, _ignore_translations),
"Check create_issue translations",
eager_start=True,
)
)
return result
# Use override functions
with patch(
"homeassistant.data_entry_flow.FlowManager._async_handle_step",
_flow_manager_async_handle_step,
with (
patch(
"homeassistant.data_entry_flow.FlowManager._async_handle_step",
_flow_manager_async_handle_step,
),
patch(
"homeassistant.helpers.issue_registry.IssueRegistry.async_get_or_create",
_issue_registry_async_create_issue,
),
):
yield
await asyncio.gather(*translation_tasks)
# Run final checks
unused_ignore = [k for k, v in _ignore_translations.items() if v == "unused"]
if unused_ignore:

View file

@ -21,6 +21,16 @@ from tests.common import mock_platform
from tests.typing import WebSocketGenerator
@pytest.mark.parametrize(
"ignore_translations",
[
[
"component.test.issues.even_worse.title",
"component.test.issues.even_worse.description",
"component.test.issues.abc_123.title",
]
],
)
@pytest.mark.freeze_time("2022-07-19 07:53:05")
async def test_create_update_issue(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
@ -160,6 +170,14 @@ async def test_create_issue_invalid_version(
assert msg["result"] == {"issues": []}
@pytest.mark.parametrize(
"ignore_translations",
[
[
"component.test.issues.abc_123.title",
]
],
)
@pytest.mark.freeze_time("2022-07-19 07:53:05")
async def test_ignore_issue(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
@ -329,6 +347,10 @@ async def test_ignore_issue(
}
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
@pytest.mark.freeze_time("2022-07-19 07:53:05")
async def test_delete_issue(
hass: HomeAssistant,
@ -483,6 +505,10 @@ async def test_non_compliant_platform(
assert list(hass.data[DOMAIN]["platforms"].keys()) == ["fake_integration"]
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
@pytest.mark.freeze_time("2022-07-21 08:22:00")
async def test_sync_methods(
hass: HomeAssistant,

View file

@ -151,6 +151,10 @@ async def mock_repairs_integration(hass: HomeAssistant) -> None:
)
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
async def test_dismiss_issue(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
@ -234,6 +238,10 @@ async def test_dismiss_issue(
}
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
async def test_fix_non_existing_issue(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
@ -281,10 +289,20 @@ async def test_fix_non_existing_issue(
@pytest.mark.parametrize(
("domain", "step", "description_placeholders"),
("domain", "step", "description_placeholders", "ignore_translations"),
[
("fake_integration", "custom_step", None),
("fake_integration_default_handler", "confirm", {"abc": "123"}),
(
"fake_integration",
"custom_step",
None,
["component.fake_integration.issues.abc_123.title"],
),
(
"fake_integration_default_handler",
"confirm",
{"abc": "123"},
["component.fake_integration_default_handler.issues.abc_123.title"],
),
],
)
async def test_fix_issue(
@ -380,6 +398,10 @@ async def test_fix_issue_unauth(
assert resp.status == HTTPStatus.UNAUTHORIZED
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
async def test_get_progress_unauth(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
@ -411,6 +433,10 @@ async def test_get_progress_unauth(
assert resp.status == HTTPStatus.UNAUTHORIZED
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
async def test_step_unauth(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
@ -442,6 +468,16 @@ async def test_step_unauth(
assert resp.status == HTTPStatus.UNAUTHORIZED
@pytest.mark.parametrize(
"ignore_translations",
[
[
"component.test.issues.even_worse.title",
"component.test.issues.even_worse.description",
"component.test.issues.abc_123.title",
]
],
)
@pytest.mark.freeze_time("2022-07-19 07:53:05")
async def test_list_issues(
hass: HomeAssistant,
@ -533,6 +569,10 @@ async def test_list_issues(
}
@pytest.mark.parametrize(
"ignore_translations",
["component.fake_integration.issues.abc_123.title"],
)
async def test_fix_issue_aborted(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
@ -594,6 +634,16 @@ async def test_fix_issue_aborted(
assert msg["result"]["issues"][0] == first_issue
@pytest.mark.parametrize(
"ignore_translations",
[
[
"component.test.issues.abc_123.title",
"component.test.issues.even_worse.title",
"component.test.issues.even_worse.description",
]
],
)
@pytest.mark.freeze_time("2022-07-19 07:53:05")
async def test_get_issue_data(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator

View file

@ -5432,6 +5432,17 @@ async def test_exclude_attributes(hass: HomeAssistant) -> None:
assert ATTR_FRIENDLY_NAME in states[0].attributes
@pytest.mark.parametrize(
"ignore_translations",
[
[
"component.test.issues..title",
"component.test.issues..description",
"component.sensor.issues..title",
"component.sensor.issues..description",
]
],
)
async def test_clean_up_repairs(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:

View file

@ -2,6 +2,8 @@
from __future__ import annotations
import pytest
from homeassistant.components.workday.const import CONF_REMOVE_HOLIDAYS, DOMAIN
from homeassistant.const import CONF_COUNTRY
from homeassistant.core import HomeAssistant
@ -427,6 +429,10 @@ async def test_bad_date_holiday(
assert issue
@pytest.mark.parametrize(
"ignore_translations",
["component.workday.issues.issue_1.title"],
)
async def test_other_fixable_issues(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,

View file

@ -3,6 +3,7 @@
from copy import deepcopy
from unittest.mock import patch
import pytest
from zwave_js_server.event import Event
from zwave_js_server.model.node import Node
@ -179,6 +180,10 @@ async def test_device_config_file_changed_ignore_step(
assert msg["result"]["issues"][0].get("dismissed_version") is not None
@pytest.mark.parametrize(
"ignore_translations",
["component.zwave_js.issues.invalid_issue.title"],
)
async def test_invalid_issue(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,