You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
5.5 KiB
160 lines
5.5 KiB
from __future__ import annotations
|
|
|
|
import sys
|
|
import tempfile
|
|
import textwrap
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
TOOLS = Path(__file__).resolve().parents[1] / "tools"
|
|
sys.path.insert(0, str(TOOLS))
|
|
|
|
from validate_dashboards import ValidationError, validate_repository # noqa: E402
|
|
|
|
|
|
HEADER = """\
|
|
######################################################################
|
|
# @CCOSTAN - Follow Me on X
|
|
# For more info visit https://www.vcloudinfo.com/click-here
|
|
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
|
# -------------------------------------------------------------------
|
|
"""
|
|
|
|
|
|
class DashboardValidatorTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.tempdir = tempfile.TemporaryDirectory(prefix="dashboard-validator-")
|
|
self.root = Path(self.tempdir.name)
|
|
(self.root / "tools").mkdir()
|
|
(self.root / "config" / "packages").mkdir(parents=True)
|
|
(self.root / "config" / "dashboards" / "overview" / "views").mkdir(
|
|
parents=True
|
|
)
|
|
(self.root / "tools" / "dashboard_entity_allowlist.txt").write_text(
|
|
"# fixture allowlist\n", encoding="utf-8"
|
|
)
|
|
self._write(
|
|
"config/configuration.yaml",
|
|
"""
|
|
lovelace:
|
|
resource_mode: yaml
|
|
resources: !include dashboards/resources.yaml
|
|
dashboards:
|
|
lovelace:
|
|
mode: yaml
|
|
filename: ui-lovelace.yaml
|
|
""",
|
|
)
|
|
self._write(
|
|
"config/ui-lovelace.yaml",
|
|
"!include /config/dashboards/overview/dashboard.yaml\n",
|
|
dedent=False,
|
|
)
|
|
self._write("config/dashboards/resources.yaml", HEADER + "[]\n", dedent=False)
|
|
self._write(
|
|
"config/dashboards/overview/dashboard.yaml",
|
|
HEADER
|
|
+ "views: !include_dir_list /config/dashboards/overview/views\n",
|
|
dedent=False,
|
|
)
|
|
self._write(
|
|
"config/dashboards/overview/views/01_home.yaml",
|
|
HEADER
|
|
+ """\
|
|
title: Home
|
|
path: home
|
|
type: sections
|
|
sections:
|
|
- type: grid
|
|
cards:
|
|
- type: entity
|
|
entity: sensor.real
|
|
""",
|
|
dedent=False,
|
|
)
|
|
self._write(
|
|
"config/packages/entities.yaml",
|
|
"sensor.real\n",
|
|
dedent=False,
|
|
)
|
|
|
|
def tearDown(self) -> None:
|
|
self.tempdir.cleanup()
|
|
|
|
def _write(self, relative: str, content: str, *, dedent: bool = True) -> Path:
|
|
path = self.root / relative
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
value = textwrap.dedent(content).lstrip() if dedent else content
|
|
path.write_text(value, encoding="utf-8")
|
|
return path
|
|
|
|
def test_valid_fixture_builds_routes(self) -> None:
|
|
result = validate_repository(self.root)
|
|
self.assertEqual(result.routes, ("/lovelace", "/lovelace/home"))
|
|
self.assertIn("sensor.real", result.entity_references)
|
|
|
|
def test_malformed_dashboard_yaml_fails(self) -> None:
|
|
path = self.root / "config/dashboards/overview/dashboard.yaml"
|
|
path.write_text(path.read_text(encoding="utf-8") + "broken: [\n", encoding="utf-8")
|
|
with self.assertRaisesRegex(ValidationError, "invalid YAML"):
|
|
validate_repository(self.root)
|
|
|
|
def test_missing_entrypoint_include_fails(self) -> None:
|
|
self._write(
|
|
"config/ui-lovelace.yaml",
|
|
"!include /config/dashboards/overview/missing.yaml\n",
|
|
dedent=False,
|
|
)
|
|
with self.assertRaisesRegex(ValidationError, "missing include file"):
|
|
validate_repository(self.root)
|
|
|
|
def test_nonexistent_static_entity_fails(self) -> None:
|
|
path = self.root / "config/dashboards/overview/views/01_home.yaml"
|
|
path.write_text(
|
|
path.read_text(encoding="utf-8").replace("sensor.real", "sensor.missing"),
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValidationError, "sensor.missing"):
|
|
validate_repository(self.root)
|
|
|
|
def test_commented_include_is_ignored(self) -> None:
|
|
path = self.root / "config/dashboards/overview/dashboard.yaml"
|
|
path.write_text(
|
|
path.read_text(encoding="utf-8")
|
|
+ "# ignored: !include /config/dashboards/overview/missing.yaml\n",
|
|
encoding="utf-8",
|
|
)
|
|
validate_repository(self.root)
|
|
|
|
def test_duplicate_view_path_fails(self) -> None:
|
|
self._write(
|
|
"config/dashboards/overview/views/02_duplicate.yaml",
|
|
HEADER + "title: Duplicate\npath: home\ntype: sections\nsections: []\n",
|
|
dedent=False,
|
|
)
|
|
with self.assertRaisesRegex(ValidationError, "duplicate view path"):
|
|
validate_repository(self.root)
|
|
|
|
def test_orphaned_dashboard_file_fails(self) -> None:
|
|
self._write(
|
|
"config/dashboards/overview/orphan.yaml",
|
|
HEADER + "type: entity\nentity: sensor.real\n",
|
|
dedent=False,
|
|
)
|
|
with self.assertRaisesRegex(ValidationError, "orphaned dashboard YAML"):
|
|
validate_repository(self.root)
|
|
|
|
def test_duplicate_yaml_key_fails(self) -> None:
|
|
path = self.root / "config/dashboards/overview/views/01_home.yaml"
|
|
path.write_text(
|
|
path.read_text(encoding="utf-8") + "path: duplicate\n",
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValidationError, "duplicate key"):
|
|
validate_repository(self.root)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|