Files
rar-autopass/tests/test_web_selector_tema.py
Claude Agent 5a964a1a8d feat(5.10): UX trimiteri (pill filtre, paginare, editare) + Mapari in meniu + branding ROMFAST
14 stories TDD prin echipa de workeri (lead orchestreaza, 3 teammates pe valuri cu fisiere disjuncte; routes.py + base.html serializate ca fisiere fierbinti).

- US-001 fix filtrare data (_iso_date_prefix pe garda+comparatie, prinde timestamp cu ora)
- US-002/007 operatie service distincta in payload_view + afisare in detaliu
- US-003 pill-uri categorii (button/aria-pressed; needs_mapping --warn, needs_data/error --err); fara lista ID-uri/dropdown
- US-004 paginare numerotata 25/pag (total ramificat SQL-COUNT vs fetch-all+slice, clamp page, poll pastreaza pagina)
- US-005 VIN block-level sub nr
- US-006/006b editare cod RAR + validare nomenclator + recalcul idempotency (needs_data/needs_mapping via /corecteaza, error via /repune)
- US-008 card eroare 3-niveluri doar pe read-only + rezumat top-of-form
- US-009 Mapari in meniu hamburger; scoatere tab-bar + role=tablist orfan
- US-010/011 pagina Mapari consolidata + butoane icon SVG + dirty-state (fara kebab/emoji)
- US-012/012b header centrat + logo ROMFAST (/static/romfast_logo.png) in header
- US-013 paleta azur ROMFAST (#2E74D6/#1F66C9) + IBM Plex Sans/Mono self-host (woff2 reale)
- US-014 selector tema ciclic Light/Dark/Petrol/Auto + anti-FOUC pe 4 stari

Backend trimitere (worker/masina stari/idempotenta/mapping) + schema NEATINSE (UI/UX pur + 1 fix de filtrare).
VERIFY context curat PASS; /code-review high: 1 finding material reparat (US-006b). Regresie 896 passed, 1 skipped, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:20:58 +00:00

164 lines
5.9 KiB
Python

"""Teste US-014 (PRD 5.10): Selector de tema ciclic Light/Dark/Petrol/Auto.
TDD: testele se scriu INAINTE de implementare (RED), dupa implementare trec (GREEN).
Testeaza:
- test_petrol_theme_definit: [data-theme="petrol"] definit cu valorile din DESIGN.md
- test_buton_cicleaza_temele: buton ciclic + anti-FOUC extins + aria-live
"""
from __future__ import annotations
import os
import re
import tempfile
import pytest
from starlette.testclient import TestClient
@pytest.fixture()
def client(monkeypatch):
tmp = tempfile.mkdtemp()
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "selector.db"))
from app.config import get_settings
get_settings.cache_clear()
from app.main import app
with TestClient(app) as c:
yield c
get_settings.cache_clear()
def _get_style(html: str) -> str:
m = re.search(r"<style>(.*?)</style>", html, re.DOTALL)
assert m, "<style> negasit in HTML"
return m.group(1)
def _get_head(html: str) -> str:
m = re.search(r"<head>(.*?)</head>", html, re.DOTALL | re.IGNORECASE)
assert m, "<head> negasit in HTML"
return m.group(1)
# ── test_petrol_theme_definit ─────────────────────────────────────────────────
def test_petrol_theme_definit(client):
"""[data-theme="petrol"] definit in CSS cu valorile din DESIGN.md:
--bg:#0e1416, --card:#161e20, --ink:#e6e9ef, --muted:#8b93a7,
--line:#232c2e, --accent:#0E7C7B, --ok:#2FBF8F, --warn:#E0A93B, --err:#E05D5D
"""
resp = client.get("/login")
assert resp.status_code == 200
style = _get_style(resp.text)
# Bloc CSS [data-theme="petrol"] prezent
petrol_m = re.search(
r'\[data-theme=["\']petrol["\']\]\s*\{([^}]+)\}',
style,
re.DOTALL,
)
assert petrol_m, (
'[data-theme="petrol"] { ... } negasit in <style>. '
"Tema Petrol trebuie definita conform DESIGN.md."
)
petrol_block = petrol_m.group(1)
petrol_vars = {
"--accent": "#0E7C7B",
"--bg": "#0e1416",
"--card": "#161e20",
"--ink": "#e6e9ef",
"--line": "#232c2e",
"--ok": "#2FBF8F",
"--warn": "#E0A93B",
"--err": "#E05D5D",
}
for var, val in petrol_vars.items():
assert val.lower() in petrol_block.lower(), (
f"Variabila {var}:{val} lipseste din [data-theme=\"petrol\"]. "
f"Block petrol: {petrol_block.strip()}"
)
# ── test_buton_cicleaza_temele ────────────────────────────────────────────────
def test_buton_cicleaza_temele(client):
"""Butonul de tema cicleaza Light->Dark->Petrol->Auto si are accesibilitate completa.
Verifica:
1. Anti-FOUC extins: cunoaste 'petrol' si 'auto'; fallback definit pt. valori legacy
2. JS-ul ciclului contine toate cele 4 teme in ordinea corecta
3. aria-label pe buton include 'Tema:' + tema curenta + urmatoarea
4. Regiune aria-live="polite" prezenta pentru anuntarea schimbarii
"""
resp = client.get("/login")
assert resp.status_code == 200
html = resp.text
head = _get_head(html)
# 1. Anti-FOUC cunoaste 'petrol' si 'auto' (script in <head>, inainte de <style>)
style_pos = head.find('<style>')
assert style_pos >= 0, "<style> negasit in <head>"
head_before_style = head[:style_pos]
assert 'petrol' in head_before_style, (
"Scriptul anti-FOUC nu cunoaste tema 'petrol'. "
"Trebuie extins sa enumere toate cele 4 teme (light/dark/petrol/auto)."
)
assert 'auto' in head_before_style, (
"Scriptul anti-FOUC nu cunoaste tema 'auto'. "
"Trebuie sa rezolve 'auto' la light/dark inainte de primul paint."
)
# 2. Anti-FOUC are fallback pentru valori legacy/necunoscute (un set de valide)
# Acceptam: un obiect/array VALID, sau un if care verifica valorile cunoscute
has_valid_guard = (
'VALID' in head_before_style
or re.search(r'light.*dark.*petrol.*auto', head_before_style, re.DOTALL)
or 'indexOf' in head_before_style
)
assert has_valid_guard, (
"Anti-FOUC lipseste de un guard pentru valori legacy/necunoscute. "
"O valoare 'localStorage.theme' necunoscuta trebuie sa cada pe 'auto' sau 'dark'."
)
# 3. JS-ul din <body> contine ciclul complet Light->Dark->Petrol->Auto
# Cautam in tot HTML-ul (nu doar head) prezenta tuturor celor 4 teme in JS
# Acceptam: array explicit ['light','dark','petrol','auto'] sau logic echivalent
cycle_match = re.search(
r"['\"]light['\"].*['\"]dark['\"].*['\"]petrol['\"].*['\"]auto['\"]",
html,
re.DOTALL,
)
assert cycle_match, (
"Ciclul Light->Dark->Petrol->Auto negasit in JS. "
"Asteptat: array sau secventa cu toate cele 4 teme in ordine."
)
# 4. aria-label pe butonul tema-toggle include 'Tema:' (format 'Tema: X, apasa pentru Y')
tema_btn = re.search(
r'<button[^>]+id=["\']tema-toggle["\'][^>]*>',
html,
re.IGNORECASE,
)
assert tema_btn, "Butonul #tema-toggle negasit in HTML"
btn_tag = tema_btn.group(0)
# Acceptam aria-label setat initial in HTML SAU prin JS (aria-label din tag poate fi
# placeholder; testam ca JS-ul contine formatul corect)
has_tema_label = (
'Tema:' in html
or 'tema:' in html.lower()
or re.search(r'aria-label[^>]*[Tt]ema', html)
)
assert has_tema_label, (
"aria-label cu formatul 'Tema: ...' negasit in HTML/JS. "
"Butonul trebuie sa anunte tema curenta + urmatoarea."
)
# 5. Regiune aria-live="polite" prezenta (pentru anuntarea schimbarii de tema)
assert 'aria-live="polite"' in html or "aria-live='polite'" in html, (
'Regiune aria-live="polite" negasita in HTML. '
"Necesara pentru a anunta schimbarea temei catre screen-readers."
)