diff --git a/tests/components/onewire/test_config_flow.py b/tests/components/onewire/test_config_flow.py index 57677fc5bff..eab44bc5d48 100644 --- a/tests/components/onewire/test_config_flow.py +++ b/tests/components/onewire/test_config_flow.py @@ -1,18 +1,27 @@ """Tests for 1-Wire config flow.""" +from collections.abc import Generator from unittest.mock import AsyncMock, patch from pyownet import protocol import pytest -from homeassistant.components.onewire.const import DOMAIN +from homeassistant.components.onewire.const import ( + DOMAIN, + INPUT_ENTRY_CLEAR_OPTIONS, + INPUT_ENTRY_DEVICE_SELECTION, +) from homeassistant.config_entries import SOURCE_USER, ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.config_validation import ensure_list + +from .const import MOCK_OWPROXY_DEVICES @pytest.fixture(autouse=True, name="mock_setup_entry") -def override_async_setup_entry() -> AsyncMock: +def override_async_setup_entry() -> Generator[AsyncMock, None, None]: """Override async_setup_entry.""" with patch( "homeassistant.components.onewire.async_setup_entry", return_value=True @@ -20,7 +29,25 @@ def override_async_setup_entry() -> AsyncMock: yield mock_setup_entry -async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock): +@pytest.fixture +async def filled_device_registry( + hass: HomeAssistant, config_entry: ConfigEntry, device_registry: dr.DeviceRegistry +) -> dr.DeviceRegistry: + """Fill device registry with mock devices.""" + for device_details in MOCK_OWPROXY_DEVICES.values(): + if infos := device_details.get("device_info"): + for info in ensure_list(infos): + device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers=info["identifiers"], + manufacturer=info["manufacturer"], + model=info["model"], + name=info["name"], + ) + return device_registry + + +async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None: """Test user flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -63,7 +90,7 @@ async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock): async def test_user_duplicate( hass: HomeAssistant, config_entry: ConfigEntry, mock_setup_entry: AsyncMock -): +) -> None: """Test user duplicate flow.""" await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -85,3 +112,167 @@ async def test_user_duplicate( assert result["reason"] == "already_configured" await hass.async_block_till_done() assert len(mock_setup_entry.mock_calls) == 1 + + +@pytest.mark.usefixtures("filled_device_registry") +async def test_user_options_clear( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Test clearing the options.""" + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + # Verify that first config step comes back with a selection list of all the 28-family devices + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["data_schema"].schema["device_selection"].options == { + "28.111111111111": False, + "28.222222222222": False, + "28.222222222223": False, + } + + # Verify that the clear-input action clears the options dict + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={INPUT_ENTRY_CLEAR_OPTIONS: True}, + ) + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["data"] == {} + + +@pytest.mark.usefixtures("filled_device_registry") +async def test_user_options_empty_selection( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Test leaving the selection of devices empty.""" + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + # Verify that first config step comes back with a selection list of all the 28-family devices + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["data_schema"].schema["device_selection"].options == { + "28.111111111111": False, + "28.222222222222": False, + "28.222222222223": False, + } + + # Verify that an empty selection does not modify the options + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={INPUT_ENTRY_DEVICE_SELECTION: []}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "device_selection" + assert result["errors"] == {"base": "device_not_selected"} + + +@pytest.mark.usefixtures("filled_device_registry") +async def test_user_options_set_single( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Test configuring a single device.""" + # Clear config options to certify functionality when starting from scratch + config_entry.options = {} + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + # Verify that first config step comes back with a selection list of all the 28-family devices + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["data_schema"].schema["device_selection"].options == { + "28.111111111111": False, + "28.222222222222": False, + "28.222222222223": False, + } + + # Verify that a single selected device to configure comes back as a form with the device to configure + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={INPUT_ENTRY_DEVICE_SELECTION: ["28.111111111111"]}, + ) + assert result["type"] == FlowResultType.FORM + assert result["description_placeholders"]["sensor_id"] == "28.111111111111" + + # Verify that the setting for the device comes back as default when no input is given + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={}, + ) + assert result["type"] == FlowResultType.CREATE_ENTRY + assert ( + result["data"]["device_options"]["28.111111111111"]["precision"] + == "temperature" + ) + + +async def test_user_options_set_multiple( + hass: HomeAssistant, + config_entry: ConfigEntry, + filled_device_registry: dr.DeviceRegistry, +) -> None: + """Test configuring multiple consecutive devices in a row.""" + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + # Verify that first config step comes back with a selection list of all the 28-family devices + for entry in dr.async_entries_for_config_entry( + filled_device_registry, config_entry.entry_id + ): + filled_device_registry.async_update_device(entry.id, name_by_user="Given Name") + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["data_schema"].schema["device_selection"].options == { + "Given Name (28.111111111111)": False, + "Given Name (28.222222222222)": False, + "Given Name (28.222222222223)": False, + } + + # Verify that selecting two devices to configure comes back as a + # form with the first device to configure using it's long name as entry + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + INPUT_ENTRY_DEVICE_SELECTION: [ + "Given Name (28.111111111111)", + "Given Name (28.222222222222)", + ] + }, + ) + assert result["type"] == FlowResultType.FORM + assert ( + result["description_placeholders"]["sensor_id"] + == "Given Name (28.222222222222)" + ) + + # Verify that next sensor is coming up for configuration after the first + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"precision": "temperature"}, + ) + assert result["type"] == FlowResultType.FORM + assert ( + result["description_placeholders"]["sensor_id"] + == "Given Name (28.111111111111)" + ) + + # Verify that the setting for the device comes back as default when no input is given + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"precision": "temperature9"}, + ) + assert result["type"] == FlowResultType.CREATE_ENTRY + assert ( + result["data"]["device_options"]["28.222222222222"]["precision"] + == "temperature" + ) + assert ( + result["data"]["device_options"]["28.111111111111"]["precision"] + == "temperature9" + ) + + +async def test_user_options_no_devices( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Test that options does not change when no devices are available.""" + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + # Verify that first config step comes back with an empty list of possible devices to choose from + result = await hass.config_entries.options.async_init(config_entry.entry_id) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "No configurable devices found." diff --git a/tests/components/onewire/test_options_flow.py b/tests/components/onewire/test_options_flow.py deleted file mode 100644 index 314a6f101d3..00000000000 --- a/tests/components/onewire/test_options_flow.py +++ /dev/null @@ -1,215 +0,0 @@ -"""Tests for 1-Wire config flow.""" -from unittest.mock import MagicMock, patch - -from homeassistant.components.onewire.const import ( - INPUT_ENTRY_CLEAR_OPTIONS, - INPUT_ENTRY_DEVICE_SELECTION, -) -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import Platform -from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import FlowResultType -from homeassistant.helpers import device_registry as dr - -from . import setup_owproxy_mock_devices -from .const import MOCK_OWPROXY_DEVICES - - -class FakeDevice: - """Mock Class for mocking DeviceEntry.""" - - name_by_user = "Given Name" - - -async def test_user_options_clear( - hass: HomeAssistant, - config_entry: ConfigEntry, - owproxy: MagicMock, -): - """Test clearing the options.""" - setup_owproxy_mock_devices( - owproxy, Platform.SENSOR, [x for x in MOCK_OWPROXY_DEVICES if "28." in x] - ) - - # Verify that first config step comes back with a selection list of all the 28-family devices - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["data_schema"].schema["device_selection"].options == { - "28.111111111111": False, - "28.222222222222": False, - "28.222222222223": False, - } - - # Verify that the clear-input action clears the options dict - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={INPUT_ENTRY_CLEAR_OPTIONS: True}, - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["data"] == {} - - -async def test_user_options_empty_selection( - hass: HomeAssistant, - config_entry: ConfigEntry, - owproxy: MagicMock, -): - """Test leaving the selection of devices empty.""" - setup_owproxy_mock_devices( - owproxy, Platform.SENSOR, [x for x in MOCK_OWPROXY_DEVICES if "28." in x] - ) - - # Verify that first config step comes back with a selection list of all the 28-family devices - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["data_schema"].schema["device_selection"].options == { - "28.111111111111": False, - "28.222222222222": False, - "28.222222222223": False, - } - - # Verify that an empty selection does not modify the options - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={INPUT_ENTRY_DEVICE_SELECTION: []}, - ) - assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "device_selection" - assert result["errors"] == {"base": "device_not_selected"} - - -async def test_user_options_set_single( - hass: HomeAssistant, - config_entry: ConfigEntry, - owproxy: MagicMock, -): - """Test configuring a single device.""" - setup_owproxy_mock_devices( - owproxy, Platform.SENSOR, [x for x in MOCK_OWPROXY_DEVICES if "28." in x] - ) - - # Clear config options to certify functionality when starting from scratch - config_entry.options = {} - - # Verify that first config step comes back with a selection list of all the 28-family devices - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["data_schema"].schema["device_selection"].options == { - "28.111111111111": False, - "28.222222222222": False, - "28.222222222223": False, - } - - # Verify that a single selected device to configure comes back as a form with the device to configure - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={INPUT_ENTRY_DEVICE_SELECTION: ["28.111111111111"]}, - ) - assert result["type"] == FlowResultType.FORM - assert result["description_placeholders"]["sensor_id"] == "28.111111111111" - - # Verify that the setting for the device comes back as default when no input is given - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={}, - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert ( - result["data"]["device_options"]["28.111111111111"]["precision"] - == "temperature" - ) - - -async def test_user_options_set_multiple( - hass: HomeAssistant, - config_entry: ConfigEntry, - owproxy: MagicMock, -): - """Test configuring multiple consecutive devices in a row.""" - setup_owproxy_mock_devices( - owproxy, Platform.SENSOR, [x for x in MOCK_OWPROXY_DEVICES if "28." in x] - ) - - # Initialize onewire hub - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - # Verify that first config step comes back with a selection list of all the 28-family devices - device_registry = dr.async_get(hass) - for entry in dr.async_entries_for_config_entry( - device_registry, config_entry.entry_id - ): - device_registry.async_update_device(entry.id, name_by_user="Given Name") - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["data_schema"].schema["device_selection"].options == { - "Given Name (28.111111111111)": False, - "Given Name (28.222222222222)": False, - "Given Name (28.222222222223)": False, - } - - # Verify that selecting two devices to configure comes back as a - # form with the first device to configure using it's long name as entry - with patch( - "homeassistant.helpers.device_registry.DeviceRegistry.async_get_device", - return_value=FakeDevice(), - ): - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={ - INPUT_ENTRY_DEVICE_SELECTION: [ - "Given Name (28.111111111111)", - "Given Name (28.222222222222)", - ] - }, - ) - assert result["type"] == FlowResultType.FORM - assert ( - result["description_placeholders"]["sensor_id"] - == "Given Name (28.222222222222)" - ) - - # Verify that next sensor is coming up for configuration after the first - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={"precision": "temperature"}, - ) - assert result["type"] == FlowResultType.FORM - assert ( - result["description_placeholders"]["sensor_id"] - == "Given Name (28.111111111111)" - ) - - # Verify that the setting for the device comes back as default when no input is given - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={"precision": "temperature9"}, - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert ( - result["data"]["device_options"]["28.222222222222"]["precision"] - == "temperature" - ) - assert ( - result["data"]["device_options"]["28.111111111111"]["precision"] - == "temperature9" - ) - - -async def test_user_options_no_devices( - hass: HomeAssistant, - config_entry: ConfigEntry, - owproxy: MagicMock, -): - """Test that options does not change when no devices are available.""" - # Initialize onewire hub - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - # Verify that first config step comes back with an empty list of possible devices to choose from - result = await hass.config_entries.options.async_init(config_entry.entry_id) - await hass.async_block_till_done() - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "No configurable devices found."