Cause : le serveur émet 'round-result' (déclenche l'animation de révélation
des cartes/gains sur le plateau Phaser, ~3.5s avant le tour suivant) et
'game-state' en phase 'resolve' (déjà avec les jauges/scores post-résolution
via getPlayersInfo) au même instant. Le HUD Vue (alimenté directement par
gameStore.players) sautait donc à ses nouvelles valeurs INSTANTANÉMENT, en
même temps que l'animation de résolution démarrait tout juste sur le
plateau — les compteurs changeaient avant/pendant que le joueur voie
l'animation qui justifie ce changement, au lieu d'être synchronisés avec
elle.
Fix : le store Pinia (client/src/stores/game.js) gèle désormais
players/currentTurn pendant toute la phase 'resolve' — les valeurs reçues
sont stockées dans un buffer interne (_pendingPlayers/_pendingCurrentTurn)
au lieu d'être appliquées immédiatement, et ne sont propagées à l'affichage
qu'au game-state suivant dont la phase n'est PLUS 'resolve' (typiquement
'selecting' du tour suivant, ou 'end'). Le reste de l'état (phase, main,
objets/bâtiments/événements) continue de se mettre à jour normalement et
immédiatement — seul l'affichage des jauges/scores est différé.
6 nouveaux tests unitaires (game.store.test.js) couvrant le gel/dégel du
buffer, la rediffusion de plusieurs game-state en phase 'resolve'
successifs, la transition directe vers 'end', et le nettoyage du buffer
par reset().
Nouveau test E2E (hud-resolution-timing.spec.js) confirmé discriminant :
échoue de façon reproductible (2/2 tentatives) sans le fix ("0 pts" attendu
pendant resolve, "5 pts" déjà affiché), passe systématiquement avec le fix
(stable sur 3 répétitions). Attente active de la phase 'resolve' via
expect.poll plutôt qu'un délai fixe, pour éviter la fenêtre d'observation
manquée sous charge variable.
60 tests unitaires client + 166 tests unitaires serveur + suite
game-flow.spec.js (4 tests, non liée à ce fix) toujours au vert.
Trois bugs fonctionnels réels découverts et corrigés en stabilisant les
tests E2E des cartes Événement :
1. client/src/stores/game.js — le store Pinia n'exposait JAMAIS les champs
liés aux bâtiments (myBuildings, pendingBuyBuildingId, availableBuildings)
ni aux événements (myEventHand, eventDeckSize, hasPendingEventDraw,
pendingEventPlayId) : updateState() ne les lisait pas depuis le
'game-state' serveur. Conséquence concrète : GameScene._epidemicNeedsTarget()
lisait this.store.myBuildings (toujours vide) pour décider si la
variante Église d'Épidémie s'applique — le ciblage de la variante Trousse
ne fonctionnait donc jamais correctement dès qu'un joueur possédait
aussi le Château. Corrigé + 4 nouveaux tests unitaires Vitest.
2. client/src/game/scenes/GameScene.js — GameScene._onEventCardSelected
envoyait encore les clés historiques gainTargetSocketId/
loseTargetSocketId/targetSocketId au serveur, alors que Room.js avait été
migré vers gainTargetTeamIndex/loseTargetTeamIndex/targetTeamIndex
(socketId n'est jamais exposé au client). Conséquence : le ciblage
d'Accord royal et de la variante Trousse d'Épidémie était intégralement
cassé en jeu réel — les clés ne correspondant à rien côté serveur, aucune
cible n'était jamais appliquée, silencieusement.
3. e2e/tests/helpers.js — drawEventUntil abandonnait immédiatement dès que
la main d'événements atteignait 2 cartes sans contenir l'id recherché,
rendant les tests E2E d'événements extrêmement flaky (avec seulement 6
types d'événements et une main de 2, l'échec dès les 2 premières pioches
est fréquent, pas rare). Corrigé : joue désormais une carte pour libérer
un slot et continue à piocher jusqu'à maxDraws (20).
Stabilisation supplémentaire :
- Toutes les fonctions d'introspection Phaser dans helpers.js sécurisées
(optional chaining) contre une scène pas encore initialisée après un
changement de tour/navigation sous charge (plusieurs pages Playwright
simultanées).
- Timeouts des tests events.spec.js augmentés (150-240s) pour laisser le
temps aux boucles d'accumulation de jauge + pioche aléatoire de converger.
- playwright.config.js : retries=1 même en local, pour absorber la
variance résiduelle des tests basés sur un tirage aléatoire, tout en
gardant une détection fiable des vraies régressions (qui échoueraient de
façon reproductible sur les deux tentatives).
- events.spec.js : test 'Accord royal' fournit désormais les 2 cibles
requises par le ciblage séquentiel (gain + perte) au lieu d'une seule,
ce qui laissait la séquence de ciblage bloquée sans jamais émettre
'play-event'.
Résultat : 166 tests unitaires serveur + 28 tests unitaires client
toujours au vert ; suite events.spec.js (9 tests E2E) passe intégralement
(certains via retry probabiliste documenté, comportement attendu vu la
pioche aléatoire dans le deck partagé).
- Fix: UIScene n'était jamais lancée (active: false, jamais scene.launch)
donc l'event 'game-over' du serveur n'était jamais affiché — la partie
semblait ne pas avoir de fin. Remplacé par un panneau Vue dans GameView
(vainqueur, classement trié, bouton Quitter via vue-router). UIScene.js
supprimée (code mort).
- Extraction de client/src/utils/hudUtils.js (constantes ATTRS/couleurs et
gaugePercent) depuis GameView.vue pour les rendre testables isolément.
- Tests unitaires client (Vitest) : store Pinia (game.js) et hudUtils
(24 tests, client/test/unit/).
- Tests d'intégration E2E (Playwright, e2e/) avec de vrais clients
Socket.io et rendu Phaser réel dans Chromium :
* game-flow.spec.js : lobby → démarrage → main de 4 cartes → résolution
* item-shop.spec.js : boutique, bouton désactivé si jauge insuffisante,
achat retire l'objet pour tous + effet appliqué + affiché sur la table
* game-over.spec.js : partie complète (12 tours) → panneau de fin
webServer démarre automatiquement client+serveur ; ajout d'une route
GET /health côté Express pour permettre à Playwright de détecter un
serveur déjà démarré.
- README : documentation des 3 niveaux de tests (unitaires serveur,
unitaires client, intégration E2E) et de la nouvelle arborescence.