Les cartes Événement (bouton de pioche + main) étaient positionnées trop près du bouton "CONFIRMER MA CARTE" — le bouton de pioche chevauchait même physiquement son bord droit de ~25px (x = width/2 + 210, même Y), au point de prêter à confusion entre "piocher un événement" et "jouer une carte". - Nouveau module pur client/src/game/objects/eventHandLayout.js (eventDrawButtonPosition, eventHandGeometry, eventCardPosition) : ancre toute la zone événement en bas-DROITE du canvas, clairement séparée du bouton de confirmation (centré) et de la main de personnages (centrée). - EVENT_CARD_SCALE remonté de 0.55 à 0.8 pour que le nom de l'événement (police de base augmentée 12px -> 15px) reste réellement lisible une fois réduit — ajout d'un bandeau semi-opaque derrière le texte (EventCardObject) pour garantir le contraste quel que soit le portrait en arrière-plan. - La légende des attributs occupait déjà le coin bas-droit : déplacée en bas-GAUCHE (setOrigin ajusté en conséquence) pour laisser la place à la nouvelle zone événement. - 16 nouveaux tests unitaires Vitest (eventHandLayout.test.js) couvrant la géométrie : positions dans les limites du canvas, absence de chevauchement avec le bouton de confirmation/la main/la légende, ancrage stable au bord droit avec 1 ou 2 cartes, robustesse sur petite résolution (1280x720) et cardCount=0. Choix explicite de tests UNITAIRES (rapides, ~600ms pour toute la suite client) plutôt que E2E pour cette correction de positionnement pur, les tests E2E de ce projet étant lents (plusieurs minutes par scénario à cause de la pioche aléatoire à stabiliser).
155 lines
7.1 KiB
JavaScript
155 lines
7.1 KiB
JavaScript
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
eventDrawButtonPosition,
|
|
eventHandGeometry,
|
|
eventCardPosition,
|
|
EVENT_CARD_SCALE
|
|
} from '../../src/game/objects/eventHandLayout'
|
|
import { CARD_W, CARD_H } from '../../src/game/objects/cardLayout'
|
|
|
|
// Dimensions de canvas typiques utilisées dans les tests (cohérentes avec
|
|
// GameFactory.js : width = innerWidth, height = innerHeight - 60).
|
|
const CANVAS_W = 1920
|
|
const CANVAS_H = 1080
|
|
|
|
describe('game/objects/eventHandLayout', () => {
|
|
describe('EVENT_CARD_SCALE', () => {
|
|
it('reste suffisamment proche de 1 pour garder le texte lisible (pas 0.55 comme l\'ancien placement)', () => {
|
|
expect(EVENT_CARD_SCALE).toBeGreaterThanOrEqual(0.7)
|
|
expect(EVENT_CARD_SCALE).toBeLessThanOrEqual(1)
|
|
})
|
|
})
|
|
|
|
describe('eventDrawButtonPosition', () => {
|
|
it('positionne le bouton dans le quart bas-droit du canvas', () => {
|
|
const { x, y } = eventDrawButtonPosition(CANVAS_W, CANVAS_H)
|
|
expect(x).toBeGreaterThan(CANVAS_W / 2)
|
|
expect(y).toBeGreaterThan(CANVAS_H / 2)
|
|
})
|
|
|
|
it('reste entièrement à l\'intérieur du canvas (ne déborde jamais à droite ni en bas)', () => {
|
|
const buttonWidth = 190
|
|
const buttonHeight = 30
|
|
const { x, y } = eventDrawButtonPosition(CANVAS_W, CANVAS_H, buttonWidth)
|
|
expect(x + buttonWidth / 2).toBeLessThanOrEqual(CANVAS_W)
|
|
expect(x - buttonWidth / 2).toBeGreaterThanOrEqual(0)
|
|
expect(y + buttonHeight / 2).toBeLessThanOrEqual(CANVAS_H)
|
|
})
|
|
|
|
it('est positionné strictement au-dessus de la main de cartes événement (pas de chevauchement)', () => {
|
|
const buttonHeight = 30
|
|
const { y: btnY } = eventDrawButtonPosition(CANVAS_W, CANVAS_H)
|
|
const { y: handY } = eventHandGeometry(CANVAS_W, CANVAS_H, 1)
|
|
const cardH = CARD_H * EVENT_CARD_SCALE
|
|
const buttonBottom = btnY + buttonHeight / 2
|
|
const handTop = handY - cardH / 2
|
|
expect(buttonBottom).toBeLessThanOrEqual(handTop)
|
|
})
|
|
|
|
it('reste bien à droite du bouton "CONFIRMER MA CARTE" (centré, ~280px de large) — pas de chevauchement', () => {
|
|
const confirmButtonWidth = 280
|
|
const confirmButtonRightEdge = CANVAS_W / 2 + confirmButtonWidth / 2
|
|
const drawButtonWidth = 190
|
|
const { x } = eventDrawButtonPosition(CANVAS_W, CANVAS_H, drawButtonWidth)
|
|
const drawButtonLeftEdge = x - drawButtonWidth / 2
|
|
// Régression du bug signalé : l'ancien placement (x = width/2 + 210,
|
|
// même Y que le bouton confirmer) chevauchait physiquement son bord
|
|
// droit de ~25px. Le nouveau placement bas-droite doit être
|
|
// verticalement séparé (voir test précédent) ET ne pas non plus se
|
|
// chevaucher horizontalement s'ils finissaient à la même hauteur.
|
|
expect(drawButtonLeftEdge).toBeGreaterThanOrEqual(confirmButtonRightEdge)
|
|
})
|
|
|
|
it('accepte une largeur de bouton personnalisée', () => {
|
|
const narrow = eventDrawButtonPosition(CANVAS_W, CANVAS_H, 100)
|
|
const wide = eventDrawButtonPosition(CANVAS_W, CANVAS_H, 300)
|
|
// Un bouton plus large doit être centré plus loin du bord droit
|
|
expect(narrow.x).toBeGreaterThan(wide.x)
|
|
})
|
|
})
|
|
|
|
describe('eventHandGeometry', () => {
|
|
it('positionne la main dans la moitié basse du canvas', () => {
|
|
const { y } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
expect(y).toBeGreaterThan(CANVAS_H / 2)
|
|
})
|
|
|
|
it('la dernière carte est ancrée sur le bord droit du canvas (avec la marge)', () => {
|
|
const { startX, spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
const cardW = CARD_W * scale
|
|
const lastCardCenterX = startX + spacing // index 1 = dernière carte pour cardCount=2
|
|
const lastCardRightEdge = lastCardCenterX + cardW / 2
|
|
expect(lastCardRightEdge).toBeLessThanOrEqual(CANVAS_W)
|
|
expect(lastCardRightEdge).toBeGreaterThan(CANVAS_W - 40) // proche du bord (marge ~20px)
|
|
})
|
|
|
|
it('avec 1 carte, elle est ancrée sur le bord droit (pas de décalage vers la gauche)', () => {
|
|
const { startX } = eventHandGeometry(CANVAS_W, CANVAS_H, 1)
|
|
const { startX: startXTwo } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
// La position de la DERNIÈRE carte doit être identique quel que soit
|
|
// le nombre de cartes (ancrage à droite stable) — avec 1 carte,
|
|
// startX doit correspondre à la position de la dernière carte du cas à 2.
|
|
const { spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
expect(startX).toBeCloseTo(startXTwo + spacing, 5)
|
|
})
|
|
|
|
it('gère cardCount=0 sans produire de position invalide (NaN/Infinity)', () => {
|
|
const { startX, y, spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 0)
|
|
expect(Number.isFinite(startX)).toBe(true)
|
|
expect(Number.isFinite(y)).toBe(true)
|
|
expect(Number.isFinite(spacing)).toBe(true)
|
|
})
|
|
|
|
it('l\'espacement entre cartes est toujours positif et couvre au moins la largeur d\'une carte', () => {
|
|
const { spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
expect(spacing).toBeGreaterThan(CARD_W * scale)
|
|
})
|
|
|
|
it('les 2 cartes ne se chevauchent jamais (distance entre centres >= largeur d\'une carte)', () => {
|
|
const { spacing, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
const cardW = CARD_W * scale
|
|
expect(spacing).toBeGreaterThanOrEqual(cardW)
|
|
})
|
|
|
|
it('reste entièrement dans le canvas sur une petite résolution (1280x720)', () => {
|
|
const { startX, y, spacing, scale } = eventHandGeometry(1280, 720, 2)
|
|
const cardW = CARD_W * scale
|
|
const cardH = CARD_H * scale
|
|
const firstCardLeftEdge = startX - cardW / 2
|
|
const lastCardRightEdge = startX + spacing + cardW / 2
|
|
expect(firstCardLeftEdge).toBeGreaterThanOrEqual(0)
|
|
expect(lastCardRightEdge).toBeLessThanOrEqual(1280)
|
|
expect(y + cardH / 2).toBeLessThanOrEqual(720)
|
|
expect(y - cardH / 2).toBeGreaterThanOrEqual(0)
|
|
})
|
|
})
|
|
|
|
describe('eventCardPosition', () => {
|
|
it('retourne la même position que eventHandGeometry pour un index donné', () => {
|
|
const { startX, y, spacing } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
const pos0 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 0)
|
|
const pos1 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 1)
|
|
expect(pos0).toEqual({ x: startX, y })
|
|
expect(pos1).toEqual({ x: startX + spacing, y })
|
|
})
|
|
|
|
it('les positions de 2 cartes sont distinctes', () => {
|
|
const pos0 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 0)
|
|
const pos1 = eventCardPosition(CANVAS_W, CANVAS_H, 2, 1)
|
|
expect(pos0.x).not.toBe(pos1.x)
|
|
})
|
|
})
|
|
|
|
describe('non-régression : séparation avec la légende d\'attributs (bas-gauche)', () => {
|
|
it('la zone événement (bas-droite) ne recouvre pas la zone légende (bas-gauche, x proche de 0)', () => {
|
|
const { startX, scale } = eventHandGeometry(CANVAS_W, CANVAS_H, 2)
|
|
const cardW = CARD_W * scale
|
|
const firstCardLeftEdge = startX - cardW / 2
|
|
// La légende occupe le coin bas-gauche (x ancré à 10px du bord gauche,
|
|
// largeur de texte ~180px max) — la zone événement doit rester dans
|
|
// la moitié droite du canvas pour ne jamais s'en approcher.
|
|
expect(firstCardLeftEdge).toBeGreaterThan(CANVAS_W / 2)
|
|
})
|
|
})
|
|
})
|