- 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.
126 lines
4.4 KiB
JavaScript
126 lines
4.4 KiB
JavaScript
import { test, expect } from '@playwright/test'
|
|
import { installPhaserDebugHook, createRoom, joinRoom, startGame, playCard, getGameScene } from './helpers.js'
|
|
|
|
/**
|
|
* Tests d'intégration E2E : parcours de base d'une partie (2 joueurs réels
|
|
* via de vrais WebSockets Socket.io + rendu Phaser réel dans un navigateur),
|
|
* du lobby jusqu'à la résolution d'un tour.
|
|
*/
|
|
test.describe('Parcours de base d\'une partie', () => {
|
|
test('deux joueurs peuvent créer/rejoindre une salle et démarrer la partie', async ({ browser }) => {
|
|
const ctx1 = await browser.newContext()
|
|
const ctx2 = await browser.newContext()
|
|
const p1 = await ctx1.newPage()
|
|
const p2 = await ctx2.newPage()
|
|
await installPhaserDebugHook(p1)
|
|
await installPhaserDebugHook(p2)
|
|
|
|
const roomId = await createRoom(p1, 'Alice')
|
|
await joinRoom(p2, 'Bob', roomId)
|
|
|
|
await expect(p1.locator('text=Bob')).toBeVisible()
|
|
await expect(p2.locator('text=Alice')).toBeVisible()
|
|
|
|
await startGame(p1)
|
|
await expect(p1).toHaveURL(new RegExp(`/game/${roomId}`))
|
|
await expect(p2).toHaveURL(new RegExp(`/game/${roomId}`))
|
|
|
|
await ctx1.close()
|
|
await ctx2.close()
|
|
})
|
|
|
|
test('chaque joueur reçoit une main de 4 cartes au démarrage', async ({ browser }) => {
|
|
const ctx1 = await browser.newContext()
|
|
const ctx2 = await browser.newContext()
|
|
const p1 = await ctx1.newPage()
|
|
const p2 = await ctx2.newPage()
|
|
await installPhaserDebugHook(p1)
|
|
await installPhaserDebugHook(p2)
|
|
|
|
const roomId = await createRoom(p1, 'Alice')
|
|
await joinRoom(p2, 'Bob', roomId)
|
|
await startGame(p1)
|
|
await p1.waitForTimeout(2000)
|
|
|
|
const handLen1 = await p1.evaluate(() => {
|
|
const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1]
|
|
return game.scene.getScene('GameScene').handCards.length
|
|
})
|
|
const handLen2 = await p2.evaluate(() => {
|
|
const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1]
|
|
return game.scene.getScene('GameScene').handCards.length
|
|
})
|
|
|
|
expect(handLen1).toBe(4)
|
|
expect(handLen2).toBe(4)
|
|
|
|
await ctx1.close()
|
|
await ctx2.close()
|
|
})
|
|
|
|
test('jouer une carte fait passer en phase "waiting" puis résout le tour une fois les deux joueurs joués', async ({ browser }) => {
|
|
const ctx1 = await browser.newContext()
|
|
const ctx2 = await browser.newContext()
|
|
const p1 = await ctx1.newPage()
|
|
const p2 = await ctx2.newPage()
|
|
await installPhaserDebugHook(p1)
|
|
await installPhaserDebugHook(p2)
|
|
|
|
const roomId = await createRoom(p1, 'Alice')
|
|
await joinRoom(p2, 'Bob', roomId)
|
|
await startGame(p1)
|
|
await p1.waitForTimeout(2000)
|
|
|
|
await playCard(p1)
|
|
await expect(p1.locator('.phase-label')).toHaveText(/attente/)
|
|
|
|
await playCard(p2)
|
|
// Résolution automatique une fois les deux joueurs joués
|
|
await p1.waitForTimeout(1000)
|
|
await expect(p1.locator('.phase-label')).toHaveText(/Résolution/)
|
|
|
|
// Après le délai serveur de résolution (3.5s), le tour suivant démarre
|
|
await p1.waitForTimeout(4000)
|
|
const turnAfter = await p1.evaluate(() => {
|
|
const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1]
|
|
return game.scene.getScene('GameScene').store.currentTurn
|
|
})
|
|
expect(turnAfter).toBe(2)
|
|
|
|
await ctx1.close()
|
|
await ctx2.close()
|
|
})
|
|
|
|
test('les gains d\'un tour respectent la règle du voisin de gauche (aucune erreur JS pendant la résolution)', async ({ browser }) => {
|
|
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))
|
|
p2.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)
|
|
|
|
await playCard(p1, 'war')
|
|
await playCard(p2, 'war')
|
|
await p1.waitForTimeout(1500)
|
|
|
|
// Vérifier que le panneau de résolution affiche bien les 2 cartes jouées
|
|
const resolveObjectsCount = await p1.evaluate(() => {
|
|
const game = window.__ALL_GAMES__[window.__ALL_GAMES__.length - 1]
|
|
return game.scene.getScene('GameScene').resolveObjects.length
|
|
})
|
|
expect(resolveObjectsCount).toBeGreaterThan(0)
|
|
expect(errors).toEqual([])
|
|
|
|
await ctx1.close()
|
|
await ctx2.close()
|
|
})
|
|
})
|