import { test, expect } from '@playwright/test' import { installPhaserDebugHook, createRoom, joinRoom, startGame, selectHandCard, confirmSelectedCard } from './helpers.js' /** * Régression : le HUD Vue (jauges/scores en haut de l'écran) sautait à sa * nouvelle valeur DÈS que le serveur résolvait le tour ('round-result' + * 'game-state' en phase 'resolve' émis au même instant), en même temps que * l'animation de révélation des cartes/gains démarrait tout juste sur le * plateau Phaser (qui dure ~3.5s avant le tour suivant) — donnant * l'impression que les compteurs se mettaient à jour "au mauvais moment". * * Fix : le store Pinia (client/src/stores/game.js) gèle désormais * players/currentTurn pendant toute la phase 'resolve', et ne les applique * qu'au game-state suivant dont la phase n'est plus 'resolve' (typiquement * 'selecting' du tour suivant). */ test.describe('Synchronisation du HUD avec la résolution du tour', () => { test('le score du HUD reste figé pendant la phase resolve, puis se met à jour au tour suivant', async ({ browser }) => { test.setTimeout(60_000) const ctx1 = await browser.newContext() const ctx2 = await browser.newContext() const p1 = await ctx1.newPage() const p2 = await ctx2.newPage() const errors = [] p1.on('pageerror', e => errors.push(e.message)) await installPhaserDebugHook(p1) await installPhaserDebugHook(p2) const roomId = await createRoom(p1, 'Alice') await joinRoom(p2, 'Bob', roomId) await startGame(p1) await p1.waitForTimeout(2000) // Score initial affiché dans le HUD Vue (panneau du joueur local, premier panneau). const scoreBefore = await p1.locator('.player-panel').first().locator('.total-score').textContent() // Alice maximise "war", Bob joue explicitement sa carte la plus FAIBLE // en guerre (mode 'min') — indispensable pour garantir un gain non-nul // (gain = max(0, ma_valeur - valeur_voisin_gauche)), sinon le score ne // change pas du tout et le test ne serait pas discriminant (il passerait // aussi bien avec ou sans le fix, faute de changement à observer). await selectHandCard(p1, 'war') await confirmSelectedCard(p1) await selectHandCard(p2, 'war', 'min') await confirmSelectedCard(p2) // Attendre ACTIVEMENT la phase 'resolve' plutôt qu'un délai fixe : sous // charge, le round-result peut mettre plus ou moins de temps à arriver, // et un délai fixe trop court laissait parfois passer la fenêtre // d'observation (le tour suivant ayant déjà démarré entretemps). await expect.poll(async () => { return p1.evaluate(() => { const game = window.__ALL_GAMES__?.[window.__ALL_GAMES__.length - 1] return game?.scene?.getScene('GameScene')?.store?.phase }) }, { timeout: 3000, intervals: [100] }).toBe('resolve') // PENDANT la phase resolve (avant les ~3.5s de délai serveur), le score // du HUD doit rester identique à sa valeur d'AVANT le tour — même si le // serveur a déjà calculé et appliqué le nouveau score en interne. const scoreDuringResolve = await p1.locator('.player-panel').first().locator('.total-score').textContent() expect(scoreDuringResolve).toBe(scoreBefore) // Attendre la fin du délai serveur (3.5s) + un peu de marge : le tour // suivant démarre, phase repasse à 'selecting', le score doit alors se // mettre à jour (ou au minimum ne plus être bloqué en 'resolve'). await p1.waitForTimeout(4200) const phaseAfter = await p1.evaluate(() => { const game = window.__ALL_GAMES__?.[window.__ALL_GAMES__.length - 1] return game?.scene?.getScene('GameScene')?.store?.phase }) expect(phaseAfter).not.toBe('resolve') // Le score doit désormais avoir changé (le gain a bien été appliqué) — // confirme que le test observe une vraie différence, pas un cas où le // score n'aurait de toute façon jamais bougé. const scoreAfter = await p1.locator('.player-panel').first().locator('.total-score').textContent() expect(scoreAfter).not.toBe(scoreBefore) expect(errors).toEqual([]) await ctx1.close() await ctx2.close() }) })