diff --git a/homeassistant/components/smarla/config_flow.py b/homeassistant/components/smarla/config_flow.py index 011311991ae..816adc85d1a 100644 --- a/homeassistant/components/smarla/config_flow.py +++ b/homeassistant/components/smarla/config_flow.py @@ -28,13 +28,13 @@ class SmarlaConfigFlow(ConfigFlow, domain=DOMAIN): conn = Connection(url=HOST, token_b64=token) except ValueError: errors["base"] = "malformed_token" - return (errors, None) + return errors, None if not await conn.refresh_token(): errors["base"] = "invalid_auth" - return (errors, None) + return errors, None - return (errors, conn.token.serialNumber) + return errors, conn.token.serialNumber async def async_step_user( self, user_input: dict[str, Any] | None = None diff --git a/tests/components/smarla/__init__.py b/tests/components/smarla/__init__.py index 5bb8b70f030..1368415ac3b 100644 --- a/tests/components/smarla/__init__.py +++ b/tests/components/smarla/__init__.py @@ -1,20 +1,22 @@ """Tests for the Smarla integration.""" -import base64 -import json +from typing import Any +from unittest.mock import AsyncMock -from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.core import HomeAssistant -MOCK_ACCESS_TOKEN_JSON = { - "refreshToken": "test", - "appIdentifier": "HA-test", - "serialNumber": "ABCD", -} +from tests.common import MockConfigEntry -MOCK_SERIAL_NUMBER = MOCK_ACCESS_TOKEN_JSON["serialNumber"] -MOCK_ACCESS_TOKEN = base64.b64encode( - json.dumps(MOCK_ACCESS_TOKEN_JSON).encode() -).decode() +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Set up the component.""" + config_entry.add_to_hass(hass) -MOCK_USER_INPUT = {CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN} + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + +async def update_property_listeners(mock: AsyncMock, value: Any = None) -> None: + """Update the property listeners for the mock object.""" + for call in mock.add_listener.call_args_list: + await call[0][0](value) diff --git a/tests/components/smarla/common.py b/tests/components/smarla/common.py deleted file mode 100644 index 10cc0edbd2e..00000000000 --- a/tests/components/smarla/common.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Common test utilities for entity component tests.""" - -from homeassistant.components.smarla.const import DOMAIN -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er - - -def get_entity_id_by_unique_id(hass: HomeAssistant, platform: str, unique_id: str): - """Get entity id by its unique id.""" - registry = er.async_get(hass) - return registry.async_get_entity_id(platform, DOMAIN, unique_id) diff --git a/tests/components/smarla/conftest.py b/tests/components/smarla/conftest.py index 5a43157ea68..516ede1cab3 100644 --- a/tests/components/smarla/conftest.py +++ b/tests/components/smarla/conftest.py @@ -6,12 +6,13 @@ from collections.abc import Generator from unittest.mock import AsyncMock, MagicMock, patch from pysmarlaapi.classes import AuthToken +from pysmarlaapi.federwiege.classes import Property import pytest from homeassistant.components.smarla.const import DOMAIN from homeassistant.config_entries import SOURCE_USER -from . import MOCK_ACCESS_TOKEN_JSON, MOCK_SERIAL_NUMBER, MOCK_USER_INPUT +from .const import MOCK_ACCESS_TOKEN_JSON, MOCK_SERIAL_NUMBER, MOCK_USER_INPUT from tests.common import MockConfigEntry @@ -55,11 +56,22 @@ def mock_connection() -> Generator[MagicMock]: @pytest.fixture -def mock_federwiege() -> Generator[MagicMock]: +def mock_federwiege( + mock_connection: AsyncMock, mock_property: AsyncMock +) -> Generator[AsyncMock]: """Mock the Federwiege instance.""" with patch( "homeassistant.components.smarla.Federwiege", autospec=True ) as mock_federwiege: federwiege = mock_federwiege.return_value federwiege.serial_number = MOCK_SERIAL_NUMBER + federwiege.get_property.return_value = mock_property yield federwiege + + +@pytest.fixture +def mock_property() -> AsyncMock: + """Mock the Federwiege instance.""" + mock = AsyncMock(spec=Property) + mock.get.return_value = False + return mock diff --git a/tests/components/smarla/const.py b/tests/components/smarla/const.py new file mode 100644 index 00000000000..33cb51c63d1 --- /dev/null +++ b/tests/components/smarla/const.py @@ -0,0 +1,20 @@ +"""Constants for the Smarla integration tests.""" + +import base64 +import json + +from homeassistant.const import CONF_ACCESS_TOKEN + +MOCK_ACCESS_TOKEN_JSON = { + "refreshToken": "test", + "appIdentifier": "HA-test", + "serialNumber": "ABCD", +} + +MOCK_SERIAL_NUMBER = MOCK_ACCESS_TOKEN_JSON["serialNumber"] + +MOCK_ACCESS_TOKEN = base64.b64encode( + json.dumps(MOCK_ACCESS_TOKEN_JSON).encode() +).decode() + +MOCK_USER_INPUT = {CONF_ACCESS_TOKEN: MOCK_ACCESS_TOKEN} diff --git a/tests/components/smarla/snapshots/test_switch.ambr b/tests/components/smarla/snapshots/test_switch.ambr new file mode 100644 index 00000000000..bd713c209c1 --- /dev/null +++ b/tests/components/smarla/snapshots/test_switch.ambr @@ -0,0 +1,95 @@ +# serializer version: 1 +# name: test_entities[switch.smarla-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.smarla', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'smarla', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'ABCD-swing_active', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[switch.smarla-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Smarla', + }), + 'context': , + 'entity_id': 'switch.smarla', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_entities[switch.smarla_smart_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.smarla_smart_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Smart Mode', + 'platform': 'smarla', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'smart_mode', + 'unique_id': 'ABCD-smart_mode', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[switch.smarla_smart_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Smarla Smart Mode', + }), + 'context': , + 'entity_id': 'switch.smarla_smart_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/smarla/test_config_flow.py b/tests/components/smarla/test_config_flow.py index 0e96e154adb..5dbb4d5001f 100644 --- a/tests/components/smarla/test_config_flow.py +++ b/tests/components/smarla/test_config_flow.py @@ -7,7 +7,7 @@ from homeassistant.config_entries import SOURCE_USER from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -from . import MOCK_SERIAL_NUMBER, MOCK_USER_INPUT +from .const import MOCK_SERIAL_NUMBER, MOCK_USER_INPUT from tests.common import MockConfigEntry diff --git a/tests/components/smarla/test_init.py b/tests/components/smarla/test_init.py index 097c929dff9..fa9c559ce2e 100644 --- a/tests/components/smarla/test_init.py +++ b/tests/components/smarla/test_init.py @@ -1,7 +1,5 @@ """Test switch platform for Swing2Sleep Smarla integration.""" -from unittest.mock import AsyncMock, patch - from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -15,11 +13,9 @@ async def test_init_invalid_auth( # Add the mock entry to hass mock_config_entry.add_to_hass(hass) - with patch.object( - mock_connection, "refresh_token", new=AsyncMock(return_value=False) - ): - # Set up the platform - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() + mock_connection.refresh_token.return_value = False + # Set up the platform + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR diff --git a/tests/components/smarla/test_switch.py b/tests/components/smarla/test_switch.py index 00aeaec8124..e5fff77ceeb 100644 --- a/tests/components/smarla/test_switch.py +++ b/tests/components/smarla/test_switch.py @@ -1,14 +1,13 @@ """Test switch platform for Swing2Sleep Smarla integration.""" -from pysmarlaapi.federwiege.classes import Property -import pytest +from unittest.mock import AsyncMock, patch -from homeassistant.components.smarla.switch import SWITCHES -from homeassistant.components.switch import ( - DOMAIN as SWITCH_DOMAIN, - SwitchEntityDescription, -) +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, @@ -16,57 +15,73 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er -from .common import get_entity_id_by_unique_id +from . import setup_integration, update_property_listeners -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, snapshot_platform -@pytest.mark.parametrize("switch_desc", SWITCHES) -async def test_switch_behavior( +async def test_entities( hass: HomeAssistant, - switch_desc: SwitchEntityDescription, + mock_federwiege: AsyncMock, mock_config_entry: MockConfigEntry, - mock_connection, - mock_federwiege, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, ) -> None: - """Test SmarlaSwitch on/off behavior.""" - # Assign switch property to federwiege - mock_property = Property[bool](None, False) - mock_federwiege.get_property.return_value = mock_property - # Add the mock entry to hass - mock_config_entry.add_to_hass(hass) + """Test the Spotify entities.""" + with ( + patch("homeassistant.components.smarla.PLATFORMS", [Platform.SWITCH]), + ): + await setup_integration(hass, mock_config_entry) - # Set up the platform - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() + await snapshot_platform( + hass, entity_registry, snapshot, mock_config_entry.entry_id + ) - # Get entity id by unique id - unique_id = f"{mock_federwiege.serial_number}-{switch_desc.key}" - entity_id = get_entity_id_by_unique_id(hass, Platform.SWITCH, unique_id) - assert entity_id is not None - # Check entity initial state - assert hass.states.get(entity_id).state == STATE_OFF +@pytest.mark.parametrize( + ("service", "parameter"), + [ + (SERVICE_TURN_ON, True), + (SERVICE_TURN_OFF, False), + ], +) +async def test_switch_action( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_federwiege: AsyncMock, + mock_property: AsyncMock, + service: str, + parameter: bool, +) -> None: + """Test Smarla Switch on/off behavior.""" + await setup_integration(hass, mock_config_entry) # Turn on await hass.services.async_call( SWITCH_DOMAIN, - SERVICE_TURN_ON, - {"entity_id": entity_id}, + service, + {ATTR_ENTITY_ID: "switch.smarla"}, blocking=True, ) - mock_property.set(True, push=False) - await mock_property.notify_listeners() - assert hass.states.get(entity_id).state == STATE_ON + mock_property.set.assert_called_once_with(parameter) - # Turn off - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - {"entity_id": entity_id}, - blocking=True, - ) - mock_property.set(False, push=False) - await mock_property.notify_listeners() - assert hass.states.get(entity_id).state == STATE_OFF + +async def test_switch_state_update( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_federwiege: AsyncMock, + mock_property: AsyncMock, +) -> None: + """Test Smarla Switch on/off behavior.""" + await setup_integration(hass, mock_config_entry) + + assert hass.states.get("switch.smarla").state == STATE_OFF + + mock_property.get.return_value = True + + await update_property_listeners(mock_property) + await hass.async_block_till_done() + + assert hass.states.get("switch.smarla").state == STATE_ON