"""Teste PRD 5.8 US-008: detaliul trimiterii apare ca rand expandabil SUB randul selectat (nu in panoul global de la baza tabelului). Verificam markup-ul server-side: fiecare rand de date are un rand-sibling de detaliu `` cu container per-rand `#detaliu-{id}`, randul clickabil tinteste acel container, iar fragmentul de detaliu (Inchide + forme) tinteste tot containerul per-rand — NU `#trimitere-detaliu` global. Single-open + pauza poll sunt logica JS in base.html (verificam prezenta hook-urilor). """ from __future__ import annotations import json import os import re import tempfile import pytest from starlette.testclient import TestClient def _create_account_user(email: str, name: str = "Service", 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, name, 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 resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)}) assert resp.status_code == 303 def _insert_submission(acct: int, status: str = "sent", *, payload: dict | None = None) -> int: from app.db import get_connection conn = get_connection() try: p = payload if payload is not None else { "vin": "WVWZZZ1JZXW000777", "nr_inmatriculare": "B777ZZZ", "data_prestatie": "2026-06-18", "odometru_final": "55000", "prestatii": [{"cod_prestatie": "R-FRANE", "denumire": "Reparatie frane"}], } cur = conn.execute( "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) " "VALUES (?, ?, ?, ?)", (f"k-{status}-{os.urandom(4).hex()}", acct, status, json.dumps(p)), ) conn.commit() return int(cur.lastrowid) finally: conn.close() @pytest.fixture() def client(monkeypatch): tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "subm.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() def test_fragment_detaliu_se_randeaza_in_container_pe_rand(client): """Tabelul are un rand-sibling de detaliu per rand (#detaliu-{id}), iar fragmentul de detaliu tinteste acel container, nu panoul global #trimitere-detaliu.""" acct = _create_account_user("inl@test.com") sid = _insert_submission(acct, "needs_data") _login(client, "inl@test.com") # 1. Tabelul: rand-sibling de detaliu + retargeting pe randul clickabil lista = client.get("/_fragments/submissions") assert lista.status_code == 200 h = lista.text assert 'class="detaliu-rand"' in h, "lipseste randul-sibling de detaliu" assert f'id="detaliu-{sid}"' in h, "lipseste containerul per-rand" assert 'colspan="8"' in h, "td-ul de detaliu trebuie sa acopere cele 8 coloane" assert f'hx-target="#detaliu-{sid}"' in h, "randul de date trebuie sa tinteasca containerul per-rand" # randul de date NU mai tinteste panoul global assert 'hx-target="#trimitere-detaliu"' not in h # 2. Fragmentul de detaliu: Inchide + forme tintesc containerul per-rand det = client.get(f"/_fragments/trimitere/{sid}") assert det.status_code == 200 d = det.text # butonul Inchide opereaza pe containerul randului curent (nu pe panoul global) assert f"detaliu-{sid}" in d assert "getElementById('trimitere-detaliu')" not in d # formele de corectie/mapare tintesc containerul per-rand assert f'hx-target="#detaliu-{sid}"' in d assert 'hx-target="#trimitere-detaliu"' not in d def test_un_singur_detaliu_deschis(client): """Logica JS din base.html asigura un singur detaliu deschis (inchide celelalte la deschidere) si pune poll-ul pe pauza cat un rand e expandat (D-eng-2).""" _create_account_user("one@test.com") _login(client, "one@test.com") pagina = client.get("/") assert pagina.status_code == 200 js = pagina.text # randul clickabil e accesibil (role/aria pentru toggle) assert 'class="trimitere-row"' not in js or True # markup-ul randului traieste in fragment # hook-uri de single-open: inchiderea altor detalii + sincronizarea starii aria assert "closeAllDetalii" in js, "lipseste logica de inchidere a celorlalte detalii" assert "detaliu-rand" in js, "logica trebuie sa opereze pe randurile de detaliu" assert "aria-expanded" in js, "starea expandata trebuie sincronizata" # pauza poll cat un rand e deschis: anuleaza request-ul periodic pe #submissions-wrap assert "submissions-wrap" in js assert "preventDefault" in js def test_rand_clickabil_accesibil(client): """Randul de date e focusabil la tastatura (role=button, tabindex, aria-expanded).""" acct = _create_account_user("a11y@test.com") sid = _insert_submission(acct, "sent") _login(client, "a11y@test.com") h = client.get("/_fragments/submissions").text # randul de date m = re.search(r'' % sid, h, re.S) assert m, "lipseste randul de date" rand = m.group(0) assert 'role="button"' in rand assert 'tabindex="0"' in rand assert 'aria-expanded="false"' in rand