Add config flow support to google_travel_time (#43509)
* add config flow support to google_travel_time * fix bugs and add strings * fix import and add new test * address comments in #43419 since this is a similar PR * fix default name and test * add unique ID and device info * fix test * feedback from waze PR * continue incorporating feedback from waze PR * final fixes and update tests * call update in lambda * Update homeassistant/components/google_travel_time/sensor.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * additional fixes * validate config entry data during config flow and config entry setup * don't store entity * patch dependency instead of HA code * fixes * improve tests by moving all patching to fixtures * use self.hass instead of setting self._hass * invert if * remove unnecessary else Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
bc5d828554
commit
aae0ccc588
14 changed files with 952 additions and 187 deletions
1
tests/components/google_travel_time/__init__.py
Normal file
1
tests/components/google_travel_time/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Tests for the Google Maps Travel Time integration."""
|
59
tests/components/google_travel_time/conftest.py
Normal file
59
tests/components/google_travel_time/conftest.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
"""Fixtures for Google Time Travel tests."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from googlemaps.exceptions import ApiError
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(name="skip_notifications", autouse=True)
|
||||
def skip_notifications_fixture():
|
||||
"""Skip notification calls."""
|
||||
with patch("homeassistant.components.persistent_notification.async_create"), patch(
|
||||
"homeassistant.components.persistent_notification.async_dismiss"
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="validate_config_entry")
|
||||
def validate_config_entry_fixture():
|
||||
"""Return valid config entry."""
|
||||
with patch(
|
||||
"homeassistant.components.google_travel_time.helpers.Client",
|
||||
return_value=Mock(),
|
||||
), patch(
|
||||
"homeassistant.components.google_travel_time.helpers.distance_matrix",
|
||||
return_value=None,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="bypass_setup")
|
||||
def bypass_setup_fixture():
|
||||
"""Bypass entry setup."""
|
||||
with patch(
|
||||
"homeassistant.components.google_travel_time.async_setup", return_value=True
|
||||
), patch(
|
||||
"homeassistant.components.google_travel_time.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="bypass_update")
|
||||
def bypass_update_fixture():
|
||||
"""Bypass sensor update."""
|
||||
with patch("homeassistant.components.google_travel_time.sensor.distance_matrix"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="invalidate_config_entry")
|
||||
def invalidate_config_entry_fixture():
|
||||
"""Return invalid config entry."""
|
||||
with patch(
|
||||
"homeassistant.components.google_travel_time.helpers.Client",
|
||||
return_value=Mock(),
|
||||
), patch(
|
||||
"homeassistant.components.google_travel_time.helpers.distance_matrix",
|
||||
side_effect=ApiError("test"),
|
||||
):
|
||||
yield
|
297
tests/components/google_travel_time/test_config_flow.py
Normal file
297
tests/components/google_travel_time/test_config_flow.py
Normal file
|
@ -0,0 +1,297 @@
|
|||
"""Test the Google Maps Travel Time config flow."""
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.google_travel_time.const import (
|
||||
ARRIVAL_TIME,
|
||||
CONF_ARRIVAL_TIME,
|
||||
CONF_AVOID,
|
||||
CONF_DEPARTURE_TIME,
|
||||
CONF_DESTINATION,
|
||||
CONF_LANGUAGE,
|
||||
CONF_OPTIONS,
|
||||
CONF_ORIGIN,
|
||||
CONF_TIME,
|
||||
CONF_TIME_TYPE,
|
||||
CONF_TRAFFIC_MODEL,
|
||||
CONF_TRANSIT_MODE,
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE,
|
||||
CONF_UNITS,
|
||||
DEFAULT_NAME,
|
||||
DEPARTURE_TIME,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_MODE,
|
||||
CONF_NAME,
|
||||
CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_minimum_fields(hass, validate_config_entry, bypass_setup):
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == f"{DEFAULT_NAME}: location1 -> location2"
|
||||
assert result2["data"] == {
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
}
|
||||
|
||||
|
||||
async def test_invalid_config_entry(hass, invalidate_config_entry):
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_options_flow(hass, validate_config_entry, bypass_update):
|
||||
"""Test options flow."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
options={
|
||||
CONF_MODE: "driving",
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id, data=None)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_TIME_TYPE: ARRIVAL_TIME,
|
||||
CONF_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == ""
|
||||
assert result["data"] == {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
}
|
||||
|
||||
assert entry.options == {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
}
|
||||
|
||||
|
||||
async def test_options_flow_departure_time(hass, validate_config_entry, bypass_update):
|
||||
"""Test options flow wiith departure time."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id, data=None)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_TIME_TYPE: DEPARTURE_TIME,
|
||||
CONF_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == ""
|
||||
assert result["data"] == {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_DEPARTURE_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
}
|
||||
|
||||
assert entry.options == {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_DEPARTURE_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
}
|
||||
|
||||
|
||||
async def test_dupe_id(hass, validate_config_entry, bypass_setup):
|
||||
"""Test setting up the same entry twice fails."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_API_KEY: "test",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_API_KEY: "test",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import_flow(hass, validate_config_entry, bypass_update):
|
||||
"""Test import_flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
CONF_NAME: "test_name",
|
||||
CONF_OPTIONS: {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
},
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "test_name"
|
||||
assert result["data"] == {
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
CONF_NAME: "test_name",
|
||||
CONF_OPTIONS: {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
},
|
||||
}
|
||||
|
||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||
assert entry.data == {
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ORIGIN: "location1",
|
||||
CONF_DESTINATION: "location2",
|
||||
}
|
||||
assert entry.options == {
|
||||
CONF_MODE: "driving",
|
||||
CONF_LANGUAGE: "en",
|
||||
CONF_AVOID: "tolls",
|
||||
CONF_UNITS: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_ARRIVAL_TIME: "test",
|
||||
CONF_TRAFFIC_MODEL: "best_guess",
|
||||
CONF_TRANSIT_MODE: "train",
|
||||
CONF_TRANSIT_ROUTING_PREFERENCE: "less_walking",
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue