Titlu pagina, antet brand si /login afiseaza acum 'ROA AUTOPASS'. Include redesignul sectiunii Problem+Calculator combinata din landing. Teste de antet/nav aliniate la noul nume. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
232 lines
8.6 KiB
Python
232 lines
8.6 KiB
Python
"""Teste US-005 (PRD 5.11): Link-uri navigatie sub contoare pe toate paginile.
|
|
|
|
TDD — testele sunt scrise INAINTE de implementare (RED), apoi devin GREEN.
|
|
|
|
Verifica:
|
|
- Sub contoare (#status-bar): rand cu link Trimiteri + Mapari (badge needs_mapping)
|
|
- Marcaj activ pe pagina curenta, via variabila de context tab_activ
|
|
- Logo ROMFAST + titlu linkeaza la / (Trimiteri)
|
|
- Hamburger capata Trimiteri (Acasa) ca prima intrare
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import tempfile
|
|
|
|
import pytest
|
|
from starlette.testclient import TestClient
|
|
|
|
|
|
# ============================================================
|
|
# Helpers
|
|
# ============================================================
|
|
|
|
def _create_account_user(email: str, password: str = "parolasecreta10"):
|
|
from app.accounts import create_account
|
|
from app.users import create_user
|
|
from app.db import get_connection
|
|
|
|
conn = get_connection()
|
|
try:
|
|
acct_id = create_account(conn, f"Service {email}", active=True)
|
|
create_user(conn, acct_id, email, password)
|
|
return acct_id
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def _login(client, email: str, password: str = "parolasecreta10") -> None:
|
|
resp = client.get("/login")
|
|
m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) or \
|
|
re.search(r'value="([^"]+)"\s+name="csrf_token"', resp.text)
|
|
assert m, "csrf_token negasit pe /login"
|
|
resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)})
|
|
assert resp.status_code == 303, f"Login esuat: {resp.status_code}"
|
|
|
|
|
|
def _ins(acct: int, *, status: str = "needs_mapping") -> int:
|
|
from app.db import get_connection
|
|
|
|
conn = get_connection()
|
|
try:
|
|
cur = conn.execute(
|
|
"INSERT INTO submissions (idempotency_key, account_id, status, payload_json) VALUES (?, ?, ?, ?)",
|
|
(
|
|
f"k-nav-{os.urandom(4).hex()}",
|
|
acct,
|
|
status,
|
|
json.dumps({
|
|
"vin": "WVWZZZ1KZAW001001",
|
|
"nr_inmatriculare": "B001NAV",
|
|
"data_prestatie": "2026-06-20",
|
|
"odometru_final": "100000",
|
|
"prestatii": [{"cod_prestatie": "OE-1"}],
|
|
}),
|
|
),
|
|
)
|
|
conn.commit()
|
|
return int(cur.lastrowid)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
# ============================================================
|
|
# Fixture
|
|
# ============================================================
|
|
|
|
@pytest.fixture()
|
|
def client(monkeypatch):
|
|
"""Client cu BD izolata + auth activat (login real)."""
|
|
tmp = tempfile.mkdtemp()
|
|
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "nav_us005.db"))
|
|
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "true")
|
|
from app.config import get_settings
|
|
get_settings.cache_clear()
|
|
from app.web import ratelimit
|
|
ratelimit._hits.clear()
|
|
from app.main import app
|
|
|
|
with TestClient(app, follow_redirects=False) as c:
|
|
yield c
|
|
ratelimit._hits.clear()
|
|
get_settings.cache_clear()
|
|
|
|
|
|
# ============================================================
|
|
# test_nav_trimiteri_mapari_pe_mapari
|
|
# ============================================================
|
|
|
|
def test_nav_trimiteri_mapari_pe_mapari(client):
|
|
"""La tab=mapari, linkul Mapari e marcat activ (aria-current='page').
|
|
Linkul Trimiteri e prezent si inactiv.
|
|
Badge-ul needs_mapping apare pe linkul Mapari.
|
|
"""
|
|
acct = _create_account_user("nav1@test.com")
|
|
_ins(acct, status="needs_mapping") # badge pentru Mapari
|
|
_login(client, "nav1@test.com")
|
|
|
|
# Testam direct fragmentul status cu tab=mapari (asa il vede HTMX dupa implementare)
|
|
resp = client.get("/_fragments/status?tab=mapari")
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
|
|
# Linkul Trimiteri (href="/") trebuie sa fie prezent
|
|
assert 'href="/"' in html, (
|
|
"Linkul Trimiteri (href='/') trebuie sa fie prezent sub contoare in #status-bar"
|
|
)
|
|
|
|
# Linkul Mapari trebuie sa fie prezent
|
|
assert '/?tab=mapari' in html, (
|
|
"Linkul Mapari (/?tab=mapari) trebuie sa fie prezent sub contoare in #status-bar"
|
|
)
|
|
|
|
# Mapari trebuie sa fie marcat activ (aria-current="page" pe elementul Mapari)
|
|
assert 'aria-current="page"' in html, (
|
|
"Linkul Mapari trebuie sa fie marcat activ cu aria-current='page' la tab=mapari"
|
|
)
|
|
|
|
# Trimiteri NU trebuie sa fie marcat activ (alta pagina e activa)
|
|
# Gasim tag-ul <a href="/"> si verificam ca NU contine aria-current
|
|
m = re.search(r'<a\b[^>]*href="/"[^>]*>', html)
|
|
assert m is not None, "Tag-ul <a href='/'> (Trimiteri) negasit in HTML"
|
|
tag_trimiteri = m.group(0)
|
|
assert 'aria-current' not in tag_trimiteri, (
|
|
f"Trimiteri NU trebuie sa fie activ la tab=mapari — tag gasit: {tag_trimiteri}"
|
|
)
|
|
|
|
|
|
# ============================================================
|
|
# test_nav_trimiteri_pe_jurnal
|
|
# ============================================================
|
|
|
|
def test_nav_trimiteri_pe_jurnal(client):
|
|
"""La tab=jurnal, ambele linkuri (Trimiteri + Mapari) sunt prezente,
|
|
dar niciunul nu e marcat activ (jurnal e pagina curenta).
|
|
"""
|
|
acct = _create_account_user("nav2@test.com")
|
|
_login(client, "nav2@test.com")
|
|
|
|
resp = client.get("/_fragments/status?tab=jurnal")
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
|
|
# Ambele linkuri trebuie sa fie prezente
|
|
assert 'href="/"' in html, "Linkul Trimiteri (href='/') trebuie sa fie prezent la tab=jurnal"
|
|
assert '/?tab=mapari' in html, "Linkul Mapari trebuie sa fie prezent la tab=jurnal"
|
|
|
|
# La tab=jurnal, niciun link din nav nu trebuie sa aiba aria-current="page"
|
|
# (jurnal e tab-ul curent, nu Trimiteri si nici Mapari)
|
|
assert 'aria-current="page"' not in html, (
|
|
"La tab=jurnal, niciun link din status-nav nu trebuie sa fie aria-current='page'"
|
|
)
|
|
|
|
|
|
# ============================================================
|
|
# test_fragment_status_pastreaza_tab_la_self_refresh
|
|
# ============================================================
|
|
|
|
def test_fragment_status_pastreaza_tab_la_self_refresh(client):
|
|
"""Fragmentul status randat cu tab=mapari contine hx-get cu tab=mapari.
|
|
|
|
BUG 1: fara ?tab=, la poll-ul every 15s _status.html se re-randeaza fara
|
|
tab_activ -> nav-ul marcheaza gresit Trimiteri activ chiar daca userul e
|
|
pe ?tab=mapari. Fix: hx-get='/_fragments/status?tab={{ tab_activ }}'
|
|
in _status.html.
|
|
"""
|
|
acct = _create_account_user("refresh@test.com")
|
|
_login(client, "refresh@test.com")
|
|
|
|
resp = client.get("/_fragments/status?tab=mapari")
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
|
|
# Fragmentul randat cu tab=mapari trebuie sa-si includa propriul hx-get
|
|
# cu tab=mapari, ca la self-refresh (every 15s / trimiteriChanged) sa
|
|
# primeasca acelasi tab si sa marcheze corect Mapari activ.
|
|
assert 'hx-get="/_fragments/status?tab=mapari"' in html, (
|
|
"status-bar randat cu tab=mapari trebuie sa contina hx-get cu tab=mapari "
|
|
"in el insusi (altfel self-refresh-ul pierde tab-ul activ)"
|
|
)
|
|
|
|
|
|
# ============================================================
|
|
# test_logo_linkeaza_acasa
|
|
# ============================================================
|
|
|
|
def test_logo_linkeaza_acasa(client):
|
|
"""Logo-ul ROMFAST (brand-logo) linkeaza la / (Trimiteri).
|
|
|
|
Logo-ul trebuie sa fie invelit intr-un <a href='/'> in header.
|
|
Aceasta face logo-ul clickabil pe toate paginile.
|
|
"""
|
|
acct = _create_account_user("logo@test.com")
|
|
_login(client, "logo@test.com")
|
|
|
|
resp = client.get("/")
|
|
assert resp.status_code == 200
|
|
html = resp.text
|
|
|
|
# Extrage sectiunea <header> pentru a izola cautarea
|
|
m_header = re.search(r'<header\b[^>]*>(.*?)</header>', html, re.DOTALL)
|
|
assert m_header, "<header> negasit in HTML"
|
|
header_html = m_header.group(1)
|
|
|
|
# Logo brand-logo trebuie sa fie prezent in header
|
|
assert 'brand-logo' in header_html, "Clasa brand-logo trebuie sa fie in header"
|
|
|
|
# Logo trebuie sa fie invelit intr-un <a href="/">
|
|
# Verificam: exista un <a href="/"> care contine class="brand-logo" in interiorul sau
|
|
assert re.search(r'<a\b[^>]*href="/"[^>]*>.*?brand-logo', header_html, re.DOTALL), (
|
|
"Logo-ul (class='brand-logo') trebuie sa fie intr-un <a href='/'> in header. "
|
|
"In prezent logo-ul nu e un link."
|
|
)
|
|
|
|
# Titlul "ROA AUTOPASS" trebuie sa fie si el in interiorul unui <a href="/">
|
|
# (PRD AC US-010: Logo-ul ROMFAST + titlul linkeaza la /; titlul a fost redenumit in 5.16)
|
|
assert re.search(r'<a\b[^>]*href="/"[^>]*>.*?ROA AUTOPASS', header_html, re.DOTALL), (
|
|
"Titlul 'ROA AUTOPASS' trebuie sa fie intr-un <a href='/'> in header."
|
|
)
|