Ensure config_flow abort reasons have translations (#128140)

* Ensure config_flow abort reasons have translations

* Ignore fake_integration in application_credentials

* Mark gardena_bluetooth as needs fixing

* Mark google as needs fixing

* Mark google_assistant_sdk as needs fixing

* Mark homewizard as needs fixing

* Mark homeworks as needs fixing

* Mark honeywell as needs fixing

* Mark jewish_calendar as needs fixing

* Mark lg_netcast as needs fixing

* Mark lifx as needs fixing

* Mark lyric as needs fixing

* Mark madvr as needs fixing

* Mark matter as needs fixing

* Mark melcloud as needs fixing

* Mark motioneye as needs fixing

* Mark ollama as needs fixing

* Mark philips_js as needs fixing

* Mark spotify as needs fixing

* Mark srp_energy as needs fixing

* Mark subaru as needs fixing

* Mark tplink as needs fixing

* Mark yolink as needs fixing

* Mark youtube as needs fixing

* Fix incorrect comment
This commit is contained in:
epenet 2024-10-14 16:39:10 +02:00 committed by GitHub
parent 821d9abc56
commit f41494b7cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 215 additions and 3 deletions

View File

@ -48,6 +48,18 @@ NAME = "Name"
TEST_DOMAIN = "fake_integration"
@pytest.fixture
def ignore_translations() -> list[str]:
"""Ignore specific translations.
We can ignore translations for the fake_integration we are testing with.
"""
return [
f"component.{TEST_DOMAIN}.config.abort.missing_configuration",
f"component.{TEST_DOMAIN}.config.abort.missing_credentials",
]
@pytest.fixture
async def authorization_server() -> AuthorizationServer:
"""Fixture AuthorizationServer for mock application_credentials integration."""

View File

@ -11,8 +11,16 @@ from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
from aiohasupervisor.models import Repository, StoreAddon, StoreInfo
import pytest
from homeassistant.config_entries import (
DISCOVERY_SOURCES,
SOURCE_SYSTEM,
ConfigEntriesFlowManager,
FlowResult,
)
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowHandler, FlowManager, FlowResultType
from homeassistant.helpers.translation import async_get_translations
if TYPE_CHECKING:
from homeassistant.components.hassio import AddonManager
@ -456,3 +464,87 @@ def supervisor_client() -> Generator[AsyncMock]:
),
):
yield supervisor_client
async def _ensure_translation_exists(
hass: HomeAssistant,
ignore_translations: list[str],
category: str,
component: str,
key: str,
) -> None:
"""Raise if translation doesn't exist."""
full_key = f"component.{component}.{category}.{key}"
if full_key in ignore_translations:
return
translations = await async_get_translations(hass, "en", category, [component])
if full_key in translations:
return
key_parts = key.split(".")
# Ignore step data translations if title or description exists
if (
len(key_parts) >= 3
and key_parts[0] == "step"
and key_parts[2] == "data"
and (
f"component.{component}.{category}.{key_parts[0]}.{key_parts[1]}.description"
in translations
or f"component.{component}.{category}.{key_parts[0]}.{key_parts[1]}.title"
in translations
)
):
return
raise ValueError(
f"Translation not found for {component}: `{category}.{key}`. "
f"Please add to homeassistant/components/{component}/strings.json"
)
@pytest.fixture
def ignore_translations() -> list[str]:
"""Ignore specific translations.
Override or parametrize this fixture with a fixture that returns,
a list of translation that should be ignored.
"""
return []
@pytest.fixture(autouse=True)
def check_config_translations(ignore_translations: list[str]) -> Generator[None]:
"""Ensure config_flow translations are available."""
_original = FlowManager._async_handle_step
async def _async_handle_step(
self: FlowManager, flow: FlowHandler, *args
) -> FlowResult:
result = await _original(self, flow, *args)
if isinstance(self, ConfigEntriesFlowManager):
category = "config"
component = flow.handler
else:
return result
if (
result["type"] is FlowResultType.ABORT
and flow.source != SOURCE_SYSTEM
and flow.source not in DISCOVERY_SOURCES
):
await _ensure_translation_exists(
flow.hass,
ignore_translations,
category,
component,
f"abort.{result["reason"]}",
)
return result
with patch(
"homeassistant.data_entry_flow.FlowManager._async_handle_step",
_async_handle_step,
):
yield

View File

@ -138,7 +138,7 @@
'version': 1,
})
# ---
# name: test_failed_connect
# name: test_failed_connect[component.gardena_bluetooth.config.abort.cannot_connect]
FlowResultSnapshot({
'data_schema': list([
dict({
@ -163,7 +163,7 @@
'type': <FlowResultType.FORM: 'form'>,
})
# ---
# name: test_failed_connect.1
# name: test_failed_connect[component.gardena_bluetooth.config.abort.cannot_connect].1
FlowResultSnapshot({
'data_schema': None,
'description_placeholders': dict({
@ -178,7 +178,7 @@
'type': <FlowResultType.FORM: 'form'>,
})
# ---
# name: test_failed_connect.2
# name: test_failed_connect[component.gardena_bluetooth.config.abort.cannot_connect].2
FlowResultSnapshot({
'description_placeholders': dict({
'error': 'something went wrong',

View File

@ -50,6 +50,10 @@ async def test_user_selection(
assert result == snapshot
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.gardena_bluetooth.config.abort.cannot_connect"],
)
async def test_failed_connect(
hass: HomeAssistant,
mock_client: Mock,

View File

@ -437,6 +437,10 @@ async def test_multiple_config_entries(
assert len(entries) == 2
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.google.config.abort.missing_credentials"],
)
async def test_missing_configuration(
hass: HomeAssistant,
) -> None:

View File

@ -157,6 +157,10 @@ async def test_reauth(
assert config_entry.data["token"].get("refresh_token") == "mock-refresh-token"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.google_assistant_sdk.config.abort.single_instance_allowed"],
)
@pytest.mark.usefixtures("current_request_with_host")
async def test_single_instance_allowed(
hass: HomeAssistant,

View File

@ -302,6 +302,10 @@ async def test_error_flow(
assert result["type"] is FlowResultType.CREATE_ENTRY
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.homewizard.config.abort.unsupported_api_version"],
)
@pytest.mark.parametrize(
("exception", "reason"),
[

View File

@ -235,6 +235,10 @@ async def test_user_flow_cannot_connect(
assert result["step_id"] == "user"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.homeworks.config.abort.reconfigure_successful"],
)
async def test_reconfigure_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
) -> None:
@ -322,6 +326,10 @@ async def test_reconfigure_flow_flow_duplicate(
assert result["errors"] == {"base": "duplicated_host_port"}
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.homeworks.config.abort.reconfigure_successful"],
)
async def test_reconfigure_flow_flow_no_change(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
) -> None:

View File

@ -120,6 +120,10 @@ async def test_create_option_entry(
}
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.honeywell.config.abort.reauth_successful"],
)
async def test_reauth_flow(hass: HomeAssistant) -> None:
"""Test a successful reauth flow."""

View File

@ -166,6 +166,10 @@ async def test_options_reconfigure(
)
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.jewish_calendar.config.abort.reconfigure_successful"],
)
async def test_reconfigure(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:

View File

@ -3,6 +3,8 @@
from datetime import timedelta
from unittest.mock import DEFAULT, patch
import pytest
from homeassistant import data_entry_flow
from homeassistant.components.lg_netcast.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
@ -112,6 +114,10 @@ async def test_manual_host_unsuccessful_details_response(hass: HomeAssistant) ->
assert result["reason"] == "cannot_connect"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.lg_netcast.config.abort.invalid_host"],
)
async def test_manual_host_no_unique_id_response(hass: HomeAssistant) -> None:
"""Test manual host configuration."""
with _patch_lg_netcast(no_unique_id=True):

View File

@ -101,6 +101,10 @@ async def test_discovery(hass: HomeAssistant) -> None:
assert result2["reason"] == "no_devices_found"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.lifx.config.abort.cannot_connect"],
)
async def test_discovery_but_cannot_connect(hass: HomeAssistant) -> None:
"""Test we can discover the device but we cannot connect."""
with _patch_discovery(), _patch_config_flow_try_connect(no_device=True):

View File

@ -36,6 +36,10 @@ async def mock_impl(hass: HomeAssistant) -> None:
)
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.lyric.config.abort.missing_credentials"],
)
async def test_abort_if_no_configuration(hass: HomeAssistant) -> None:
"""Check flow abort when no configuration."""
result = await hass.config_entries.flow.async_init(

View File

@ -165,6 +165,10 @@ async def test_reconfigure_flow(
mock_madvr_client.async_cancel_tasks.assert_called()
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.madvr.config.abort.set_up_new_device"],
)
async def test_reconfigure_new_device(
hass: HomeAssistant,
mock_madvr_client: AsyncMock,

View File

@ -827,6 +827,10 @@ async def test_addon_running(
assert setup_entry.call_count == 1
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.matter.config.abort.cannot_connect"],
)
@pytest.mark.parametrize(
(
"discovery_info",

View File

@ -73,6 +73,10 @@ async def test_form(hass: HomeAssistant, mock_login, mock_get_devices) -> None:
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.melcloud.config.abort.cannot_connect"],
)
@pytest.mark.parametrize(
("error", "reason"),
[(ClientError(), "cannot_connect"), (TimeoutError(), "cannot_connect")],
@ -94,6 +98,15 @@ async def test_form_errors(
assert result["reason"] == reason
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
[
[
"component.melcloud.config.abort.cannot_connect",
"component.melcloud.config.abort.invalid_auth",
],
],
)
@pytest.mark.parametrize(
("error", "message"),
[

View File

@ -7,6 +7,7 @@ from motioneye_client.client import (
MotionEyeClientInvalidAuthError,
MotionEyeClientRequestError,
)
import pytest
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
@ -390,6 +391,10 @@ async def test_hassio_ignored(hass: HomeAssistant) -> None:
assert result.get("reason") == "already_configured"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.motioneye.config.abort.already_in_progress"],
)
async def test_hassio_abort_if_already_in_progress(hass: HomeAssistant) -> None:
"""Test Supervisor discovered flow aborts if user flow in progress."""
result = await hass.config_entries.flow.async_init(

View File

@ -204,6 +204,10 @@ async def test_form_errors(hass: HomeAssistant, side_effect, error) -> None:
assert result2["errors"] == {"base": error}
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.ollama.config.abort.download_failed"],
)
async def test_download_error(hass: HomeAssistant) -> None:
"""Test we handle errors while downloading a model."""
result = await hass.config_entries.flow.async_init(

View File

@ -161,6 +161,10 @@ async def test_pairing(hass: HomeAssistant, mock_tv_pairable, mock_setup_entry)
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.philips_js.config.abort.pairing_failure"],
)
async def test_pair_request_failed(
hass: HomeAssistant, mock_tv_pairable, mock_setup_entry
) -> None:
@ -188,6 +192,10 @@ async def test_pair_request_failed(
}
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.philips_js.config.abort.pairing_failure"],
)
async def test_pair_grant_failed(
hass: HomeAssistant, mock_tv_pairable, mock_setup_entry
) -> None:

View File

@ -29,6 +29,10 @@ BLANK_ZEROCONF_INFO = zeroconf.ZeroconfServiceInfo(
)
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.spotify.config.abort.missing_credentials"],
)
async def test_abort_if_no_configuration(hass: HomeAssistant) -> None:
"""Check flow aborts when no configuration is present."""
result = await hass.config_entries.flow.async_init(

View File

@ -100,6 +100,10 @@ async def test_form_invalid_auth(
assert result["errors"] == {"base": "invalid_auth"}
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.srp_energy.config.abort.unknown"],
)
async def test_form_unknown_error(
hass: HomeAssistant,
mock_srp_energy_config_flow: MagicMock,

View File

@ -192,6 +192,10 @@ async def test_two_factor_request_success(
assert len(mock_two_factor_request.mock_calls) == 1
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.subaru.config.abort.two_factor_request_failed"],
)
async def test_two_factor_request_fail(
hass: HomeAssistant, two_factor_start_form
) -> None:

View File

@ -1348,6 +1348,10 @@ async def test_reauth_errors(
assert result3["reason"] == "reauth_successful"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.tplink.config.abort.cannot_connect"],
)
@pytest.mark.parametrize(
("error_type", "expected_flow"),
[

View File

@ -22,6 +22,10 @@ CLIENT_SECRET = "6789"
DOMAIN = "yolink"
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.yolink.config.abort.missing_credentials"],
)
async def test_abort_if_no_configuration(hass: HomeAssistant) -> None:
"""Check flow abort when no configuration."""
result = await hass.config_entries.flow.async_init(

View File

@ -210,6 +210,10 @@ async def test_flow_http_error(
)
@pytest.mark.parametrize( # Remove when translations fixed
"ignore_translations",
["component.youtube.config.abort.wrong_account"],
)
@pytest.mark.parametrize(
("fixture", "abort_reason", "placeholders", "call_count", "access_token"),
[