"""Test script blueprints.""" import asyncio import contextlib import pathlib from typing import Iterator from unittest.mock import patch from homeassistant.components import script from homeassistant.components.blueprint.models import Blueprint, DomainBlueprints from homeassistant.core import Context, HomeAssistant, callback from homeassistant.helpers import template from homeassistant.setup import async_setup_component from homeassistant.util import yaml from tests.common import async_mock_service BUILTIN_BLUEPRINT_FOLDER = pathlib.Path(script.__file__).parent / "blueprints" @contextlib.contextmanager def patch_blueprint(blueprint_path: str, data_path: str) -> Iterator[None]: """Patch blueprint loading from a different source.""" orig_load = DomainBlueprints._load_blueprint @callback def mock_load_blueprint(self, path: str) -> Blueprint: if path != blueprint_path: assert False, f"Unexpected blueprint {path}" return orig_load(self, path) return Blueprint( yaml.load_yaml(data_path), expected_domain=self.domain, path=path ) with patch( "homeassistant.components.blueprint.models.DomainBlueprints._load_blueprint", mock_load_blueprint, ): yield async def test_confirmable_notification(hass: HomeAssistant) -> None: """Test confirmable notification blueprint.""" with patch_blueprint( "confirmable_notification.yaml", BUILTIN_BLUEPRINT_FOLDER / "confirmable_notification.yaml", ): assert await async_setup_component( hass, script.DOMAIN, { "script": { "confirm": { "use_blueprint": { "path": "confirmable_notification.yaml", "input": { "notify_device": "frodo", "title": "Lord of the things", "message": "Throw ring in mountain?", "confirm_action": [ { "service": "homeassistant.turn_on", "target": {"entity_id": "mount.doom"}, } ], }, } } } }, ) turn_on_calls = async_mock_service(hass, "homeassistant", "turn_on") context = Context() with patch( "homeassistant.components.mobile_app.device_action.async_call_action_from_config" ) as mock_call_action: # Trigger script await hass.services.async_call(script.DOMAIN, "confirm", context=context) # Give script the time to attach the trigger. await asyncio.sleep(0.1) hass.bus.async_fire("mobile_app_notification_action", {"action": "ANYTHING_ELSE"}) hass.bus.async_fire( "mobile_app_notification_action", {"action": "CONFIRM_" + Context().id} ) hass.bus.async_fire( "mobile_app_notification_action", {"action": "CONFIRM_" + context.id} ) await hass.async_block_till_done() assert len(mock_call_action.mock_calls) == 1 _hass, config, variables, _context = mock_call_action.mock_calls[0][1] template.attach(hass, config) rendered_config = template.render_complex(config, variables) assert rendered_config == { "title": "Lord of the things", "message": "Throw ring in mountain?", "alias": "Send notification", "domain": "mobile_app", "type": "notify", "device_id": "frodo", "data": { "actions": [ {"action": "CONFIRM_" + _context.id, "title": "Confirm"}, {"action": "DISMISS_" + _context.id, "title": "Dismiss"}, ] }, } assert len(turn_on_calls) == 1 assert turn_on_calls[0].data == { "entity_id": ["mount.doom"], }