Files
shelly-ui/tests/test_streamlit_app.py
jonas 71803418e5 Initial commit: Shelly Manager with Textual CLI, Streamlit UI, and comprehensive .gitignore
Shelly device management app with mDNS/subnet discovery, inventory,
configuration, and mass operations for Gen1/Gen2+ devices.

Includes .gitignore excluding runtime data (device DB, user config),
AI conversation history, build artifacts, and common Python/OS patterns.
2026-03-23 21:51:59 +01:00

94 lines
3.7 KiB
Python

"""Streamlit AppTest — no live server (uses embedded script runner)."""
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from streamlit.testing.v1 import AppTest
from shelly_manager.core.config import AppConfig
from shelly_manager.core.models import ShellyDevice
ROOT = Path(__file__).resolve().parents[1]
DASHBOARD_APP = ROOT / "src" / "shelly_manager" / "ui" / "Dashboard.py"
MASS_CONFIG_PAGE = ROOT / "src" / "shelly_manager" / "ui" / "pages" / "3_Mass_Config.py"
DEVICE_PAGE = ROOT / "src" / "shelly_manager" / "ui" / "pages" / "2_Device.py"
@pytest.fixture
def mock_manager() -> MagicMock:
mgr = MagicMock()
mgr.storage.list_devices = AsyncMock(return_value=[])
mgr.storage.get_device = AsyncMock(return_value=None)
mgr.storage.get_kv = AsyncMock(return_value=None)
mgr.storage.set_kv = AsyncMock(return_value=None)
mgr.storage.list_snapshots = AsyncMock(return_value=[])
mgr.storage.recent_snapshot_labels_for_devices = AsyncMock(return_value={})
mgr.discover_all = AsyncMock(return_value=[])
mgr.refresh_all = AsyncMock(return_value=[])
mgr.refresh_device = AsyncMock(return_value=None)
mgr.mass_apply_operation = AsyncMock(return_value=[])
mgr.apply_mass_tags = AsyncMock(return_value=0)
mgr.apply_device_config = AsyncMock(return_value=[("sys", "ok", False)])
mgr.reboot_device = AsyncMock(return_value=None)
mgr.reboot_devices_by_ids = AsyncMock(return_value=[])
mgr.check_firmware_update = AsyncMock(return_value={})
mgr.check_firmware_updates_for_ids = AsyncMock(return_value=[])
mgr.apply_firmware_update = AsyncMock(return_value=None)
mgr.mass_apply_section_patch = AsyncMock(return_value=[])
return mgr
def test_dashboard_renders_title_and_metrics(mock_manager: MagicMock) -> None:
with patch("shelly_manager.ui.Dashboard.get_manager", return_value=mock_manager):
at = AppTest.from_file(str(DASHBOARD_APP), default_timeout=15)
at.run(timeout=15)
titles = [n.value for n in at.title]
assert any("Shelly Manager" in (t or "") for t in titles)
assert len(at.metric) >= 1
labels = [m.label for m in at.metric]
assert "Matching (after filters)" in labels
md_blob = "\n".join((m.value or "") for m in at.markdown)
assert "Total devices" in md_blob
def test_mass_config_page_renders(mock_manager: MagicMock) -> None:
with patch("shelly_manager.ui.state.get_manager", return_value=mock_manager):
at = AppTest.from_file(str(MASS_CONFIG_PAGE), default_timeout=20)
at.run(timeout=20)
assert not at.exception
titles = [n.value for n in at.title]
assert any("Mass configuration" in (t or "") for t in titles)
def test_device_detail_page_renders(mock_manager: MagicMock) -> None:
d = ShellyDevice(
id="AABBCCDDEEFF",
name="Kitchen",
mac="AA:BB:CC:DD:EE:FF",
ip="192.168.1.10",
generation=2,
model="Plus1",
firmware="1.0",
online=True,
last_seen=datetime.now(UTC),
capabilities=[],
status={},
settings={"sys": {"device": {"name": "Test"}}},
tags=[],
)
mock_manager.storage.list_devices = AsyncMock(return_value=[d])
mock_manager.storage.get_device = AsyncMock(return_value=d)
with (
patch("shelly_manager.ui.state.get_manager", return_value=mock_manager),
patch("shelly_manager.ui.state.get_config", return_value=AppConfig()),
):
at = AppTest.from_file(str(DEVICE_PAGE), default_timeout=20)
at.run(timeout=20)
assert not at.exception
titles = [n.value for n in at.title]
assert any("Device" in (t or "") for t in titles)