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:
Erik Montnemery 2024-03-14 14:04:41 +01:00 committed by GitHub
parent fef2d7ddd4
commit a16ea3d7bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 480 additions and 36 deletions

View File

@ -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

View File

@ -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"}})

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -3,5 +3,8 @@
"living_room": "Living Room",
"bedroom": "Bedroom",
"kitchen": "Kitchen"
},
"dashboard": {
"map": { "title": "Map" }
}
}

View File

@ -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):

View File

@ -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}},
}
)

View File

@ -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."""

View File

@ -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",

View 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"}}

View File

@ -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", {})

View File

@ -0,0 +1 @@
"""Tests for Map."""

View 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")

View File

@ -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)

View File

@ -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()