Files
leocrm/tests/test_frontend_assets.py
T

178 lines
5.7 KiB
Python
Raw Normal View History

2026-06-03 23:52:07 +00:00
"""Frontend-Asset Tests: verify JS modules and CSS are syntactically valid
and export / register the expected symbols.
These tests perform static text-level checks on the JS / CSS files — no
runtime execution, no nodejs dependency. They are sufficient to catch:
- Missing exports (e.g. `export { api, ApiError }` removed from api.js)
- Missing global Alpine.data() registrations for the components
- CSS variables removed from the custom-overrides file
"""
from __future__ import annotations
from pathlib import Path
import pytest
WEBUI = Path(__file__).resolve().parent.parent / "app" / "webui"
def _read(rel: str) -> str:
p = WEBUI / rel
assert p.exists(), f"Missing asset: {p}"
return p.read_text(encoding="utf-8")
# ---------------------------------------------------------------------------
# api.js
# ---------------------------------------------------------------------------
def test_api_js_exports_api_and_ApiError():
content = _read("js/api.js")
assert "export class ApiError" in content
assert "export const api" in content or "export default api" in content
def test_api_js_uses_localStorage_for_jwt():
"""R-1 / architecture: JWT must live in localStorage."""
content = _read("js/api.js")
assert "localStorage" in content
assert "jwt" in content
def test_api_js_handles_401_redirect():
"""401 must clear the token and redirect to /index.html."""
content = _read("js/api.js")
assert "401" in content
assert "/index.html" in content
def test_api_js_handles_204_no_content():
content = _read("js/api.js")
assert "204" in content
# ---------------------------------------------------------------------------
# store.js
# ---------------------------------------------------------------------------
def test_store_js_defines_auth_and_notifications():
content = _read("js/store.js")
assert "Alpine.store('auth'" in content
assert "Alpine.store('notifications'" in content
def test_store_js_auth_has_login_and_logout():
content = _read("js/store.js")
assert "login" in content
assert "logout" in content
assert "fetchUser" in content
def test_store_js_notifications_has_push_and_dismiss():
content = _read("js/store.js")
assert "push" in content
assert "dismiss" in content
assert "success" in content
assert "error" in content
# ---------------------------------------------------------------------------
# app.css
# ---------------------------------------------------------------------------
def test_app_css_contains_tailwind_overrides():
content = _read("css/app.css")
# The file defines custom CSS variables (--crm-primary, etc.)
assert "--crm-primary" in content
assert "--crm-success" in content
assert "--crm-danger" in content
def test_app_css_has_loading_spinner():
content = _read("css/app.css")
assert "crm-spinner" in content
assert "@keyframes crm-spin" in content
def test_app_css_has_toast_styles():
content = _read("css/app.css")
assert "crm-toast" in content
assert "toast-success" in content
assert "toast-error" in content
# ---------------------------------------------------------------------------
# Alpine components
# ---------------------------------------------------------------------------
COMPONENTS = [
("account-list.js", "accountList"),
("deal-kanban.js", "dealKanban"),
("activity-list.js", "activityList"),
("dashboard-kpis.js", "dashboardKpis"),
]
@pytest.mark.parametrize("filename,factory", COMPONENTS)
def test_alpine_component_defined(filename, factory):
"""Each component file must define a named factory and register it
globally with Alpine.data(name, factory)."""
content = _read(f"components/{filename}")
# factory function definition
assert f"export function {factory}" in content, \
f"{filename} missing `export function {factory}()`"
# Alpine.data() registration
assert f"Alpine.data('{factory}'" in content, \
f"{filename} missing `Alpine.data('{factory}', …)` registration"
def test_deal_kanban_handles_drag_and_drop():
content = _read("components/deal-kanban.js")
assert "draggable" in content or "dataTransfer" in content
assert "onDrop" in content
assert "/deals/" in content and "/stage" in content
def test_activity_list_supports_overdue_filter():
content = _read("components/activity-list.js")
assert "overdue" in content
assert "/activities/" in content
assert "/complete" in content
def test_dashboard_kpis_loads_both_kpis_and_feed():
content = _read("components/dashboard-kpis.js")
assert "/dashboard/kpis" in content
assert "/dashboard/feed" in content
def test_account_list_uses_pagination():
content = _read("components/account-list.js")
assert "skip" in content
assert "limit" in content
assert "q" in content
assert "/accounts/" in content
# ---------------------------------------------------------------------------
# notifications.js (toast component)
# ---------------------------------------------------------------------------
def test_notifications_js_registers_toast_container():
content = _read("js/components/notifications.js")
assert "toastContainer" in content
assert "Alpine.data" in content
# ---------------------------------------------------------------------------
# auth.js
# ---------------------------------------------------------------------------
def test_auth_js_exports_login_logout_register():
content = _read("js/auth.js")
assert "export async function login" in content
assert "export async function logout" in content
assert "export async function register" in content
assert "export async function refreshToken" in content