import { test, expect } from '@playwright/test' import { installPhaserDebugHook, createRoom, joinRoom, startGame, selectHandCard, confirmSelectedCard, playFullTurn, getMyGauges, clickEventDrawButton } from './helpers.js' /** * Régression : sélectionner une carte de la main, puis effectuer une autre * action réseau AVANT de confirmer (ex: piocher un événement) ne devait pas * désynchroniser le bouton "CONFIRMER MA CARTE" de l'état réel de sélection. * * Cause du bug : le listener 'game-state' (déclenché à CHAQUE action de * N'IMPORTE QUEL joueur, pas seulement au changement de tour) appelait * inconditionnellement _deactivateBtn() dès que phase==='selecting', même si * this.selectedCard restait valide en mémoire. La carte restait visuellement * sélectionnée (surélevée, CardObject._selected=true) mais le bouton de * confirmation apparaissait grisé — un premier clic sur la carte la * DÉSÉLECTIONNAIT (déjà _selected=true côté CardObject) au lieu de la * confirmer, obligeant à cliquer une seconde fois pour la re-sélectionner. */ test.describe('Persistance de la sélection de carte personnage', () => { test('sélectionner une carte puis piocher un événement ne désactive pas le bouton de confirmation', async ({ browser }) => { test.setTimeout(90_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) // Accumuler assez de points sur les 4 jauges pour pouvoir piocher un événement. let gauges = await getMyGauges(p1) let attempts = 0 while (!['food', 'war', 'health', 'diplo'].every(a => gauges[a] >= 1) && attempts < 12) { await playFullTurn([p1, p2], [ ['food', 'war', 'health', 'diplo'], { attr: ['food', 'war', 'health', 'diplo'], mode: 'min' } ]) gauges = await getMyGauges(p1) attempts++ } expect(['food', 'war', 'health', 'diplo'].every(a => gauges[a] >= 1)).toBe(true) // Sélectionner une carte personnage SANS la confirmer. const selected = await selectHandCard(p1) expect(selected).toBe(true) const selectedCardIdBefore = await p1.evaluate(() => { const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1] const scene = game.scene.getScene('GameScene') return scene.selectedCard?.cardData?.id ?? null }) expect(selectedCardIdBefore).not.toBeNull() // Déclencher une action réseau qui provoque un game-state broadcast à // tous les joueurs (piocher un événement), SANS toucher à la carte. const drew = await clickEventDrawButton(p1) expect(drew).toBe(true) await p1.waitForTimeout(300) // La carte doit rester sélectionnée EN MÉMOIRE... const selectedCardIdAfter = await p1.evaluate(() => { const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1] const scene = game.scene.getScene('GameScene') return scene.selectedCard?.cardData?.id ?? null }) expect(selectedCardIdAfter).toBe(selectedCardIdBefore) // ...ET le bouton de confirmation doit rester visuellement ACTIF (pas de // pulse-tween arrêté par un _deactivateBtn() intempestif). const btnActive = await p1.evaluate(() => { const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1] const scene = game.scene.getScene('GameScene') return !!scene._pulseTween && scene._pulseTween.isPlaying() }) expect(btnActive).toBe(true) // Confirmer directement (SANS recliquer sur la carte au préalable) doit // fonctionner du premier coup. const confirmed = await confirmSelectedCard(p1) expect(confirmed).toBe(true) await p1.waitForTimeout(300) const phaseAfterConfirm = await p1.evaluate(() => { const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1] const scene = game.scene.getScene('GameScene') return scene.phaseText.text }) expect(phaseAfterConfirm).toMatch(/attente/) expect(errors).toEqual([]) await ctx1.close() await ctx2.close() }) })