Files
rar-autopass/tests/test_web_nav.py
Claude Agent 283299ff20 feat(ux): import compact + preview format Trimiteri + navigatie + scoatere auto_send (5.11)
8 stories TDD (echipa Sonnet, lead orchestreaza). US-001 scoate hold-ul auto_send din mapare
(has_no_auto_send->False, simbol pastrat; cod rezolvat->queued). US-002 scoate bifa auto_send
din UI. US-003 preview pas 3 in format .tabel-trimiteri (STARI_PREVIEW + nota_umana_preview,
fara repr Python; view-model prez). US-004 filtre layout/stil ca referinta + buton Custom.
US-005 navigatie Trimiteri/Mapari sub contoare pe toate paginile. US-006 import <details> nativ
colapsabil. US-007 post-commit reveal (OOB _coada/_status + HX-Trigger). US-008 auto-refresh
dupa actiuni (nudge eliminat).

VERIFY context curat PASS (8/8). /code-review high: 3 buguri reparate (tab nav la self-refresh,
pill Custom valori stale, nota_umana_preview precedenta needs_mapping). 934 passed, 1 skipped.
Backend trimitere + schema NEATINSE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:16:28 +00:00

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 "Gateway RAR AUTOPASS" trebuie sa fie si el in interiorul unui <a href="/">
# (PRD AC: Logo-ul ROMFAST + titlul linkeaza la /)
assert re.search(r'<a\b[^>]*href="/"[^>]*>.*?Gateway RAR AUTOPASS', header_html, re.DOTALL), (
"Titlul 'Gateway RAR AUTOPASS' trebuie sa fie intr-un <a href='/'> in header."
)