- 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>
180 lines
6.9 KiB
Python
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'."
|
|
)
|