2018-03-01 03:31:38 +00:00
|
|
|
"""Test the translation helper."""
|
2020-04-16 15:38:54 +00:00
|
|
|
import asyncio
|
2018-03-01 03:31:38 +00:00
|
|
|
from os import path
|
2020-04-16 15:38:54 +00:00
|
|
|
import pathlib
|
2018-03-01 03:31:38 +00:00
|
|
|
|
2018-03-22 19:21:33 +00:00
|
|
|
import pytest
|
|
|
|
|
2020-04-22 00:57:21 +00:00
|
|
|
from homeassistant.const import EVENT_COMPONENT_LOADED
|
2019-12-09 15:52:24 +00:00
|
|
|
from homeassistant.generated import config_flows
|
2020-04-22 00:57:21 +00:00
|
|
|
from homeassistant.helpers import translation
|
2020-04-16 15:38:54 +00:00
|
|
|
from homeassistant.loader import async_get_integration
|
2020-04-30 20:29:50 +00:00
|
|
|
from homeassistant.setup import async_setup_component, setup_component
|
|
|
|
|
|
|
|
from tests.async_mock import Mock, patch
|
2019-12-09 15:52:24 +00:00
|
|
|
|
2018-03-01 03:31:38 +00:00
|
|
|
|
2018-03-22 19:21:33 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def mock_config_flows():
|
|
|
|
"""Mock the config flows."""
|
|
|
|
flows = []
|
2019-07-31 19:25:30 +00:00
|
|
|
with patch.object(config_flows, "FLOWS", flows):
|
2018-03-22 19:21:33 +00:00
|
|
|
yield flows
|
|
|
|
|
|
|
|
|
2018-03-01 03:31:38 +00:00
|
|
|
def test_flatten():
|
|
|
|
"""Test the flatten function."""
|
2019-07-31 19:25:30 +00:00
|
|
|
data = {"parent1": {"child1": "data1", "child2": "data2"}, "parent2": "data3"}
|
2018-03-01 03:31:38 +00:00
|
|
|
|
|
|
|
flattened = translation.flatten(data)
|
|
|
|
|
|
|
|
assert flattened == {
|
|
|
|
"parent1.child1": "data1",
|
|
|
|
"parent1.child2": "data2",
|
|
|
|
"parent2": "data3",
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-19 00:13:13 +00:00
|
|
|
async def test_component_translation_path(hass):
|
2018-03-01 03:31:38 +00:00
|
|
|
"""Test the component translation file function."""
|
2019-07-31 19:25:30 +00:00
|
|
|
assert await async_setup_component(
|
|
|
|
hass,
|
|
|
|
"switch",
|
|
|
|
{"switch": [{"platform": "test"}, {"platform": "test_embedded"}]},
|
|
|
|
)
|
|
|
|
assert await async_setup_component(hass, "test_standalone", {"test_standalone"})
|
|
|
|
assert await async_setup_component(hass, "test_package", {"test_package"})
|
|
|
|
|
2020-04-16 15:38:54 +00:00
|
|
|
(
|
|
|
|
int_test,
|
|
|
|
int_test_embedded,
|
|
|
|
int_test_standalone,
|
|
|
|
int_test_package,
|
|
|
|
) = await asyncio.gather(
|
|
|
|
async_get_integration(hass, "test"),
|
|
|
|
async_get_integration(hass, "test_embedded"),
|
|
|
|
async_get_integration(hass, "test_standalone"),
|
|
|
|
async_get_integration(hass, "test_package"),
|
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert path.normpath(
|
2020-04-19 00:13:13 +00:00
|
|
|
translation.component_translation_path("switch.test", "en", int_test)
|
2019-07-31 19:25:30 +00:00
|
|
|
) == path.normpath(
|
2020-04-21 23:11:05 +00:00
|
|
|
hass.config.path("custom_components", "test", "translations", "switch.en.json")
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert path.normpath(
|
2020-04-19 00:13:13 +00:00
|
|
|
translation.component_translation_path(
|
2020-04-16 15:38:54 +00:00
|
|
|
"switch.test_embedded", "en", int_test_embedded
|
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
) == path.normpath(
|
|
|
|
hass.config.path(
|
2020-04-21 23:11:05 +00:00
|
|
|
"custom_components", "test_embedded", "translations", "switch.en.json"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
assert (
|
2020-04-19 00:13:13 +00:00
|
|
|
translation.component_translation_path(
|
2020-04-16 15:38:54 +00:00
|
|
|
"test_standalone", "en", int_test_standalone
|
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
is None
|
|
|
|
)
|
|
|
|
|
|
|
|
assert path.normpath(
|
2020-04-19 00:13:13 +00:00
|
|
|
translation.component_translation_path("test_package", "en", int_test_package)
|
2019-07-31 19:25:30 +00:00
|
|
|
) == path.normpath(
|
2020-04-21 23:11:05 +00:00
|
|
|
hass.config.path("custom_components", "test_package", "translations", "en.json")
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-03-01 03:31:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_load_translations_files(hass):
|
|
|
|
"""Test the load translation files function."""
|
|
|
|
# Test one valid and one invalid file
|
|
|
|
file1 = hass.config.path(
|
2020-04-21 23:11:05 +00:00
|
|
|
"custom_components", "test", "translations", "switch.en.json"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-03-01 03:31:38 +00:00
|
|
|
file2 = hass.config.path(
|
2020-04-21 23:11:05 +00:00
|
|
|
"custom_components", "test", "translations", "invalid.json"
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert translation.load_translations_files(
|
|
|
|
{"switch.test": file1, "invalid": file2}
|
|
|
|
) == {
|
2020-04-19 00:13:13 +00:00
|
|
|
"switch.test": {
|
|
|
|
"state": {"string1": "Value 1", "string2": "Value 2"},
|
|
|
|
"something": "else",
|
|
|
|
},
|
2019-07-31 19:25:30 +00:00
|
|
|
"invalid": {},
|
2018-03-01 03:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-22 19:21:33 +00:00
|
|
|
async def test_get_translations(hass, mock_config_flows):
|
2018-03-01 03:31:38 +00:00
|
|
|
"""Test the get translations helper."""
|
2020-04-20 03:35:49 +00:00
|
|
|
translations = await translation.async_get_translations(hass, "en", "state")
|
2018-03-01 03:31:38 +00:00
|
|
|
assert translations == {}
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert await async_setup_component(hass, "switch", {"switch": {"platform": "test"}})
|
2020-06-01 05:18:30 +00:00
|
|
|
await hass.async_block_till_done()
|
2018-03-01 03:31:38 +00:00
|
|
|
|
2020-04-20 03:35:49 +00:00
|
|
|
translations = await translation.async_get_translations(hass, "en", "state")
|
2019-09-10 20:07:55 +00:00
|
|
|
|
|
|
|
assert translations["component.switch.state.string1"] == "Value 1"
|
|
|
|
assert translations["component.switch.state.string2"] == "Value 2"
|
2018-03-01 03:31:38 +00:00
|
|
|
|
2020-04-19 00:13:13 +00:00
|
|
|
translations = await translation.async_get_translations(hass, "de", "state")
|
|
|
|
assert "component.switch.something" not in translations
|
2019-09-10 20:07:55 +00:00
|
|
|
assert translations["component.switch.state.string1"] == "German Value 1"
|
|
|
|
assert translations["component.switch.state.string2"] == "German Value 2"
|
2018-03-01 03:31:38 +00:00
|
|
|
|
|
|
|
# Test a partial translation
|
2020-04-20 03:35:49 +00:00
|
|
|
translations = await translation.async_get_translations(hass, "es", "state")
|
2019-09-10 20:07:55 +00:00
|
|
|
assert translations["component.switch.state.string1"] == "Spanish Value 1"
|
|
|
|
assert translations["component.switch.state.string2"] == "Value 2"
|
2018-03-01 03:31:38 +00:00
|
|
|
|
|
|
|
# Test that an untranslated language falls back to English.
|
2020-04-20 03:35:49 +00:00
|
|
|
translations = await translation.async_get_translations(
|
|
|
|
hass, "invalid-language", "state"
|
|
|
|
)
|
2019-09-10 20:07:55 +00:00
|
|
|
assert translations["component.switch.state.string1"] == "Value 1"
|
|
|
|
assert translations["component.switch.state.string2"] == "Value 2"
|
2018-03-22 19:21:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_get_translations_loads_config_flows(hass, mock_config_flows):
|
|
|
|
"""Test the get translations helper loads config flow translations."""
|
2019-07-31 19:25:30 +00:00
|
|
|
mock_config_flows.append("component1")
|
2020-04-16 15:38:54 +00:00
|
|
|
integration = Mock(file_path=pathlib.Path(__file__))
|
|
|
|
integration.name = "Component 1"
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2020-04-30 20:29:50 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.translation.component_translation_path",
|
|
|
|
return_value="bla.json",
|
|
|
|
), patch(
|
|
|
|
"homeassistant.helpers.translation.load_translations_files",
|
2019-07-31 19:25:30 +00:00
|
|
|
return_value={"component1": {"hello": "world"}},
|
2020-04-16 15:38:54 +00:00
|
|
|
), patch(
|
|
|
|
"homeassistant.helpers.translation.async_get_integration",
|
|
|
|
return_value=integration,
|
2020-04-19 00:13:13 +00:00
|
|
|
):
|
|
|
|
translations = await translation.async_get_translations(
|
2020-04-20 03:35:49 +00:00
|
|
|
hass, "en", "hello", config_flow=True
|
2020-04-19 00:13:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert translations == {
|
|
|
|
"component.component1.hello": "world",
|
|
|
|
}
|
|
|
|
|
|
|
|
assert "component1" not in hass.config.components
|
|
|
|
|
|
|
|
|
|
|
|
async def test_get_translations_while_loading_components(hass):
|
|
|
|
"""Test the get translations helper loads config flow translations."""
|
|
|
|
integration = Mock(file_path=pathlib.Path(__file__))
|
|
|
|
integration.name = "Component 1"
|
|
|
|
hass.config.components.add("component1")
|
2020-10-27 21:02:38 +00:00
|
|
|
load_count = 0
|
2020-04-19 00:13:13 +00:00
|
|
|
|
2020-04-30 20:29:50 +00:00
|
|
|
def mock_load_translation_files(files):
|
2020-04-19 00:13:13 +00:00
|
|
|
"""Mock load translation files."""
|
2020-10-27 21:02:38 +00:00
|
|
|
nonlocal load_count
|
|
|
|
load_count += 1
|
2020-04-19 00:13:13 +00:00
|
|
|
# Mimic race condition by loading a component during setup
|
2020-04-30 20:29:50 +00:00
|
|
|
setup_component(hass, "persistent_notification", {})
|
2020-04-19 00:13:13 +00:00
|
|
|
return {"component1": {"hello": "world"}}
|
|
|
|
|
2020-04-30 20:29:50 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.translation.component_translation_path",
|
|
|
|
return_value="bla.json",
|
|
|
|
), patch(
|
|
|
|
"homeassistant.helpers.translation.load_translations_files",
|
|
|
|
mock_load_translation_files,
|
2020-04-19 00:13:13 +00:00
|
|
|
), patch(
|
|
|
|
"homeassistant.helpers.translation.async_get_integration",
|
|
|
|
return_value=integration,
|
2019-07-31 19:25:30 +00:00
|
|
|
):
|
2020-10-27 21:02:38 +00:00
|
|
|
tasks = [
|
|
|
|
translation.async_get_translations(hass, "en", "hello") for _ in range(5)
|
|
|
|
]
|
|
|
|
all_translations = await asyncio.gather(*tasks)
|
2020-04-19 00:13:13 +00:00
|
|
|
|
2020-10-27 21:02:38 +00:00
|
|
|
assert all_translations[0] == {
|
2020-04-16 15:38:54 +00:00
|
|
|
"component.component1.hello": "world",
|
|
|
|
}
|
2020-10-27 21:02:38 +00:00
|
|
|
assert load_count == 1
|
2020-04-19 19:37:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_get_translation_categories(hass):
|
|
|
|
"""Test the get translations helper loads config flow translations."""
|
|
|
|
with patch.object(translation, "async_get_config_flows", return_value={"light"}):
|
|
|
|
translations = await translation.async_get_translations(
|
|
|
|
hass, "en", "title", None, True
|
|
|
|
)
|
|
|
|
assert "component.light.title" in translations
|
|
|
|
|
|
|
|
translations = await translation.async_get_translations(
|
|
|
|
hass, "en", "device_automation", None, True
|
|
|
|
)
|
|
|
|
assert "component.light.device_automation.action_type.turn_on" in translations
|
2020-04-20 03:35:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_translation_merging(hass, caplog):
|
|
|
|
"""Test we merge translations of two integrations."""
|
|
|
|
hass.config.components.add("sensor.moon")
|
|
|
|
hass.config.components.add("sensor.season")
|
|
|
|
hass.config.components.add("sensor")
|
|
|
|
|
|
|
|
translations = await translation.async_get_translations(hass, "en", "state")
|
|
|
|
|
|
|
|
assert "component.sensor.state.moon__phase.first_quarter" in translations
|
|
|
|
assert "component.sensor.state.season__season.summer" in translations
|
|
|
|
|
2020-04-22 00:57:21 +00:00
|
|
|
# Clear cache
|
|
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
# Patch in some bad translation data
|
|
|
|
|
|
|
|
orig_load_translations = translation.load_translations_files
|
|
|
|
|
|
|
|
def mock_load_translations_files(files):
|
|
|
|
"""Mock loading."""
|
|
|
|
result = orig_load_translations(files)
|
|
|
|
result["sensor.season"] = {"state": "bad data"}
|
|
|
|
return result
|
2020-04-20 03:35:49 +00:00
|
|
|
|
2020-04-30 20:29:50 +00:00
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.translation.load_translations_files",
|
2020-04-22 00:57:21 +00:00
|
|
|
side_effect=mock_load_translations_files,
|
2020-04-20 03:35:49 +00:00
|
|
|
):
|
|
|
|
translations = await translation.async_get_translations(hass, "en", "state")
|
|
|
|
|
|
|
|
assert "component.sensor.state.moon__phase.first_quarter" in translations
|
|
|
|
|
|
|
|
assert (
|
|
|
|
"An integration providing translations for sensor provided invalid data: bad data"
|
|
|
|
in caplog.text
|
|
|
|
)
|
2020-04-22 00:57:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_caching(hass):
|
|
|
|
"""Test we cache data."""
|
|
|
|
hass.config.components.add("sensor")
|
|
|
|
|
|
|
|
# Patch with same method so we can count invocations
|
|
|
|
with patch(
|
|
|
|
"homeassistant.helpers.translation.merge_resources",
|
|
|
|
side_effect=translation.merge_resources,
|
|
|
|
) as mock_merge:
|
|
|
|
await translation.async_get_translations(hass, "en", "state")
|
|
|
|
assert len(mock_merge.mock_calls) == 1
|
|
|
|
|
|
|
|
await translation.async_get_translations(hass, "en", "state")
|
|
|
|
assert len(mock_merge.mock_calls) == 1
|
|
|
|
|
|
|
|
# This event clears the cache so we should record another call
|
|
|
|
hass.bus.async_fire(EVENT_COMPONENT_LOADED)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
await translation.async_get_translations(hass, "en", "state")
|
|
|
|
assert len(mock_merge.mock_calls) == 2
|
2020-05-01 18:34:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_custom_component_translations(hass):
|
|
|
|
"""Test getting translation from custom components."""
|
|
|
|
hass.config.components.add("test_standalone")
|
|
|
|
hass.config.components.add("test_embedded")
|
|
|
|
hass.config.components.add("test_package")
|
|
|
|
assert await translation.async_get_translations(hass, "en", "state") == {}
|