Files
NestQuest/tests/test_frontend.py
T
grok 89f517b3ad Add bundled fonts, token CSS, and local font-face loading
Add bundled fonts, token CSS, and local font-face loading

Co-authored-by: grok <24+grok@code.cubecraftcreations.com>
Co-committed-by: grok <24+grok@code.cubecraftcreations.com>
2026-09-20 11:16:17 -04:00

158 lines
5.4 KiB
Python

"""Tests for the Lovelace card toolchain and auto-registered resource."""
from __future__ import annotations
import json
from pathlib import Path
from conftest import wire_entry_to_registry
from custom_components.nestquest import async_setup, async_setup_entry
from custom_components.nestquest.frontend import (
BUNDLE_FILENAME,
BUNDLE_URL,
FRONTEND_URL_BASE,
WWW_DIR,
async_register_frontend,
)
REPO_ROOT = Path(__file__).parent.parent
FRONTEND_DIR = REPO_ROOT / "frontend"
BUNDLE_PATH = WWW_DIR / BUNDLE_FILENAME
CARD_TYPES = (
"nestquest-party-board-card",
"nestquest-quest-log-card",
"nestquest-admin-card",
)
def test_frontend_package_documents_vite_lit_build() -> None:
package = json.loads((FRONTEND_DIR / "package.json").read_text(encoding="utf-8"))
assert package["scripts"]["build"] == "vite build"
assert "lit" in package["dependencies"]
assert "typescript" in package["devDependencies"]
assert "vite" in package["devDependencies"]
def test_card_modules_exist_as_separate_sources() -> None:
src = FRONTEND_DIR / "src"
assert (src / "nestquest-cards.ts").is_file()
assert (src / "cards" / "party-board.ts").is_file()
assert (src / "cards" / "quest-log.ts").is_file()
assert (src / "cards" / "admin.ts").is_file()
entry = (src / "nestquest-cards.ts").read_text(encoding="utf-8")
assert "./cards/party-board" in entry
assert "./cards/quest-log" in entry
assert "./cards/admin" in entry
def test_card_sources_have_no_visual_editor() -> None:
for path in (FRONTEND_DIR / "src" / "cards").glob("*.ts"):
text = path.read_text(encoding="utf-8")
assert "getConfigElement" not in text
assert "setConfig" in text
assert "getCardSize" in text
def test_built_bundle_contains_all_three_cards() -> None:
assert BUNDLE_PATH.is_file()
bundle = BUNDLE_PATH.read_text(encoding="utf-8")
for card_type in CARD_TYPES:
assert card_type in bundle
async def test_setup_registers_static_path_and_extra_js(hass, make_entry) -> None:
entry = wire_entry_to_registry(make_entry(), hass.registry)
assert await async_setup_entry(hass, entry) is True
assert len(hass.http.static_paths) == 1
registered = hass.http.static_paths[0]
assert registered.url_path == FRONTEND_URL_BASE
assert registered.path == str(WWW_DIR)
assert hass.extra_js_urls == [BUNDLE_URL]
async def test_async_setup_registers_frontend_without_a_config_entry(hass) -> None:
assert await async_setup(hass, {}) is True
assert hass.extra_js_urls == [BUNDLE_URL]
assert hass.http.static_paths[0].url_path == FRONTEND_URL_BASE
async def test_frontend_registration_is_idempotent(hass, make_entry) -> None:
await async_register_frontend(hass)
await async_register_frontend(hass)
entry_a = wire_entry_to_registry(make_entry(entry_id="a"), hass.registry)
entry_b = wire_entry_to_registry(make_entry(entry_id="b"), hass.registry)
assert await async_setup_entry(hass, entry_a) is True
assert await async_setup_entry(hass, entry_b) is True
assert len(hass.http.static_paths) == 1
assert hass.extra_js_urls == [BUNDLE_URL]
def test_readme_documents_npm_build() -> None:
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
assert "npm run build" in readme
assert "nestquest-cards.js" in readme
WOFF2_FILES = (
"nunito-latin-400-normal.woff2",
"nunito-latin-600-normal.woff2",
"nunito-latin-700-normal.woff2",
"nunito-latin-800-normal.woff2",
"cinzel-latin-600-normal.woff2",
"cinzel-latin-700-normal.woff2",
"cinzel-latin-900-normal.woff2",
"cinzel-decorative-latin-700-normal.woff2",
"cinzel-decorative-latin-900-normal.woff2",
)
CDN_MARKERS = (
"fonts.googleapis.com",
"fonts.gstatic.com",
"cdn.jsdelivr.net",
"unpkg.com",
"cdnjs.cloudflare.com",
)
def test_www_contains_required_woff2_fonts() -> None:
fonts_dir = WWW_DIR / "fonts"
for name in WOFF2_FILES:
path = fonts_dir / name
assert path.is_file(), name
assert path.read_bytes()[:4] == b"wOF2"
def test_font_css_declares_swap_and_local_urls() -> None:
css = (WWW_DIR / "nestquest-fonts.css").read_text(encoding="utf-8")
assert css.count("font-display: swap") == 9
assert css.count("@font-face") == 9
assert 'font-family: "Nunito"' in css
assert 'font-family: "Cinzel"' in css
assert 'font-family: "Cinzel Decorative"' in css
for name in WOFF2_FILES:
assert f"./fonts/{name}" in css
assert "http://" not in css
assert "https://" not in css
def test_token_css_copied_verbatim_from_design() -> None:
design = REPO_ROOT / "design" / "tokens"
for name in ("nestquest-panel-tokens.css", "nestquest-admin-tokens.css"):
expected = (design / name).read_bytes()
actual = (WWW_DIR / name).read_bytes()
assert actual == expected
def test_frontend_and_www_have_no_cdn_font_references() -> None:
roots = (FRONTEND_DIR, WWW_DIR)
for root in roots:
for path in root.rglob("*"):
if not path.is_file():
continue
if "node_modules" in path.parts:
continue
if path.suffix.lower() in {".woff2", ".png", ".jpg"}:
continue
text = path.read_text(encoding="utf-8", errors="ignore")
for marker in CDN_MARKERS:
assert marker not in text, f"{path}: {marker}"