Le forfait était suivi brut, sans bonification. Considéré réalisé en
journée du jour où il est attribué, il reçoit désormais le facteur
journée correspondant : ×1 en semaine, ×1,5 le samedi, ×2 le dimanche
ou un jour férié (suit les règles de ratios configurées). Toujours hors
heures travaillées ; affiché pondéré sur la ligne du jour, dans les
totaux du récapitulatif et expliqué dans Réglages.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
116 lines
5.1 KiB
Python
116 lines
5.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
|
||
|
||
|
||
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
|