Files
rar-autopass/tests/test_web_selector_tema.py
Claude Agent 074b6e7c8a fix(5.10): logo ROMFAST in stanga header (ca romfast.ro) + tooltip tema doar numele temei
- US-012c: logo .brand-logo mutat in header-left (32px, aliniat stanga); env badge mutat sub titlu in header-center; titlul ramane centrat; responsiv pastrat.
- US-014b: title-ul butonului de tema = doar numele temei curente (Light/Dark/Petrol/Auto), fara enumerarea ciclului; aria-label informativ + aria-live pastrate (a11y).

Regresie 896 passed, 1 skipped, 0 failed.

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

180 lines
6.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."
)
# 6. US-014b (decizie user): title-ul vizual al butonului = DOAR numele temei, NU ciclul.
# Verificam in sursa JS ca variabila TOOLTIP_CICLU (sirul cu "Ciclu: Light → Dark → ...") nu
# mai e folosita in title. aria-label-ul informativ (curenta+urmatoarea) RAMANE neschimbat.
assert 'TOOLTIP_CICLU' not in html, (
"Variabila TOOLTIP_CICLU inca prezenta in JS — title-ul trebuie simplificat. "
"US-014b: btn.title trebuie sa fie DOAR LABELS[s] (ex. 'Light'), "
"nu 'Tema: X. Ciclu: Light -> Dark -> Petrol -> Auto'. "
"aria-label-ul informativ RAMANE neschimbat."
)
# Verifica ca JS-ul seteaza btn.title la doar LABELS[s] (fara prefix 'Tema:' sau ciclul)
assert re.search(r"btn\.title\s*=\s*LABELS\[", html), (
"btn.title nu e setat la LABELS[s] in JS. "
"US-014b: formatul asteptat: btn.title = LABELS[s] (ex. rezultat: 'Petrol'). "
"Aria-label-ul informativ ramane separat: 'Tema: X, apasa pentru Y'."
)