From af51aeb6dc000579e7e12ba43a6516be2650890e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 23 Nov 2021 15:50:54 -0600 Subject: [PATCH] Show how user input is malformed in the UI on error (#60057) --- homeassistant/helpers/data_entry_flow.py | 6 +- .../components/config/test_config_entries.py | 76 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/data_entry_flow.py b/homeassistant/helpers/data_entry_flow.py index 787041700f4..061233c0e1a 100644 --- a/homeassistant/helpers/data_entry_flow.py +++ b/homeassistant/helpers/data_entry_flow.py @@ -110,8 +110,10 @@ class FlowManagerResourceView(_BaseFlowManagerView): result = await self._flow_mgr.async_configure(flow_id, data) except data_entry_flow.UnknownFlow: return self.json_message("Invalid flow specified", HTTPStatus.NOT_FOUND) - except vol.Invalid: - return self.json_message("User input malformed", HTTPStatus.BAD_REQUEST) + except vol.Invalid as ex: + return self.json_message( + f"User input malformed: {ex}", HTTPStatus.BAD_REQUEST + ) result = self._prepare_result_json(result) diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 04444e40f5d..8b890148d51 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -12,6 +12,7 @@ from homeassistant.components.config import config_entries from homeassistant.config_entries import HANDLERS from homeassistant.core import callback from homeassistant.generated import config_flows +import homeassistant.helpers.config_validation as cv from homeassistant.setup import async_setup_component from tests.common import ( @@ -689,6 +690,81 @@ async def test_two_step_options_flow(hass, client): } +async def test_options_flow_with_invalid_data(hass, client): + """Test an options flow with invalid_data.""" + mock_integration( + hass, MockModule("test", async_setup_entry=AsyncMock(return_value=True)) + ) + + class TestFlow(core_ce.ConfigFlow): + @staticmethod + @callback + def async_get_options_flow(config_entry): + class OptionsFlowHandler(data_entry_flow.FlowHandler): + async def async_step_init(self, user_input=None): + return self.async_show_form( + step_id="finish", + data_schema=vol.Schema( + { + vol.Required( + "choices", default=["invalid", "valid"] + ): cv.multi_select({"valid": "Valid"}) + } + ), + ) + + async def async_step_finish(self, user_input=None): + return self.async_create_entry( + title="Enable disable", data=user_input + ) + + return OptionsFlowHandler() + + MockConfigEntry( + domain="test", + entry_id="test1", + source="bla", + ).add_to_hass(hass) + entry = hass.config_entries.async_entries()[0] + + with patch.dict(HANDLERS, {"test": TestFlow}): + url = "/api/config/config_entries/options/flow" + resp = await client.post(url, json={"handler": entry.entry_id}) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + flow_id = data.pop("flow_id") + assert data == { + "type": "form", + "handler": "test1", + "step_id": "finish", + "data_schema": [ + { + "default": ["invalid", "valid"], + "name": "choices", + "options": {"valid": "Valid"}, + "required": True, + "type": "multi_select", + } + ], + "description_placeholders": None, + "errors": None, + "last_step": None, + } + + with patch.dict(HANDLERS, {"test": TestFlow}): + resp = await client.post( + f"/api/config/config_entries/options/flow/{flow_id}", + json={"choices": ["valid", "invalid"]}, + ) + assert resp.status == HTTPStatus.BAD_REQUEST + data = await resp.json() + assert data == { + "message": "User input malformed: invalid is not a valid option for " + "dictionary value @ data['choices']" + } + + async def test_update_prefrences(hass, hass_ws_client): """Test that we can update system options.""" assert await async_setup_component(hass, "config", {})