1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Update template button tests to use new framework (#167806)

This commit is contained in:
Petro31
2026-04-09 11:53:11 -04:00
committed by GitHub
parent 36944525e1
commit 7bad7fc4f6
+189 -184
View File
@@ -7,10 +7,8 @@ from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant import setup
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.template import DOMAIN
from homeassistant.components.template.button import DEFAULT_NAME
from homeassistant.components.template.const import CONF_PICTURE
from homeassistant.const import (
ATTR_ENTITY_PICTURE,
@@ -19,15 +17,72 @@ from homeassistant.const import (
CONF_ENTITY_ID,
CONF_FRIENDLY_NAME,
CONF_ICON,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import MockConfigEntry, assert_setup_component
from .conftest import (
ConfigurationStyle,
TemplatePlatformSetup,
assert_action,
async_trigger,
make_test_action,
setup_and_test_nested_unique_id,
setup_and_test_unique_id,
setup_entity,
)
_TEST_BUTTON = "button.template_button"
_TEST_OPTIONS_BUTTON = "button.test"
from tests.common import MockConfigEntry
TEST_ATTRIBUTE_ENTITY_ID = "sensor.test_attribute"
TEST_AVAILABILITY_ENTITY = "binary_sensor.availability"
TEST_BUTTON = TemplatePlatformSetup(BUTTON_DOMAIN, None, "template_button", {})
PRESS_ACTION = make_test_action("press")
@pytest.fixture
async def setup_button(hass: HomeAssistant, count: int, config: dict[str, Any]) -> None:
"""Do setup of button integration."""
await setup_entity(hass, TEST_BUTTON, ConfigurationStyle.MODERN, count, config)
@pytest.fixture
async def setup_single_attribute_button(
hass: HomeAssistant,
attribute: str,
attribute_template: str,
config: dict,
) -> None:
"""Do setup of button integration with a single attribute."""
await setup_entity(
hass,
TEST_BUTTON,
ConfigurationStyle.MODERN,
1,
config,
extra_config={attribute: attribute_template}
if attribute and attribute_template
else {},
)
def _verify(
hass: HomeAssistant,
expected_value: str,
attributes: dict[str, Any] | None = None,
entity_id: str = TEST_BUTTON.entity_id,
) -> None:
"""Verify button's state."""
attributes = attributes or {}
if CONF_FRIENDLY_NAME not in attributes:
attributes[CONF_FRIENDLY_NAME] = TEST_BUTTON.object_id
state = hass.states.get(entity_id)
assert state.state == expected_value
assert state.attributes == attributes
@pytest.mark.parametrize(
@@ -74,50 +129,20 @@ async def test_setup_config_entry(
assert state == snapshot
@pytest.mark.parametrize(("count", "config"), [(1, PRESS_ACTION)])
@pytest.mark.usefixtures("setup_button")
async def test_missing_optional_config(hass: HomeAssistant) -> None:
"""Test: missing optional template is ok."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"button": {
"press": {"service": "script.press"},
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
_verify(hass, STATE_UNKNOWN)
@pytest.mark.parametrize(("count", "config"), [(1, {"press": []})])
@pytest.mark.usefixtures("setup_button")
async def test_missing_emtpy_press_action_config(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test: missing optional template is ok."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"button": {
"press": [],
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
_verify(hass, STATE_UNKNOWN)
now = dt.datetime.now(dt.UTC)
@@ -125,7 +150,7 @@ async def test_missing_emtpy_press_action_config(
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{CONF_ENTITY_ID: _TEST_BUTTON},
{CONF_ENTITY_ID: TEST_BUTTON.entity_id},
blocking=True,
)
@@ -135,63 +160,40 @@ async def test_missing_emtpy_press_action_config(
)
@pytest.mark.parametrize(("count", "config"), [(0, {})])
@pytest.mark.usefixtures("setup_button")
async def test_missing_required_keys(hass: HomeAssistant) -> None:
"""Test: missing required fields will fail."""
with assert_setup_component(0, "template"):
assert await setup.async_setup_component(
hass,
"template",
{"template": {"button": {}}},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.async_all("button") == []
async def test_all_optional_config(
@pytest.mark.parametrize(
("count", "config"),
[
(
1,
{
**PRESS_ACTION,
"device_class": "restart",
},
)
],
)
@pytest.mark.usefixtures("setup_button")
async def test_device_class_option(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
freezer: FrozenDateTimeFactory,
calls: list[ServiceCall],
) -> None:
"""Test: including all optional templates is ok."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"unique_id": "test",
"button": {
"press": {
"service": "test.automation",
"data_template": {"caller": "{{ this.entity_id }}"},
},
"device_class": "restart",
"unique_id": "test",
"name": "test",
"icon": "mdi:test",
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
"""Test optional options is ok."""
_verify(
hass,
STATE_UNKNOWN,
{
CONF_DEVICE_CLASS: "restart",
CONF_FRIENDLY_NAME: "test",
CONF_ICON: "mdi:test",
CONF_FRIENDLY_NAME: TEST_BUTTON.object_id,
},
_TEST_OPTIONS_BUTTON,
TEST_BUTTON.entity_id,
)
now = dt.datetime.now(dt.UTC)
@@ -199,47 +201,77 @@ async def test_all_optional_config(
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{CONF_ENTITY_ID: _TEST_OPTIONS_BUTTON},
{CONF_ENTITY_ID: TEST_BUTTON.entity_id},
blocking=True,
)
assert len(calls) == 1
assert calls[0].data["caller"] == _TEST_OPTIONS_BUTTON
assert_action(TEST_BUTTON, calls, 1, "press")
_verify(
hass,
now.isoformat(),
{
CONF_DEVICE_CLASS: "restart",
CONF_FRIENDLY_NAME: "test",
CONF_ICON: "mdi:test",
CONF_FRIENDLY_NAME: TEST_BUTTON.object_id,
},
_TEST_OPTIONS_BUTTON,
TEST_BUTTON.entity_id,
)
assert entity_registry.async_get_entity_id("button", "template", "test-test")
@pytest.mark.parametrize("config", [PRESS_ACTION])
@pytest.mark.parametrize(
("attribute", "attribute_template", "attribute_name", "expected"),
[
(
CONF_ICON,
"{{ 'mdi:test' if is_state('sensor.test_attribute', 'on') else '' }}",
ATTR_ICON,
"mdi:test",
),
(
CONF_PICTURE,
"{{ 'test.jpg' if is_state('sensor.test_attribute', 'on') else '' }}",
ATTR_ENTITY_PICTURE,
"test.jpg",
),
],
)
@pytest.mark.usefixtures("setup_single_attribute_button")
async def test_options_that_are_templates(
hass: HomeAssistant,
attribute_name: str,
expected: str,
freezer: FrozenDateTimeFactory,
calls: list[ServiceCall],
) -> None:
"""Test button options that are templates."""
expected_attributes = {attribute_name: expected}
_verify(hass, STATE_UNKNOWN, {attribute_name: ""})
await async_trigger(hass, TEST_ATTRIBUTE_ENTITY_ID, STATE_ON)
_verify(hass, STATE_UNKNOWN, expected_attributes)
now = dt.datetime.now(dt.UTC)
freezer.move_to(now)
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{CONF_ENTITY_ID: TEST_BUTTON.entity_id},
blocking=True,
)
assert_action(TEST_BUTTON, calls, 1, "press")
_verify(hass, now.isoformat(), expected_attributes)
@pytest.mark.parametrize("config", [PRESS_ACTION])
@pytest.mark.parametrize(
("attribute", "attribute_template"), [("name", "Button {{ 1 + 1 }}")]
)
@pytest.mark.usefixtures("setup_single_attribute_button")
async def test_name_template(hass: HomeAssistant) -> None:
"""Test: name template."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"button": {
"press": {"service": "script.press"},
"name": "Button {{ 1 + 1 }}",
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
_verify(
hass,
STATE_UNKNOWN,
@@ -250,86 +282,20 @@ async def test_name_template(hass: HomeAssistant) -> None:
)
@pytest.mark.parametrize(
("field", "attribute", "test_template", "expected"),
[
(CONF_ICON, ATTR_ICON, "mdi:test{{ 1 + 1 }}", "mdi:test2"),
(CONF_PICTURE, ATTR_ENTITY_PICTURE, "test{{ 1 + 1 }}.jpg", "test2.jpg"),
],
)
async def test_templated_optional_config(
hass: HomeAssistant,
field: str,
attribute: str,
test_template: str,
expected: str,
) -> None:
"""Test optional config templates."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"button": {
"press": {"service": "script.press"},
field: test_template,
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
_verify(
hass,
STATE_UNKNOWN,
{
attribute: expected,
},
"button.template_button",
async def test_unique_id(hass: HomeAssistant) -> None:
"""Test unique_id option only creates one button per id."""
await setup_and_test_unique_id(
hass, TEST_BUTTON, ConfigurationStyle.MODERN, PRESS_ACTION
)
async def test_unique_id(hass: HomeAssistant) -> None:
"""Test: unique id is ok."""
with assert_setup_component(1, "template"):
assert await setup.async_setup_component(
hass,
"template",
{
"template": {
"unique_id": "test",
"button": {
"press": {"service": "script.press"},
"unique_id": "test",
},
}
},
)
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
_verify(hass, STATE_UNKNOWN)
def _verify(
hass: HomeAssistant,
expected_value: str,
attributes: dict[str, Any] | None = None,
entity_id: str = _TEST_BUTTON,
async def test_nested_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Verify button's state."""
attributes = attributes or {}
if CONF_FRIENDLY_NAME not in attributes:
attributes[CONF_FRIENDLY_NAME] = DEFAULT_NAME
state = hass.states.get(entity_id)
assert state.state == expected_value
assert state.attributes == attributes
"""Test a template unique_id propagates to button unique_ids."""
await setup_and_test_nested_unique_id(
hass, TEST_BUTTON, ConfigurationStyle.MODERN, entity_registry, PRESS_ACTION
)
async def test_device_id(
@@ -376,3 +342,42 @@ async def test_device_id(
template_entity = entity_registry.async_get("button.my_template")
assert template_entity is not None
assert template_entity.device_id == device_entry.id
@pytest.mark.parametrize(
("config", "attribute", "attribute_template"),
[
(
PRESS_ACTION,
"availability",
"{{ is_state('binary_sensor.availability', 'on') }}",
)
],
)
@pytest.mark.usefixtures("setup_single_attribute_button")
async def test_available_template_with_entities(hass: HomeAssistant) -> None:
"""Test availability templates with values from other entities."""
await async_trigger(hass, TEST_AVAILABILITY_ENTITY, STATE_ON)
# Device State should not be unavailable
assert hass.states.get(TEST_BUTTON.entity_id).state != STATE_UNAVAILABLE
# When Availability template returns false
await async_trigger(hass, TEST_AVAILABILITY_ENTITY, STATE_OFF)
# device state should be unavailable
assert hass.states.get(TEST_BUTTON.entity_id).state == STATE_UNAVAILABLE
@pytest.mark.parametrize(
("config", "attribute", "attribute_template"),
[(PRESS_ACTION, "availability", "{{ x - 12 }}")],
)
@pytest.mark.usefixtures("setup_single_attribute_button")
async def test_invalid_availability_template_keeps_component_available(
hass: HomeAssistant, caplog_setup_text
) -> None:
"""Test that an invalid availability keeps the device available."""
assert hass.states.get(TEST_BUTTON.entity_id).state != STATE_UNAVAILABLE
assert "UndefinedError: 'x' is undefined" in caplog_setup_text