Files
pointeuse-optimisator/app/tests/test_ui_refonte.py
Jacquin Antoine e797098e79 Cibles : reprise ancrée au présent et rafraîchissement en direct
L'heure de reprise de pause n'est qu'indicative : le jour même, une
fois l'heure désirée passée, la cible d'un après-midi non commencé
ancre la reprise à maintenant et la sortie s'ajuste au reste à faire —
suivre les cibles mène au solde 0 exact, et aucune cible ne recommande
de reprendre dans le passé.

La page semaine rafraîchit cibles, panneau Aujourd'hui et compteurs
toutes les 30 s, et immédiatement au retour sur l'onglet (les
minuteurs des navigateurs sont gelés en arrière-plan, l'affichage ne
peit plus rester périmé).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-09-11 14:29:50 +02:00

153 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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
def test_retour_decale_affiche_le_credit_de_presence(monkeypatch):
"""Retour mercredi 11:00 (event mardi 22:39→23:49) : la colonne Travaillé
crédite les 2h de présence couvertes, avec badge et détail explicites."""
monkeypatch.setattr(main, "datetime", _FrozenDatetime)
cookies = _login("u-ui-credit")
client.post("/astreinte/2026-07-14/toggle", cookies=cookies, follow_redirects=False)
r = client.post("/pointage/2026-07-14", cookies=cookies,
data={"astreinte_debut": ["22:39"], "astreinte_fin": ["23:49"]},
headers={"HX-Request": "true"}, follow_redirects=False)
assert r.status_code == 200
# Le crédit s'affiche sur la ligne du lendemain (jour du retour décalé)
r = client.get("/semaine/2026/29", cookies=cookies, follow_redirects=False)
assert r.status_code == 200
assert 'class="calc-couvert"' in r.text
assert "+2h00 repos" in r.text
assert "matin 09:00→11:00" in r.text # détail des plages couvertes
assert "de présence comptées" in r.text # rappel dans le bloc astreinte
def test_forfait_bonifie_journee_affiche_dans_le_recap(monkeypatch):
"""Forfait 1h00 réglé + AS le samedi 18/07 : le récapitulatif montre le
forfait pondéré par la bonification journée du samedi (×1,5 → 1h30)."""
monkeypatch.setattr(main, "datetime", _FrozenDatetime)
cookies = _login("u-ui-forfait")
r = client.post("/settings/astreinte-forfait", cookies=cookies,
data={"astreinte_forfait": "01:00"}, follow_redirects=False)
assert r.status_code == 303, r.text
client.post("/astreinte/2026-07-18/toggle", cookies=cookies, follow_redirects=False)
r = client.get("/semaine/2026/29", cookies=cookies, follow_redirects=False)
assert r.status_code == 200, r.text
assert "forfait 1h00 → 1h30 pondérées" in r.text # ligne du jour (bloc AS)
assert "forfaits 1h00 → 1h30 pondérés" in r.text # totaux du récapitulatif
assert "considéré réalisé en journée" in r.text # explication au survol
def test_refresh_regenerere_les_cibles_en_fonction_de_lheure(monkeypatch):
"""Le poll /refresh (toutes les 30 s, et au retour sur l'onglet) régénère
les cibles OOB avec l'heure courante : à 14:12, l'après-midi non commencé
du vendredi ancre sa reprise à maintenant — jamais à l'heure désirée 13:30
passée — et la sortie s'ajuste au reste à faire de la semaine."""
class _Frozen14(dt_module.datetime):
@classmethod
def today(cls):
return cls(2026, 7, 17, 14, 12)
monkeypatch.setattr(main, "datetime", _Frozen14)
import calcul
monkeypatch.setattr(calcul, "datetime", _Frozen14)
cookies = _login("u-ui-refresh")
# Lundi-jeudi complets, vendredi matin seulement : reste 4h42,
# seul créneau ouvert = vendredi aprem, reprise ancrée à 14:12.
for d in ("2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"):
client.post(f"/pointage/{d}", cookies=cookies,
data={"matin_entree": "09:00", "matin_sortie": "12:24",
"aprem_entree": "13:30", "aprem_sortie": "17:54"},
headers={"HX-Request": "true"}, follow_redirects=False)
client.post(f"/pointage/{TODAY}", cookies=cookies,
data={"matin_entree": "09:00", "matin_sortie": "12:00"},
headers={"HX-Request": "true"}, follow_redirects=False)
r = client.get(f"/refresh/{TODAY}", cookies=cookies, follow_redirects=False)
assert r.status_code == 200
# Cible du jour régénérée, ancrée à maintenant (14:12), pas à 13:30
assert f'id="calc-{TODAY}-cible"' in r.text
assert "A 14:12 → 19:00" in r.text
# Toutes les cibles de la semaine suivent (swaps OOB), pas seulement le jour
for d in ("2026-07-13", "2026-07-14", "2026-07-15", "2026-07-16"):
assert f'id="calc-{d}-cible"' in r.text
# Le panneau aujourd'hui est rafraîchi avec la même sortie cible
assert "today-panel" in r.text
assert "19:00" in r.text