131 lines
3.9 KiB
Python
131 lines
3.9 KiB
Python
"""
|
|
Frontend smoke tests — lightweight checks via curl + grep.
|
|
|
|
Verifies the server serves correct HTML, CSS, and tab structure.
|
|
Playwright is unavailable due to snap chromium incompatibility.
|
|
"""
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import pytest
|
|
|
|
BASE_URL = "https://canteen.ourpad.casa"
|
|
|
|
def _curl(path):
|
|
"""Fetch a URL and return (status_code, body)."""
|
|
url = f"{BASE_URL}{path}"
|
|
result = subprocess.run(
|
|
["curl", "-sk", "-w", "\n%{http_code}", url],
|
|
capture_output=True, text=True, timeout=10
|
|
)
|
|
lines = result.stdout.strip().split("\n")
|
|
status = int(lines[-1])
|
|
body = "\n".join(lines[:-1])
|
|
return status, body
|
|
|
|
|
|
class TestFrontendServes:
|
|
"""Basic server serving tests."""
|
|
|
|
def test_html_returns_200(self):
|
|
"""Homepage returns 200."""
|
|
status, _ = _curl("/")
|
|
assert status == 200
|
|
|
|
def test_has_title(self):
|
|
"""Page has correct title."""
|
|
_, body = _curl("/")
|
|
assert "<title>Canteen Asset Tracker</title>" in body
|
|
|
|
def test_has_doctype(self):
|
|
"""Returns valid HTML5."""
|
|
_, body = _curl("/")
|
|
assert body.strip().startswith("<!DOCTYPE html>")
|
|
|
|
def test_has_viewport_meta(self):
|
|
"""Has mobile viewport meta tag."""
|
|
_, body = _curl("/")
|
|
assert 'name="viewport"' in body
|
|
assert "user-scalable=no" in body
|
|
|
|
|
|
class TestFrontendUIElements:
|
|
"""Key UI elements present in the HTML."""
|
|
|
|
def test_has_hamburger_menu(self):
|
|
"""Header has hamburger menu button."""
|
|
_, body = _curl("/")
|
|
assert 'class="hamburger"' in body or "hamburger" in body
|
|
|
|
def test_has_tab_bar(self):
|
|
"""Has tab navigation."""
|
|
_, body = _curl("/")
|
|
# Check for tab-related class names
|
|
assert "tab" in body.lower()
|
|
|
|
def test_dark_theme(self):
|
|
"""Dark theme CSS variables are defined."""
|
|
_, body = _curl("/")
|
|
assert "var(--bg)" in body
|
|
|
|
def test_has_leaflet_js(self):
|
|
"""Leaflet map library is loaded."""
|
|
_, body = _curl("/")
|
|
assert "leaflet.js" in body or "leaflet" in body
|
|
|
|
def test_has_zxing_barcode(self):
|
|
"""Barcode scanning library is loaded."""
|
|
_, body = _curl("/")
|
|
assert "zxing" in body
|
|
|
|
def test_mobile_layout(self):
|
|
"""Page uses mobile-first max-width layout."""
|
|
_, body = _curl("/")
|
|
assert "max-width: 480px" in body
|
|
|
|
def test_has_drawer(self):
|
|
"""Has drawer/sidebar component."""
|
|
_, body = _curl("/")
|
|
assert "drawer" in body
|
|
|
|
|
|
class TestAPIEndpoints:
|
|
"""Key API endpoints are reachable."""
|
|
|
|
def test_health_endpoint(self):
|
|
"""Health check works."""
|
|
status, body = _curl("/health")
|
|
assert status == 200
|
|
assert '"status":"ok"' in body
|
|
|
|
def test_login_reachable(self):
|
|
"""Login endpoint is reachable (POST)."""
|
|
import subprocess
|
|
result = subprocess.run(
|
|
["curl", "-sk", "-o", "/dev/null", "-w", "%{http_code}",
|
|
"-X", "POST", "-H", "Content-Type: application/json",
|
|
"-d", '{"username":"admin","password":"changeme"}',
|
|
f"{BASE_URL}/api/auth/login"],
|
|
capture_output=True, text=True, timeout=10
|
|
)
|
|
status = int(result.stdout.strip())
|
|
assert status == 200, f"Login returned {status}"
|
|
|
|
def test_assets_listable(self):
|
|
"""Assets endpoint is reachable."""
|
|
status, _ = _curl("/api/assets")
|
|
assert status in (200, 401) # 401=needs auth, but reachable
|
|
|
|
def test_static_files(self):
|
|
"""Static asset files are served."""
|
|
status, _ = _curl("/static/index.html")
|
|
assert status == 404 # index.html is at root, not /static/
|
|
|
|
def test_frontend_loads_fast(self):
|
|
"""Frontend loads in under 2 seconds."""
|
|
import time
|
|
start = time.time()
|
|
_curl("/")
|
|
elapsed = time.time() - start
|
|
assert elapsed < 2.0, f"Frontend took {elapsed:.2f}s to load"
|