Étendre la préservation des formulaires, et corriger la publication

Suite de PR #31. `PersistentForm` couvre désormais les formulaires où la
saisie compte : ajout d'un salarié, d'un établissement, d'une équipe, d'une
entrée de registre, demande d'absence, heures réelles, correspondances de
paie, réglages d'envoi, conservation, dépôt de pièce.

Le composant gère `select`, `textarea`, cases et boutons radio, et reçoit
`resetAfter` : passé l'état retourné par l'action en cas de succès, le
formulaire revient aux valeurs rendues par le serveur — vide pour un ajout,
la valeur enregistrée pour un champ d'édition. C'est ce qui rend visible une
normalisation faite côté serveur.

Les champs cachés sont exclus. Ils ne portent jamais une saisie mais l'état
de l'application, et les rétablir écrase ce que le serveur vient de
renvoyer. Le cas s'est produit sur `expectedVersion`, le verrou optimiste
d'une semaine de planning.

En éprouvant cela, un défaut plus grave est apparu, antérieur et sans
rapport avec la préservation : le formulaire de publication recevait tantôt
l'action de publication, tantôt celle de dépublication selon l'état, et
cessait de suivre le changement. Lecture de l'en-tête `Next-Action` sur
trois envois consécutifs :

    1er envoi   60b8097e  (dépublier)  → brouillon
    2e  envoi   6012d40f  (publier)    → publiée
    3e  envoi   6012d40f  (publier)    → publiée

Le bouton affichait « Dépublier » et republiait, sans message d'erreur
puisque l'action réellement exécutée réussissait. Une seule action reçoit
maintenant l'intention, portée par le bouton déclencheur — un champ caché
serait remis à sa valeur d'origine par la réinitialisation que React
applique après chaque action, un bouton ne l'est jamais.

Le test du planning fait désormais deux allers-retours dans le même
chargement : le premier envoi d'une page emploie toujours la bonne action,
si bien qu'un seul aller-retour laissait passer le défaut selon l'état où
la semaine avait été trouvée. Vérifié : le test échoue sur l'ancien
câblage, passe sur le nouveau.

450 tests unitaires et d'intégration, 77 de bout en bout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
This commit is contained in:
Claude committed 2026-08-09 19:40:41 +00:00
1 parent f39e7a0552
commit b243df4772
15 files changed
+254 -69

No files matched your search

+22
View File
@@ -109,6 +109,28 @@ test('un manager pose un créneau, publie, puis dépublie', async ({ page }) =>
await section.getByRole('button', { name: 'Dépublier' }).click();
await expect(section.getByText('Brouillon')).toBeVisible();
// Un second aller-retour, sans recharger la page. Régression : le formulaire
// recevait tantôt l'action de publication, tantôt celle de dépublication
// selon l'état, et cessait de suivre le changement. Le bouton affichait
// « Dépublier » et republiait — sans message d'erreur, puisque l'action
// réellement exécutée réussissait.
//
// Le vérifier deux fois, et non une : le premier envoi d'un chargement
// emploie toujours la bonne action, si bien qu'un test qui s'arrêterait là
// laisserait passer le défaut selon l'état où la semaine a été trouvée.
// Le motif n'est plus demandé : l'alerte a été acquittée au passage
// précédent, et l'acquittement survit à la dépublication — c'est justement ce
// que garantit le report des acquittements d'une réévaluation à l'autre.
const motif = section.getByPlaceholder(/^Motif —/);
if (await motif.isVisible()) {
await motif.fill('Second passage, même chargement');
}
await section.getByRole('button', { name: 'Publier' }).click();
await expect(section.getByText('Publiée')).toBeVisible();
await section.getByRole('button', { name: 'Dépublier' }).click();
await expect(section.getByText('Brouillon')).toBeVisible();
});
test('la navigation de semaine change la grille', async ({ page }) => {