feat: écran de fin de partie + tests unitaires client et E2E Playwright

- 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.
This commit is contained in:
Kevin L'Hereec committed 2026-08-21 15:27:44 +02:00
1 parent 6a1c800253
commit 11cba0467b
20 files changed
+3447 -100

No files matched your search

-52
View File
@@ -1,52 +0,0 @@
import Phaser from 'phaser'
// Scène superposée pour les effets UI (notifications, fin de partie)
export class UIScene extends Phaser.Scene {
constructor() {
super({ key: 'UIScene', active: false })
}
create() {
this.socket = this.registry.get('socket')
this.socket.on('game-over', (results) => {
this.showEndScreen(results)
})
}
showEndScreen(results) {
const { width, height } = this.scale
// Fond semi-transparent
this.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.85)
this.add.text(width / 2, height / 2 - 150, 'FIN DE PARTIE', {
fontSize: '40px',
color: '#d4a732',
fontFamily: 'Georgia'
}).setOrigin(0.5)
const sorted = [...results].sort((a, b) => b.score - a.score)
sorted.forEach((p, i) => {
const y = height / 2 - 80 + i * 50
const medal = i === 0 ? '👑 ' : `${i + 1}. `
this.add.text(width / 2, y, `${medal}${p.name} — ${p.score} pts`, {
fontSize: '22px',
color: i === 0 ? '#d4a732' : '#f5e6c8',
fontFamily: 'Georgia'
}).setOrigin(0.5)
})
const btn = this.add.text(width / 2, height / 2 + 150, '[ RETOUR AU MENU ]', {
fontSize: '18px',
color: '#1a0a00',
backgroundColor: '#d4a732',
padding: { x: 20, y: 10 },
fontFamily: 'Georgia'
}).setOrigin(0.5).setInteractive({ cursor: 'pointer' })
btn.on('pointerdown', () => {
window.location.href = '/'
})
}
}