mirror of
https://github.com/home-assistant/core.git
synced 2025-12-20 02:48:57 +00:00
365 lines
11 KiB
Python
365 lines
11 KiB
Python
"""template conftest."""
|
|
|
|
from dataclasses import dataclass
|
|
from enum import Enum, StrEnum
|
|
|
|
import pytest
|
|
|
|
from homeassistant.components import template
|
|
from homeassistant.config_entries import SOURCE_USER
|
|
from homeassistant.core import HomeAssistant, ServiceCall
|
|
from homeassistant.data_entry_flow import FlowResultType
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.helpers.typing import ConfigType
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import assert_setup_component, async_mock_service
|
|
from tests.conftest import WebSocketGenerator
|
|
|
|
|
|
class ConfigurationStyle(Enum):
|
|
"""Configuration Styles for template testing."""
|
|
|
|
LEGACY = "Legacy"
|
|
MODERN = "Modern"
|
|
TRIGGER = "Trigger"
|
|
|
|
|
|
class Brewery(StrEnum):
|
|
"""Test enum."""
|
|
|
|
MMMM = "mmmm"
|
|
BEER = "beer"
|
|
IS = "is"
|
|
GOOD = "good"
|
|
|
|
|
|
def make_test_trigger(*entities: str) -> dict:
|
|
"""Make a test state trigger."""
|
|
return {
|
|
"trigger": [
|
|
{
|
|
"trigger": "state",
|
|
"entity_id": list(entities),
|
|
},
|
|
{"platform": "event", "event_type": "test_event"},
|
|
],
|
|
"variables": {"triggering_entity": "{{ trigger.entity_id }}"},
|
|
"action": [
|
|
{"event": "action_event", "event_data": {"what": "{{ triggering_entity }}"}}
|
|
],
|
|
}
|
|
|
|
|
|
async def async_trigger(
|
|
hass: HomeAssistant, entity_id: str, state: str | None = None
|
|
) -> None:
|
|
"""Trigger a state change."""
|
|
hass.states.async_set(entity_id, state)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def async_setup_legacy_platforms(
|
|
hass: HomeAssistant,
|
|
domain: str,
|
|
slug: str | None,
|
|
count: int,
|
|
config: ConfigType | list[ConfigType],
|
|
) -> None:
|
|
"""Do setup of any legacy platform that supports a keyed dictionary of template entities."""
|
|
if slug is None:
|
|
# Lock and Weather platforms do not use a slug
|
|
if isinstance(config, list):
|
|
config = {domain: [{"platform": "template", **item} for item in config]}
|
|
else:
|
|
config = {domain: {"platform": "template", **config}}
|
|
else:
|
|
assert isinstance(config, dict)
|
|
config = {domain: {"platform": "template", slug: config}}
|
|
|
|
with assert_setup_component(count, domain):
|
|
assert await async_setup_component(
|
|
hass,
|
|
domain,
|
|
config,
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
await hass.async_start()
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def async_setup_modern_state_format(
|
|
hass: HomeAssistant,
|
|
domain: str,
|
|
count: int,
|
|
config: ConfigType | list[ConfigType],
|
|
extra_section_config: ConfigType | None = None,
|
|
) -> None:
|
|
"""Do setup of template integration via modern format."""
|
|
with assert_setup_component(count, template.DOMAIN):
|
|
assert await async_setup_component(
|
|
hass,
|
|
template.DOMAIN,
|
|
{"template": {domain: config, **(extra_section_config or {})}},
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
await hass.async_start()
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def async_setup_modern_trigger_format(
|
|
hass: HomeAssistant,
|
|
domain: str,
|
|
trigger: dict,
|
|
count: int,
|
|
config: ConfigType | list[ConfigType],
|
|
extra_section_config: ConfigType | None = None,
|
|
) -> None:
|
|
"""Do setup of template integration via trigger format."""
|
|
config = {"template": {domain: config, **trigger, **(extra_section_config or {})}}
|
|
|
|
with assert_setup_component(count, template.DOMAIN):
|
|
assert await async_setup_component(
|
|
hass,
|
|
template.DOMAIN,
|
|
config,
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
await hass.async_start()
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TemplatePlatformSetup:
|
|
"""Template Platform Setup Information."""
|
|
|
|
domain: str
|
|
legacy_slug: str | None
|
|
object_id: str
|
|
trigger: ConfigType
|
|
|
|
@property
|
|
def entity_id(self) -> str:
|
|
"""Return test entity ID."""
|
|
return f"{self.domain}.{self.object_id}"
|
|
|
|
|
|
async def setup_entity(
|
|
hass: HomeAssistant,
|
|
platform_setup: TemplatePlatformSetup,
|
|
style: ConfigurationStyle,
|
|
count: int,
|
|
config: ConfigType,
|
|
state_template: str | None = None,
|
|
extra_config: ConfigType | None = None,
|
|
attributes: ConfigType | None = None,
|
|
extra_section_config: ConfigType | None = None,
|
|
) -> None:
|
|
"""Do setup of a template entity based on the configuration style."""
|
|
if style == ConfigurationStyle.LEGACY:
|
|
await async_setup_legacy_platforms(
|
|
hass,
|
|
platform_setup.domain,
|
|
platform_setup.legacy_slug,
|
|
count,
|
|
{
|
|
platform_setup.object_id: {
|
|
**({"value_template": state_template} if state_template else {}),
|
|
**config,
|
|
**(extra_config or {}),
|
|
**({"attribute_templates": attributes} if attributes else {}),
|
|
}
|
|
},
|
|
)
|
|
return
|
|
|
|
entity_config = {
|
|
"name": platform_setup.object_id,
|
|
**({"state": state_template} if state_template else {}),
|
|
**config,
|
|
**({"attributes": attributes} if attributes else {}),
|
|
**(extra_config or {}),
|
|
}
|
|
if style == ConfigurationStyle.MODERN:
|
|
await async_setup_modern_state_format(
|
|
hass, platform_setup.domain, count, entity_config, extra_section_config
|
|
)
|
|
elif style == ConfigurationStyle.TRIGGER:
|
|
await async_setup_modern_trigger_format(
|
|
hass,
|
|
platform_setup.domain,
|
|
platform_setup.trigger,
|
|
count,
|
|
entity_config,
|
|
extra_section_config,
|
|
)
|
|
|
|
|
|
async def setup_and_test_unique_id(
|
|
hass: HomeAssistant,
|
|
platform_setup: TemplatePlatformSetup,
|
|
style: ConfigurationStyle,
|
|
entity_config: ConfigType | None,
|
|
) -> None:
|
|
"""Setup 2 entities with the same unique_id and verify only 1 entity is created.
|
|
|
|
The entity_config not provide name or unique_id, those are added automatically.
|
|
"""
|
|
entity_config = {"unique_id": "not-so_-unique-anymore", **(entity_config or {})}
|
|
if style == ConfigurationStyle.LEGACY:
|
|
if platform_setup.legacy_slug is None:
|
|
config = [
|
|
{"name": "template_entity_1", **entity_config},
|
|
{"name": "template_entity_2", **entity_config},
|
|
]
|
|
else:
|
|
config = {
|
|
"template_entity_1": entity_config,
|
|
"template_entity_2": entity_config,
|
|
}
|
|
await async_setup_legacy_platforms(
|
|
hass, platform_setup.domain, platform_setup.legacy_slug, 1, config
|
|
)
|
|
elif style == ConfigurationStyle.MODERN:
|
|
await async_setup_modern_state_format(
|
|
hass,
|
|
platform_setup.domain,
|
|
1,
|
|
[
|
|
{"name": "template_entity_1", **entity_config},
|
|
{"name": "template_entity_2", **entity_config},
|
|
],
|
|
)
|
|
elif style == ConfigurationStyle.TRIGGER:
|
|
await async_setup_modern_trigger_format(
|
|
hass,
|
|
platform_setup.domain,
|
|
platform_setup.trigger,
|
|
1,
|
|
[
|
|
{"name": "template_entity_1", **entity_config},
|
|
{"name": "template_entity_2", **entity_config},
|
|
],
|
|
)
|
|
|
|
assert len(hass.states.async_all(platform_setup.domain)) == 1
|
|
|
|
|
|
async def setup_and_test_nested_unique_id(
|
|
hass: HomeAssistant,
|
|
platform_setup: TemplatePlatformSetup,
|
|
style: ConfigurationStyle,
|
|
entity_registry: er.EntityRegistry,
|
|
entity_config: ConfigType | None,
|
|
) -> None:
|
|
"""Setup 2 entities with unique unique_ids in a template section that contains a unique_id.
|
|
|
|
The test will verify that 2 entities are created where the unique_id appends the
|
|
section unique_id to each entity unique_id.
|
|
|
|
The entity_config should not provide name or unique_id, those are added automatically.
|
|
"""
|
|
entities = [
|
|
{"name": "test_a", "unique_id": "a", **(entity_config or {})},
|
|
{"name": "test_b", "unique_id": "b", **(entity_config or {})},
|
|
]
|
|
extra_section_config = {"unique_id": "x"}
|
|
if style == ConfigurationStyle.MODERN:
|
|
await async_setup_modern_state_format(
|
|
hass, platform_setup.domain, 1, entities, extra_section_config
|
|
)
|
|
elif style == ConfigurationStyle.TRIGGER:
|
|
await async_setup_modern_trigger_format(
|
|
hass,
|
|
platform_setup.domain,
|
|
platform_setup.trigger,
|
|
1,
|
|
entities,
|
|
extra_section_config,
|
|
)
|
|
|
|
assert len(hass.states.async_all(platform_setup.domain)) == 2
|
|
|
|
entry = entity_registry.async_get(f"{platform_setup.domain}.test_a")
|
|
assert entry.unique_id == "x-a"
|
|
|
|
entry = entity_registry.async_get(f"{platform_setup.domain}.test_b")
|
|
assert entry.unique_id == "x-b"
|
|
|
|
|
|
@pytest.fixture
|
|
def calls(hass: HomeAssistant) -> list[ServiceCall]:
|
|
"""Track calls to a mock service."""
|
|
return async_mock_service(hass, "test", "automation")
|
|
|
|
|
|
@pytest.fixture
|
|
async def start_ha(
|
|
hass: HomeAssistant, count: int, domain: str, config: ConfigType
|
|
) -> None:
|
|
"""Do setup of integration."""
|
|
with assert_setup_component(count, domain):
|
|
assert await async_setup_component(
|
|
hass,
|
|
domain,
|
|
config,
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
await hass.async_start()
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
@pytest.fixture
|
|
async def caplog_setup_text(caplog: pytest.LogCaptureFixture) -> str:
|
|
"""Return setup log of integration."""
|
|
return caplog.text
|
|
|
|
|
|
@pytest.fixture(autouse=True, name="stub_blueprint_populate")
|
|
def stub_blueprint_populate_autouse(stub_blueprint_populate: None) -> None:
|
|
"""Stub copying the blueprints to the config folder."""
|
|
|
|
|
|
async def async_get_flow_preview_state(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
domain: str,
|
|
user_input: ConfigType,
|
|
) -> ConfigType:
|
|
"""Test the config flow preview."""
|
|
client = await hass_ws_client(hass)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
template.DOMAIN, context={"source": SOURCE_USER}
|
|
)
|
|
assert result["type"] is FlowResultType.MENU
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"next_step_id": domain},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert result["type"] is FlowResultType.FORM
|
|
assert result["step_id"] == domain
|
|
assert result["errors"] is None
|
|
assert result["preview"] == "template"
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "template/start_preview",
|
|
"flow_id": result["flow_id"],
|
|
"flow_type": "config_flow",
|
|
"user_input": user_input,
|
|
}
|
|
)
|
|
msg = await client.receive_json()
|
|
assert msg["success"]
|
|
assert msg["result"] is None
|
|
|
|
msg = await client.receive_json()
|
|
return msg["event"]
|