mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Deprecate the map integration (#113215)
* Deprecate the map integration * Revert changes in DashboardsCollection._async_load_data * Add option to allow single word in dashboard URL * Update tests * Translate title * Add icon * Improve test coverage
This commit is contained in:
parent
fef2d7ddd4
commit
a16ea3d7bd
@ -747,7 +747,6 @@ omit =
|
||||
homeassistant/components/lyric/climate.py
|
||||
homeassistant/components/lyric/sensor.py
|
||||
homeassistant/components/mailgun/notify.py
|
||||
homeassistant/components/map/*
|
||||
homeassistant/components/mastodon/notify.py
|
||||
homeassistant/components/matrix/__init__.py
|
||||
homeassistant/components/matrix/notify.py
|
||||
|
@ -4,7 +4,7 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import frontend, websocket_api
|
||||
from homeassistant.components import frontend, onboarding, websocket_api
|
||||
from homeassistant.config import (
|
||||
async_hass_config_yaml,
|
||||
async_process_component_and_handle_errors,
|
||||
@ -14,11 +14,13 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import collection, config_validation as cv
|
||||
from homeassistant.helpers.service import async_register_admin_service
|
||||
from homeassistant.helpers.translation import async_get_translations
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import async_get_integration
|
||||
|
||||
from . import dashboard, resources, websocket
|
||||
from .const import ( # noqa: F401
|
||||
CONF_ALLOW_SINGLE_WORD,
|
||||
CONF_ICON,
|
||||
CONF_REQUIRE_ADMIN,
|
||||
CONF_SHOW_IN_SIDEBAR,
|
||||
@ -201,6 +203,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
# Process storage dashboards
|
||||
dashboards_collection = dashboard.DashboardsCollection(hass)
|
||||
|
||||
# This can be removed when the map integration is removed
|
||||
hass.data[DOMAIN]["dashboards_collection"] = dashboards_collection
|
||||
|
||||
dashboards_collection.async_add_listener(storage_dashboard_changed)
|
||||
await dashboards_collection.async_load()
|
||||
|
||||
@ -212,6 +217,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
STORAGE_DASHBOARD_UPDATE_FIELDS,
|
||||
).async_setup(hass, create_list=False)
|
||||
|
||||
def create_map_dashboard():
|
||||
hass.async_create_task(_create_map_dashboard(hass))
|
||||
|
||||
if not onboarding.async_is_onboarded(hass):
|
||||
onboarding.async_add_listener(hass, create_map_dashboard)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@ -249,3 +260,25 @@ def _register_panel(hass, url_path, mode, config, update):
|
||||
kwargs["sidebar_icon"] = config.get(CONF_ICON, DEFAULT_ICON)
|
||||
|
||||
frontend.async_register_built_in_panel(hass, DOMAIN, **kwargs)
|
||||
|
||||
|
||||
async def _create_map_dashboard(hass: HomeAssistant):
|
||||
translations = await async_get_translations(
|
||||
hass, hass.config.language, "dashboard", {onboarding.DOMAIN}
|
||||
)
|
||||
title = translations["component.onboarding.dashboard.map.title"]
|
||||
|
||||
dashboards_collection: dashboard.DashboardsCollection = hass.data[DOMAIN][
|
||||
"dashboards_collection"
|
||||
]
|
||||
await dashboards_collection.async_create_item(
|
||||
{
|
||||
CONF_ALLOW_SINGLE_WORD: True,
|
||||
CONF_ICON: "mdi:map",
|
||||
CONF_TITLE: title,
|
||||
CONF_URL_PATH: "map",
|
||||
}
|
||||
)
|
||||
|
||||
map_store: dashboard.LovelaceStorage = hass.data[DOMAIN]["dashboards"]["map"]
|
||||
await map_store.async_save({"strategy": {"type": "map"}})
|
||||
|
@ -24,6 +24,7 @@ MODE_STORAGE = "storage"
|
||||
MODE_AUTO = "auto-gen"
|
||||
|
||||
LOVELACE_CONFIG_FILE = "ui-lovelace.yaml"
|
||||
CONF_ALLOW_SINGLE_WORD = "allow_single_word"
|
||||
CONF_URL_PATH = "url_path"
|
||||
CONF_RESOURCE_TYPE_WS = "res_type"
|
||||
|
||||
@ -75,6 +76,8 @@ STORAGE_DASHBOARD_CREATE_FIELDS = {
|
||||
# For now we write "storage" as all modes.
|
||||
# In future we can adjust this to be other modes.
|
||||
vol.Optional(CONF_MODE, default=MODE_STORAGE): MODE_STORAGE,
|
||||
# Set to allow adding dashboard without hyphen
|
||||
vol.Optional(CONF_ALLOW_SINGLE_WORD): bool,
|
||||
}
|
||||
|
||||
STORAGE_DASHBOARD_UPDATE_FIELDS = DASHBOARD_BASE_UPDATE_FIELDS
|
||||
|
@ -18,6 +18,7 @@ from homeassistant.helpers import collection, storage
|
||||
from homeassistant.util.yaml import Secrets, load_yaml_dict
|
||||
|
||||
from .const import (
|
||||
CONF_ALLOW_SINGLE_WORD,
|
||||
CONF_ICON,
|
||||
CONF_URL_PATH,
|
||||
DOMAIN,
|
||||
@ -234,10 +235,14 @@ class DashboardsCollection(collection.DictStorageCollection):
|
||||
|
||||
async def _process_create_data(self, data: dict) -> dict:
|
||||
"""Validate the config is valid."""
|
||||
if "-" not in data[CONF_URL_PATH]:
|
||||
url_path = data[CONF_URL_PATH]
|
||||
|
||||
allow_single_word = data.pop(CONF_ALLOW_SINGLE_WORD, False)
|
||||
|
||||
if not allow_single_word and "-" not in url_path:
|
||||
raise vol.Invalid("Url path needs to contain a hyphen (-)")
|
||||
|
||||
if data[CONF_URL_PATH] in self.hass.data[DATA_PANELS]:
|
||||
if url_path in self.hass.data[DATA_PANELS]:
|
||||
raise vol.Invalid("Panel url path needs to be unique")
|
||||
|
||||
return self.CREATE_SCHEMA(data)
|
||||
|
@ -2,6 +2,7 @@
|
||||
"domain": "lovelace",
|
||||
"name": "Dashboards",
|
||||
"codeowners": ["@home-assistant/frontend"],
|
||||
"dependencies": ["onboarding"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/lovelace",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal"
|
||||
|
@ -1,16 +1,52 @@
|
||||
"""Support for showing device locations."""
|
||||
|
||||
from homeassistant.components import frontend
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.components import onboarding
|
||||
from homeassistant.components.lovelace import _create_map_dashboard
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
DOMAIN = "map"
|
||||
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Register the built-in map panel."""
|
||||
frontend.async_register_built_in_panel(hass, "map", "map", "hass:tooltip-account")
|
||||
"""Create a map panel."""
|
||||
|
||||
if DOMAIN in config:
|
||||
async_create_issue(
|
||||
hass,
|
||||
HOMEASSISTANT_DOMAIN,
|
||||
f"deprecated_yaml_{DOMAIN}",
|
||||
breaks_in_ha_version="2024.10.0",
|
||||
is_fixable=False,
|
||||
is_persistent=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
translation_placeholders={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "map",
|
||||
},
|
||||
)
|
||||
|
||||
store: Store[dict[str, bool]] = Store(
|
||||
hass,
|
||||
STORAGE_VERSION_MAJOR,
|
||||
STORAGE_KEY,
|
||||
)
|
||||
data = await store.async_load()
|
||||
if data:
|
||||
return True
|
||||
|
||||
if onboarding.async_is_onboarded(hass):
|
||||
await _create_map_dashboard(hass)
|
||||
|
||||
await store.async_save({"migrated": True})
|
||||
|
||||
return True
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "map",
|
||||
"name": "Map",
|
||||
"codeowners": [],
|
||||
"dependencies": ["frontend"],
|
||||
"dependencies": ["frontend", "lovelace"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/map",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal"
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, TypedDict
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@ -26,15 +28,30 @@ STORAGE_VERSION = 4
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
|
||||
|
||||
class OnboadingStorage(Store[dict[str, list[str]]]):
|
||||
@dataclass
|
||||
class OnboardingData:
|
||||
"""Container for onboarding data."""
|
||||
|
||||
listeners: list[Callable[[], None]]
|
||||
onboarded: bool
|
||||
steps: OnboardingStoreData
|
||||
|
||||
|
||||
class OnboardingStoreData(TypedDict):
|
||||
"""Onboarding store data."""
|
||||
|
||||
done: list[str]
|
||||
|
||||
|
||||
class OnboardingStorage(Store[OnboardingStoreData]):
|
||||
"""Store onboarding data."""
|
||||
|
||||
async def _async_migrate_func(
|
||||
self,
|
||||
old_major_version: int,
|
||||
old_minor_version: int,
|
||||
old_data: dict[str, list[str]],
|
||||
) -> dict[str, list[str]]:
|
||||
old_data: OnboardingStoreData,
|
||||
) -> OnboardingStoreData:
|
||||
"""Migrate to the new version."""
|
||||
# From version 1 -> 2, we automatically mark the integration step done
|
||||
if old_major_version < 2:
|
||||
@ -50,21 +67,37 @@ class OnboadingStorage(Store[dict[str, list[str]]]):
|
||||
@callback
|
||||
def async_is_onboarded(hass: HomeAssistant) -> bool:
|
||||
"""Return if Home Assistant has been onboarded."""
|
||||
data = hass.data.get(DOMAIN)
|
||||
return data is None or data is True
|
||||
data: OnboardingData | None = hass.data.get(DOMAIN)
|
||||
return data is None or data.onboarded is True
|
||||
|
||||
|
||||
@bind_hass
|
||||
@callback
|
||||
def async_is_user_onboarded(hass: HomeAssistant) -> bool:
|
||||
"""Return if a user has been created as part of onboarding."""
|
||||
return async_is_onboarded(hass) or STEP_USER in hass.data[DOMAIN]["done"]
|
||||
return async_is_onboarded(hass) or STEP_USER in hass.data[DOMAIN].steps["done"]
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_listener(hass: HomeAssistant, listener: Callable[[], None]) -> None:
|
||||
"""Add a listener to be called when onboarding is complete."""
|
||||
data: OnboardingData | None = hass.data.get(DOMAIN)
|
||||
|
||||
if not data:
|
||||
# Onboarding not active
|
||||
return
|
||||
|
||||
if data.onboarded:
|
||||
listener()
|
||||
return
|
||||
|
||||
data.listeners.append(listener)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the onboarding component."""
|
||||
store = OnboadingStorage(hass, STORAGE_VERSION, STORAGE_KEY, private=True)
|
||||
data: dict[str, list[str]] | None
|
||||
store = OnboardingStorage(hass, STORAGE_VERSION, STORAGE_KEY, private=True)
|
||||
data: OnboardingStoreData | None
|
||||
if (data := await store.async_load()) is None:
|
||||
data = {"done": []}
|
||||
|
||||
@ -88,7 +121,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
if set(data["done"]) == set(STEPS):
|
||||
return True
|
||||
|
||||
hass.data[DOMAIN] = data
|
||||
hass.data[DOMAIN] = OnboardingData([], False, data)
|
||||
|
||||
await views.async_setup(hass, data, store)
|
||||
|
||||
|
@ -3,5 +3,8 @@
|
||||
"living_room": "Living Room",
|
||||
"bedroom": "Bedroom",
|
||||
"kitchen": "Kitchen"
|
||||
},
|
||||
"dashboard": {
|
||||
"map": { "title": "Map" }
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.async_ import create_eager_task
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import OnboadingStorage
|
||||
from . import OnboardingData, OnboardingStorage, OnboardingStoreData
|
||||
|
||||
from .const import (
|
||||
DEFAULT_AREAS,
|
||||
@ -40,7 +40,7 @@ from .const import (
|
||||
|
||||
|
||||
async def async_setup(
|
||||
hass: HomeAssistant, data: dict[str, list[str]], store: OnboadingStorage
|
||||
hass: HomeAssistant, data: OnboardingStoreData, store: OnboardingStorage
|
||||
) -> None:
|
||||
"""Set up the onboarding view."""
|
||||
hass.http.register_view(OnboardingView(data, store))
|
||||
@ -58,7 +58,7 @@ class OnboardingView(HomeAssistantView):
|
||||
url = "/api/onboarding"
|
||||
name = "api:onboarding"
|
||||
|
||||
def __init__(self, data: dict[str, list[str]], store: OnboadingStorage) -> None:
|
||||
def __init__(self, data: OnboardingStoreData, store: OnboardingStorage) -> None:
|
||||
"""Initialize the onboarding view."""
|
||||
self._store = store
|
||||
self._data = data
|
||||
@ -77,7 +77,7 @@ class InstallationTypeOnboardingView(HomeAssistantView):
|
||||
url = "/api/onboarding/installation_type"
|
||||
name = "api:onboarding:installation_type"
|
||||
|
||||
def __init__(self, data: dict[str, list[str]]) -> None:
|
||||
def __init__(self, data: OnboardingStoreData) -> None:
|
||||
"""Initialize the onboarding installation type view."""
|
||||
self._data = data
|
||||
|
||||
@ -96,7 +96,7 @@ class _BaseOnboardingView(HomeAssistantView):
|
||||
|
||||
step: str
|
||||
|
||||
def __init__(self, data: dict[str, list[str]], store: OnboadingStorage) -> None:
|
||||
def __init__(self, data: OnboardingStoreData, store: OnboardingStorage) -> None:
|
||||
"""Initialize the onboarding view."""
|
||||
self._store = store
|
||||
self._data = data
|
||||
@ -113,7 +113,10 @@ class _BaseOnboardingView(HomeAssistantView):
|
||||
await self._store.async_save(self._data)
|
||||
|
||||
if set(self._data["done"]) == set(STEPS):
|
||||
hass.data[DOMAIN] = True
|
||||
data: OnboardingData = hass.data[DOMAIN]
|
||||
data.onboarded = True
|
||||
for listener in data.listeners:
|
||||
listener()
|
||||
|
||||
|
||||
class UserOnboardingView(_BaseOnboardingView):
|
||||
|
@ -441,7 +441,10 @@ def gen_platform_strings_schema(config: Config, integration: Integration) -> vol
|
||||
|
||||
|
||||
ONBOARDING_SCHEMA = vol.Schema(
|
||||
{vol.Required("area"): {str: translation_value_validator}}
|
||||
{
|
||||
vol.Required("area"): {str: translation_value_validator},
|
||||
vol.Required("dashboard"): {str: {"title": translation_value_validator}},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
"""Test the Lovelace Cast platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from time import time
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -15,6 +16,19 @@ from homeassistant.setup import async_setup_component
|
||||
from tests.common import async_mock_service
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_onboarding_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding.
|
||||
|
||||
Enabled to prevent creating default dashboards during test execution.
|
||||
"""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=True,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_https_url(hass):
|
||||
"""Mock valid URL."""
|
||||
|
@ -1,7 +1,8 @@
|
||||
"""Test the Lovelace initialization."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -14,6 +15,19 @@ from tests.common import assert_setup_component, async_capture_events
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_onboarding_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding.
|
||||
|
||||
Enabled to prevent creating default dashboards during test execution.
|
||||
"""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=True,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
async def test_lovelace_from_storage(
|
||||
hass: HomeAssistant, hass_ws_client, hass_storage: dict[str, Any]
|
||||
) -> None:
|
||||
@ -277,7 +291,7 @@ async def test_dashboard_from_yaml(
|
||||
|
||||
async def test_wrong_key_dashboard_from_yaml(hass: HomeAssistant) -> None:
|
||||
"""Test we don't load lovelace dashboard without hyphen config from yaml."""
|
||||
with assert_setup_component(0):
|
||||
with assert_setup_component(0, "lovelace"):
|
||||
assert not await async_setup_component(
|
||||
hass,
|
||||
"lovelace",
|
||||
|
98
tests/components/lovelace/test_init.py
Normal file
98
tests/components/lovelace/test_init.py
Normal file
@ -0,0 +1,98 @@
|
||||
"""Test the Lovelace initialization."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_onboarding_not_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding."""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=False,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_onboarding_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding."""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=True,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_add_onboarding_listener() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding."""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_add_listener",
|
||||
) as mock_add_onboarding_listener:
|
||||
yield mock_add_onboarding_listener
|
||||
|
||||
|
||||
async def test_create_dashboards_when_onboarded(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_onboarding_done,
|
||||
) -> None:
|
||||
"""Test we don't create dashboards when onboarded."""
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
||||
# List dashboards
|
||||
await client.send_json_auto_id({"type": "lovelace/dashboards/list"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == []
|
||||
|
||||
|
||||
async def test_create_dashboards_when_not_onboarded(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_add_onboarding_listener,
|
||||
mock_onboarding_not_done,
|
||||
) -> None:
|
||||
"""Test we automatically create dashboards when not onboarded."""
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
||||
# Call onboarding listener
|
||||
mock_add_onboarding_listener.mock_calls[0][1][1]()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# List dashboards
|
||||
await client.send_json_auto_id({"type": "lovelace/dashboards/list"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == [
|
||||
{
|
||||
"icon": "mdi:map",
|
||||
"id": "map",
|
||||
"mode": "storage",
|
||||
"require_admin": False,
|
||||
"show_in_sidebar": True,
|
||||
"title": "Map",
|
||||
"url_path": "map",
|
||||
}
|
||||
]
|
||||
|
||||
# List map dashboard config
|
||||
await client.send_json_auto_id({"type": "lovelace/config", "url_path": "map"})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == {"strategy": {"type": "map"}}
|
@ -1,7 +1,10 @@
|
||||
"""Tests for Lovelace system health."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lovelace import dashboard
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -10,6 +13,19 @@ from homeassistant.setup import async_setup_component
|
||||
from tests.common import get_system_health_info
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_onboarding_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding.
|
||||
|
||||
Enabled to prevent creating default dashboards during test execution.
|
||||
"""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=True,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
async def test_system_health_info_autogen(hass: HomeAssistant) -> None:
|
||||
"""Test system health info endpoint."""
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
1
tests/components/map/__init__.py
Normal file
1
tests/components/map/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for Map."""
|
116
tests/components/map/test_init.py
Normal file
116
tests/components/map/test_init.py
Normal file
@ -0,0 +1,116 @@
|
||||
"""Test the Map initialization."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.map import DOMAIN
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockModule, mock_integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_onboarding_not_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding."""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=False,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_onboarding_done() -> Generator[MagicMock, None, None]:
|
||||
"""Mock that Home Assistant is currently onboarding."""
|
||||
with patch(
|
||||
"homeassistant.components.onboarding.async_is_onboarded",
|
||||
return_value=True,
|
||||
) as mock_onboarding:
|
||||
yield mock_onboarding
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_create_map_dashboard() -> Generator[MagicMock, None, None]:
|
||||
"""Mock the create map dashboard function."""
|
||||
with patch(
|
||||
"homeassistant.components.map._create_map_dashboard",
|
||||
) as mock_create_map_dashboard:
|
||||
yield mock_create_map_dashboard
|
||||
|
||||
|
||||
async def test_create_dashboards_when_onboarded(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_onboarding_done,
|
||||
mock_create_map_dashboard,
|
||||
) -> None:
|
||||
"""Test we create map dashboard when onboarded."""
|
||||
# Mock the lovelace integration to prevent it from creating a map dashboard
|
||||
mock_integration(hass, MockModule("lovelace"))
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
mock_create_map_dashboard.assert_called_once()
|
||||
assert hass_storage[DOMAIN]["data"] == {"migrated": True}
|
||||
|
||||
|
||||
async def test_create_dashboards_once_when_onboarded(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_onboarding_done,
|
||||
mock_create_map_dashboard,
|
||||
) -> None:
|
||||
"""Test we create map dashboard once when onboarded."""
|
||||
hass_storage[DOMAIN] = {
|
||||
"version": 1,
|
||||
"minor_version": 1,
|
||||
"key": "map",
|
||||
"data": {"migrated": True},
|
||||
}
|
||||
|
||||
# Mock the lovelace integration to prevent it from creating a map dashboard
|
||||
mock_integration(hass, MockModule("lovelace"))
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
mock_create_map_dashboard.assert_not_called()
|
||||
assert hass_storage[DOMAIN]["data"] == {"migrated": True}
|
||||
|
||||
|
||||
async def test_create_dashboards_when_not_onboarded(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_onboarding_not_done,
|
||||
mock_create_map_dashboard,
|
||||
) -> None:
|
||||
"""Test we do not create map dashboard when not onboarded."""
|
||||
# Mock the lovelace integration to prevent it from creating a map dashboard
|
||||
mock_integration(hass, MockModule("lovelace"))
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
mock_create_map_dashboard.assert_not_called()
|
||||
assert hass_storage[DOMAIN]["data"] == {"migrated": True}
|
||||
|
||||
|
||||
async def test_create_issue_when_not_manually_configured(hass: HomeAssistant) -> None:
|
||||
"""Test creating issue registry issues."""
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
issue_registry = ir.async_get(hass)
|
||||
assert not issue_registry.async_get_issue(
|
||||
HOMEASSISTANT_DOMAIN, "deprecated_yaml_map"
|
||||
)
|
||||
|
||||
|
||||
async def test_create_issue_when_manually_configured(hass: HomeAssistant) -> None:
|
||||
"""Test creating issue registry issues."""
|
||||
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
issue_registry = ir.async_get(hass)
|
||||
assert issue_registry.async_get_issue(HOMEASSISTANT_DOMAIN, "deprecated_yaml_map")
|
@ -48,10 +48,10 @@ async def test_is_onboarded() -> None:
|
||||
|
||||
assert onboarding.async_is_onboarded(hass)
|
||||
|
||||
hass.data[onboarding.DOMAIN] = True
|
||||
hass.data[onboarding.DOMAIN] = onboarding.OnboardingData([], True, {"done": []})
|
||||
assert onboarding.async_is_onboarded(hass)
|
||||
|
||||
hass.data[onboarding.DOMAIN] = {"done": []}
|
||||
hass.data[onboarding.DOMAIN] = onboarding.OnboardingData([], False, {"done": []})
|
||||
assert not onboarding.async_is_onboarded(hass)
|
||||
|
||||
|
||||
@ -62,10 +62,15 @@ async def test_is_user_onboarded() -> None:
|
||||
|
||||
assert onboarding.async_is_user_onboarded(hass)
|
||||
|
||||
hass.data[onboarding.DOMAIN] = True
|
||||
hass.data[onboarding.DOMAIN] = onboarding.OnboardingData([], True, {"done": []})
|
||||
assert onboarding.async_is_user_onboarded(hass)
|
||||
|
||||
hass.data[onboarding.DOMAIN] = {"done": []}
|
||||
hass.data[onboarding.DOMAIN] = onboarding.OnboardingData(
|
||||
[], False, {"done": ["user"]}
|
||||
)
|
||||
assert onboarding.async_is_user_onboarded(hass)
|
||||
|
||||
hass.data[onboarding.DOMAIN] = onboarding.OnboardingData([], False, {"done": []})
|
||||
assert not onboarding.async_is_user_onboarded(hass)
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ import asyncio
|
||||
from http import HTTPStatus
|
||||
import os
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -655,3 +655,64 @@ async def test_onboarding_installation_type_after_done(
|
||||
resp = await client.get("/api/onboarding/installation_type")
|
||||
|
||||
assert resp.status == 401
|
||||
|
||||
|
||||
async def test_complete_onboarding(
|
||||
hass: HomeAssistant, hass_client: ClientSessionGenerator
|
||||
) -> None:
|
||||
"""Test completing onboarding calls listeners."""
|
||||
listener_1 = Mock()
|
||||
onboarding.async_add_listener(hass, listener_1)
|
||||
listener_1.assert_not_called()
|
||||
|
||||
assert await async_setup_component(hass, "onboarding", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
listener_2 = Mock()
|
||||
onboarding.async_add_listener(hass, listener_2)
|
||||
listener_2.assert_not_called()
|
||||
|
||||
client = await hass_client()
|
||||
|
||||
assert not onboarding.async_is_onboarded(hass)
|
||||
|
||||
# Complete the user step
|
||||
resp = await client.post(
|
||||
"/api/onboarding/users",
|
||||
json={
|
||||
"client_id": CLIENT_ID,
|
||||
"name": "Test Name",
|
||||
"username": "test-user",
|
||||
"password": "test-pass",
|
||||
"language": "en",
|
||||
},
|
||||
)
|
||||
assert resp.status == 200
|
||||
assert not onboarding.async_is_onboarded(hass)
|
||||
listener_2.assert_not_called()
|
||||
|
||||
# Complete the core config step
|
||||
resp = await client.post("/api/onboarding/core_config")
|
||||
assert resp.status == 200
|
||||
assert not onboarding.async_is_onboarded(hass)
|
||||
listener_2.assert_not_called()
|
||||
|
||||
# Complete the integration step
|
||||
resp = await client.post(
|
||||
"/api/onboarding/integration",
|
||||
json={"client_id": CLIENT_ID, "redirect_uri": CLIENT_REDIRECT_URI},
|
||||
)
|
||||
assert resp.status == 200
|
||||
assert not onboarding.async_is_onboarded(hass)
|
||||
listener_2.assert_not_called()
|
||||
|
||||
# Complete the analytics step
|
||||
resp = await client.post("/api/onboarding/analytics")
|
||||
assert resp.status == 200
|
||||
assert onboarding.async_is_onboarded(hass)
|
||||
listener_1.assert_not_called() # Registered before the integration was setup
|
||||
listener_2.assert_called_once_with()
|
||||
|
||||
listener_3 = Mock()
|
||||
onboarding.async_add_listener(hass, listener_3)
|
||||
listener_3.assert_called_once_with()
|
||||
|
Loading…
x
Reference in New Issue
Block a user