Files
esphome/tests/unit_tests/components/test_script.py
T

161 lines
5.1 KiB
Python

"""Tests for the script component's config validation."""
from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
from esphome import automation
from esphome.components.const import CONF_ON_STOP
from esphome.components.script import (
CONF_SCRIPT_STOP,
SingleScript,
default_script_stop_ids,
to_code,
)
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE, CONF_PARAMETERS, CONF_THEN
from esphome.core import ID
from esphome.cpp_generator import MockObj
def test_bare_script_stop_defaults_to_enclosing_script() -> None:
config = [
{
CONF_ID: "my_script",
CONF_THEN: [
CONF_SCRIPT_STOP,
{CONF_SCRIPT_STOP: None},
{CONF_SCRIPT_STOP: {}},
{CONF_SCRIPT_STOP: "other_script"},
{CONF_SCRIPT_STOP: {CONF_ID: "other_script"}},
{"if": {"condition": "x", CONF_THEN: [CONF_SCRIPT_STOP]}},
],
"on_stop": [{CONF_SCRIPT_STOP: None}],
},
{CONF_ID: "second", CONF_THEN: [CONF_SCRIPT_STOP]},
]
result = default_script_stop_ids(config)
assert result[0][CONF_THEN] == [
{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}},
{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}},
{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}},
{CONF_SCRIPT_STOP: "other_script"},
{CONF_SCRIPT_STOP: {CONF_ID: "other_script"}},
{
"if": {
"condition": "x",
CONF_THEN: [{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}}],
}
},
]
assert result[0]["on_stop"] == [{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}}]
assert result[1][CONF_THEN] == [{CONF_SCRIPT_STOP: {CONF_ID: "second"}}]
# The input is not modified
assert config[1][CONF_THEN] == [CONF_SCRIPT_STOP]
def test_unchanged_script_config_is_not_copied() -> None:
then = [{"logger.log": "hello"}]
config = [{CONF_ID: "my_script", CONF_THEN: then}]
result = default_script_stop_ids(config)
assert result[0] is config[0]
assert result[0][CONF_THEN] is then
def test_script_stop_without_id_outside_script_is_invalid() -> None:
with pytest.raises(cv.Invalid, match="can only be used inside a script"):
automation.validate_action(CONF_SCRIPT_STOP)
def test_script_stop_with_id_is_valid() -> None:
result = automation.validate_action({CONF_SCRIPT_STOP: "my_script"})
assert result[CONF_SCRIPT_STOP][CONF_ID].id == "my_script"
@pytest.mark.parametrize(
"value",
[
pytest.param("my_script", id="not_a_list_or_mapping"),
pytest.param({CONF_THEN: [CONF_SCRIPT_STOP]}, id="mapping_without_id"),
pytest.param(["my_script", {CONF_THEN: [CONF_SCRIPT_STOP]}], id="no_id"),
],
)
def test_script_stop_ids_left_alone_without_script_id(value: Any) -> None:
assert default_script_stop_ids(value) == value
def test_bare_script_stop_in_single_script_mapping() -> None:
config = {CONF_ID: "my_script", CONF_THEN: [CONF_SCRIPT_STOP]}
result = default_script_stop_ids(config)
assert result[CONF_THEN] == [{CONF_SCRIPT_STOP: {CONF_ID: "my_script"}}]
@pytest.mark.asyncio
async def test_on_stop_builds_stop_trigger_automation() -> None:
stop_conf = {CONF_THEN: []}
config = [
{
CONF_ID: ID("my_script", is_declaration=True, type=SingleScript),
CONF_MODE: "single",
CONF_PARAMETERS: {},
CONF_THEN: [],
CONF_ON_STOP: stop_conf,
},
{
CONF_ID: ID("other_script", is_declaration=True, type=SingleScript),
CONF_MODE: "single",
CONF_PARAMETERS: {},
CONF_THEN: [],
},
]
with (
patch("esphome.codegen.new_Pvariable") as new_pvariable,
patch("esphome.codegen.add"),
patch("esphome.codegen.add_define") as add_define,
patch(
"esphome.automation.build_automation", new_callable=AsyncMock
) as build_automation,
):
new_pvariable.side_effect = lambda id_, *args: MockObj(id_.id, "->")
await to_code(config)
add_define.assert_called_once_with("USE_SCRIPT_ON_STOP")
assert [
(str(c.args[0]), *c.args[1:]) for c in build_automation.await_args_list
] == [
("my_script", [], config[0]),
("my_script->get_stop_trigger()", [], stop_conf),
("other_script", [], config[1]),
]
@pytest.mark.asyncio
async def test_no_on_stop_define_without_on_stop() -> None:
config = [
{
CONF_ID: ID("my_script", is_declaration=True, type=SingleScript),
CONF_MODE: "single",
CONF_PARAMETERS: {},
CONF_THEN: [],
}
]
with (
patch("esphome.codegen.new_Pvariable") as new_pvariable,
patch("esphome.codegen.add"),
patch("esphome.codegen.add_define") as add_define,
patch("esphome.automation.build_automation", new_callable=AsyncMock),
):
new_pvariable.side_effect = lambda id_, *args: MockObj(id_.id, "->")
await to_code(config)
add_define.assert_not_called()