Add reconfigure step for google_travel_time (#115178)

* Add reconfigure step for google_travel_time

* Do not allow to change name

* Duplicate tests for reconfigure

* Use link for description in strings.json

* Try except else

* Extend existing config flow tests
This commit is contained in:
Kevin Stillhammer 2024-06-09 18:13:32 +02:00 committed by GitHub
parent 361c46d491
commit 93fa9e778b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 300 additions and 25 deletions

View file

@ -2,6 +2,8 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import voluptuous as vol
from homeassistant.config_entries import (
@ -49,6 +51,20 @@ from .const import (
)
from .helpers import InvalidApiKeyException, UnknownException, validate_config_entry
RECONFIGURE_SCHEMA = vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_DESTINATION): cv.string,
vol.Required(CONF_ORIGIN): cv.string,
}
)
CONFIG_SCHEMA = RECONFIGURE_SCHEMA.extend(
{
vol.Required(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
OPTIONS_SCHEMA = vol.Schema(
{
vol.Required(CONF_MODE): SelectSelector(
@ -190,29 +206,61 @@ class GoogleTravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
user_input[CONF_ORIGIN],
user_input[CONF_DESTINATION],
)
except InvalidApiKeyException:
errors["base"] = "invalid_auth"
except TimeoutError:
errors["base"] = "timeout_connect"
except UnknownException:
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(
title=user_input.get(CONF_NAME, DEFAULT_NAME),
data=user_input,
options=default_options(self.hass),
)
return self.async_show_form(
step_id="user",
data_schema=self.add_suggested_values_to_schema(CONFIG_SCHEMA, user_input),
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration."""
entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
if TYPE_CHECKING:
assert entry
errors = {}
user_input = user_input or {}
if user_input:
try:
await self.hass.async_add_executor_job(
validate_config_entry,
self.hass,
user_input[CONF_API_KEY],
user_input[CONF_ORIGIN],
user_input[CONF_DESTINATION],
)
except InvalidApiKeyException:
errors["base"] = "invalid_auth"
except TimeoutError:
errors["base"] = "timeout_connect"
except UnknownException:
errors["base"] = "cannot_connect"
else:
return self.async_update_reload_and_abort(
entry,
data=user_input,
reason="reconfigure_successful",
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(
CONF_NAME, default=user_input.get(CONF_NAME, DEFAULT_NAME)
): cv.string,
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_DESTINATION): cv.string,
vol.Required(CONF_ORIGIN): cv.string,
}
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
RECONFIGURE_SCHEMA, entry.data.copy()
),
errors=errors,
)

View file

@ -10,6 +10,14 @@
"origin": "Origin",
"destination": "Destination"
}
},
"reconfigure": {
"description": "[%key:component::google_travel_time::config::step::user::description%]",
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"origin": "[%key:component::google_travel_time::config::step::user::data::origin%]",
"destination": "[%key:component::google_travel_time::config::step::user::data::destination%]"
}
}
},
"error": {
@ -18,7 +26,8 @@
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
}
},
"options": {

View file

@ -11,3 +11,9 @@ MOCK_CONFIG = {
CONF_ORIGIN: "location1",
CONF_DESTINATION: "location2",
}
RECONFIGURE_CONFIG = {
CONF_API_KEY: "api_key2",
CONF_ORIGIN: "location3",
CONF_DESTINATION: "location4",
}

View file

@ -1,5 +1,7 @@
"""Test the Google Maps Travel Time config flow."""
from unittest.mock import patch
import pytest
from homeassistant import config_entries
@ -25,7 +27,58 @@ from homeassistant.const import CONF_API_KEY, CONF_LANGUAGE, CONF_MODE, CONF_NAM
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .const import MOCK_CONFIG
from .const import MOCK_CONFIG, RECONFIGURE_CONFIG
async def assert_common_reconfigure_steps(
hass: HomeAssistant, reconfigure_result: config_entries.ConfigFlowResult
) -> None:
"""Step through and assert the happy case reconfigure flow."""
with (
patch("homeassistant.components.google_travel_time.helpers.Client"),
patch(
"homeassistant.components.google_travel_time.helpers.distance_matrix",
return_value=None,
),
):
reconfigure_successful_result = await hass.config_entries.flow.async_configure(
reconfigure_result["flow_id"],
RECONFIGURE_CONFIG,
)
assert reconfigure_successful_result["type"] is FlowResultType.ABORT
assert reconfigure_successful_result["reason"] == "reconfigure_successful"
await hass.async_block_till_done()
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.data == RECONFIGURE_CONFIG
async def assert_common_create_steps(
hass: HomeAssistant, user_step_result: config_entries.ConfigFlowResult
) -> None:
"""Step through and assert the happy case create flow."""
with (
patch("homeassistant.components.google_travel_time.helpers.Client"),
patch(
"homeassistant.components.google_travel_time.helpers.distance_matrix",
return_value=None,
),
):
create_result = await hass.config_entries.flow.async_configure(
user_step_result["flow_id"],
MOCK_CONFIG,
)
assert create_result["type"] is FlowResultType.CREATE_ENTRY
await hass.async_block_till_done()
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.title == DEFAULT_NAME
assert entry.data == {
CONF_NAME: DEFAULT_NAME,
CONF_API_KEY: "api_key",
CONF_ORIGIN: "location1",
CONF_DESTINATION: "location2",
}
@pytest.mark.usefixtures("validate_config_entry", "bypass_setup")
@ -37,19 +90,7 @@ async def test_minimum_fields(hass: HomeAssistant) -> None:
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_CONFIG,
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == DEFAULT_NAME
assert result2["data"] == {
CONF_NAME: DEFAULT_NAME,
CONF_API_KEY: "api_key",
CONF_ORIGIN: "location1",
CONF_DESTINATION: "location2",
}
await assert_common_create_steps(hass, result)
@pytest.mark.usefixtures("invalidate_config_entry")
@ -67,6 +108,7 @@ async def test_invalid_config_entry(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_common_create_steps(hass, result2)
@pytest.mark.usefixtures("invalid_api_key")
@ -84,6 +126,7 @@ async def test_invalid_api_key(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "invalid_auth"}
await assert_common_create_steps(hass, result2)
@pytest.mark.usefixtures("transport_error")
@ -101,6 +144,7 @@ async def test_transport_error(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_common_create_steps(hass, result2)
@pytest.mark.usefixtures("timeout")
@ -118,6 +162,7 @@ async def test_timeout(hass: HomeAssistant) -> None:
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "timeout_connect"}
await assert_common_create_steps(hass, result2)
async def test_malformed_api_key(hass: HomeAssistant) -> None:
@ -136,6 +181,173 @@ async def test_malformed_api_key(hass: HomeAssistant) -> None:
assert result2["errors"] == {"base": "invalid_auth"}
@pytest.mark.parametrize(
("data", "options"),
[
(
MOCK_CONFIG,
{
CONF_MODE: "driving",
CONF_UNITS: UNITS_IMPERIAL,
},
)
],
)
@pytest.mark.usefixtures("validate_config_entry", "bypass_setup")
async def test_reconfigure(hass: HomeAssistant, mock_config) -> None:
"""Test reconfigure flow."""
reconfigure_result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"entry_id": mock_config.entry_id,
},
)
assert reconfigure_result["type"] is FlowResultType.FORM
assert reconfigure_result["step_id"] == "reconfigure"
await assert_common_reconfigure_steps(hass, reconfigure_result)
@pytest.mark.parametrize(
("data", "options"),
[
(
MOCK_CONFIG,
{
CONF_MODE: "driving",
CONF_UNITS: UNITS_IMPERIAL,
},
)
],
)
@pytest.mark.usefixtures("invalidate_config_entry")
async def test_reconfigure_invalid_config_entry(
hass: HomeAssistant, mock_config
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"entry_id": mock_config.entry_id,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
RECONFIGURE_CONFIG,
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_common_reconfigure_steps(hass, result2)
@pytest.mark.parametrize(
("data", "options"),
[
(
MOCK_CONFIG,
{
CONF_MODE: "driving",
CONF_UNITS: UNITS_IMPERIAL,
},
)
],
)
@pytest.mark.usefixtures("invalid_api_key")
async def test_reconfigure_invalid_api_key(hass: HomeAssistant, mock_config) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"entry_id": mock_config.entry_id,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
RECONFIGURE_CONFIG,
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "invalid_auth"}
await assert_common_reconfigure_steps(hass, result2)
@pytest.mark.parametrize(
("data", "options"),
[
(
MOCK_CONFIG,
{
CONF_MODE: "driving",
CONF_UNITS: UNITS_IMPERIAL,
},
)
],
)
@pytest.mark.usefixtures("transport_error")
async def test_reconfigure_transport_error(hass: HomeAssistant, mock_config) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"entry_id": mock_config.entry_id,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
RECONFIGURE_CONFIG,
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "cannot_connect"}
await assert_common_reconfigure_steps(hass, result2)
@pytest.mark.parametrize(
("data", "options"),
[
(
MOCK_CONFIG,
{
CONF_MODE: "driving",
CONF_UNITS: UNITS_IMPERIAL,
},
)
],
)
@pytest.mark.usefixtures("timeout")
async def test_reconfigure_timeout(hass: HomeAssistant, mock_config) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"entry_id": mock_config.entry_id,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
RECONFIGURE_CONFIG,
)
assert result2["type"] is FlowResultType.FORM
assert result2["errors"] == {"base": "timeout_connect"}
await assert_common_reconfigure_steps(hass, result2)
@pytest.mark.parametrize(
("data", "options"),
[