- Chaque heure validée enregistre le jour (plus de bouton ✓), avec un témoin
« enregistré » et conservation du focus entre les champs
- Les boutons MA/AM/J/F/AS deviennent un bouton d'état unique à pastilles
ouvrant un menu déroulant (congés, férié, astreinte)
- Panneau Aujourd'hui : sortie cible en grand et progression travaillé/dû
de la semaine, rafraîchis à chaque saisie
- Polish général : coins arrondis, ombres douces, hiérarchie des soldes,
états de focus visibles
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
79 lines
3.1 KiB
Python
79 lines
3.1 KiB
Python
"""Refonte de l'interface : auto-enregistrement (plus de bouton ✓), menu
|
|
d'état unique (congé / férié / astreinte) et panneau aujourd'hui.
|
|
|
|
Vérifie le rendu de la page semaine et les réponses HTMX (swaps OOB) qui
|
|
régénèrent ces composants.
|
|
"""
|
|
import datetime as dt_module
|
|
import sys
|
|
|
|
# DATA_DIR temporaire est positionné par conftest.py avant l'import de models.
|
|
import models # noqa: F401
|
|
|
|
sys.path.insert(0, ".")
|
|
import main
|
|
from fastapi.testclient import TestClient
|
|
|
|
client = TestClient(main.app)
|
|
|
|
TODAY = "2026-07-17" # vendredi de la semaine 29 (cf. datetime gelé)
|
|
|
|
|
|
class _FrozenDatetime(dt_module.datetime):
|
|
@classmethod
|
|
def today(cls):
|
|
return cls(2026, 7, 17, 10, 30)
|
|
|
|
|
|
def _login(uid: str) -> dict:
|
|
import bcrypt
|
|
models.save_auth(uid, email=f"{uid}@x.fr",
|
|
password_hash=bcrypt.hashpw(b"x" * 12, bcrypt.gensalt()).decode())
|
|
r = client.post("/login/password",
|
|
data={"email": f"{uid}@x.fr", "password": "x" * 12},
|
|
follow_redirects=False)
|
|
assert r.status_code == 302, r.text
|
|
return {"user_id": r.cookies.get("user_id")}
|
|
|
|
|
|
def test_page_semaine_rend_les_nouveaux_composants(monkeypatch):
|
|
monkeypatch.setattr(main, "datetime", _FrozenDatetime)
|
|
cookies = _login("u-ui")
|
|
r = client.get("/semaine/2026/29", cookies=cookies, follow_redirects=False)
|
|
assert r.status_code == 200
|
|
# Auto-enregistrement : un témoin par jour, plus de bouton ✓ ni sa colonne
|
|
assert "btn-valider" not in r.text
|
|
assert "col-save" not in r.text
|
|
assert r.text.count('class="autosave"') == 7
|
|
assert 'hx-trigger="save-day"' in r.text
|
|
# Menu d'état : un par jour, options des cinq bascules
|
|
assert r.text.count('class="cg-menu"') == 7
|
|
assert "Congé matin" in r.text
|
|
assert "Jour férié" in r.text
|
|
assert 'cg-opt-chip as">AS</span>' in r.text # option jour d'astreinte
|
|
# Panneau aujourd'hui (le jour gelé appartient à la semaine affichée)
|
|
assert "today-panel" in r.text
|
|
assert "Aujourd'hui ·" in r.text # Jinja laisse l'apostrophe telle quelle
|
|
|
|
|
|
def test_pointage_htmx_rafrachit_panneau_et_compteurs(monkeypatch):
|
|
monkeypatch.setattr(main, "datetime", _FrozenDatetime)
|
|
cookies = _login("u-ui-pointage")
|
|
r = client.post(f"/pointage/{TODAY}", cookies=cookies,
|
|
data={"matin_entree": "08:00", "matin_sortie": "12:00"},
|
|
headers={"HX-Request": "true"}, follow_redirects=False)
|
|
assert r.status_code == 200
|
|
assert f'id="calc-{TODAY}-trav"' in r.text # compteurs OOB
|
|
assert "today-panel" in r.text # soldes + panneau re-rendus
|
|
|
|
|
|
def test_bascule_congen_regenere_le_menu_d_etat(monkeypatch):
|
|
monkeypatch.setattr(main, "datetime", _FrozenDatetime)
|
|
cookies = _login("u-ui-conge")
|
|
r = client.post(f"/conge/{TODAY}/matin", cookies=cookies,
|
|
headers={"HX-Request": "true"}, follow_redirects=False)
|
|
assert r.status_code == 200
|
|
# Le swap OOB régénère le bouton avec la pastille MA active
|
|
assert 'cg-chip st-ma">MA<' in r.text
|
|
assert 'id="cg-btn-' + TODAY + '"' in r.text
|