test: ripara la suite Vitest e migra gli e2e all'architettura attuale

La suite era allineata a una vecchia forma dello stato (sp.punt/sp.set/
sp.servHome) e a una vecchia architettura e2e (controller su :3001).
Baseline iniziale: 77/170 test Vitest falliti.

Vitest (ora 212/212 verdi):
- gameState.test.js: riscritto con helper che derivano punteggio/set/
  servizio dalla striscia; aggiunto blocco formInizio
- server-utils.js: getNetworkIPs accetta interfacce iniettabili e
  printServerInfo accetta gli IP iniettabili (deterministico anche su WSL);
  filtro LAN unificato (esclude 127./169.254./172.)
- websocket + stress: punteggio letto via punteggio(striscia)
- ControllerPage/DisplayPage: forzato layout mobile (viewport portrait),
  punteggi impostati via striscia; aggiunto test bottone REFERTO
- nuovi: referto.test.js, persist.test.js (mock fs), wsMixin.test.js,
  integration/server.test.js (routing)

Refactor di supporto:
- referto.js: estratta buildRefertoHtml(state, now) pura; generaReferto
  resta wrapper con window.open/print
- server.js: estratti createApp()/startServer(); avvio solo se entrypoint

e2e (migrazione parziale, NON ancora verificata verde):
- tutti i riferimenti controller :3001 -> :3000/controller
- forzato viewport portrait sul controller prima del goto
- reset helper: chiude il dialog di configurazione che doReset apre
- game-simulation: gestione del dialog SET VINTO automatico a 25
This commit is contained in:
2026-06-21 00:36:02 +02:00
parent ddf68010a4
commit eb37f8319f
19 changed files with 931 additions and 302 deletions
+6 -3
View File
@@ -17,7 +17,8 @@ test.describe('Accessibility (a11y)', () => {
});
test('Controller: non dovrebbe avere violazioni critiche a11y', async ({ page }) => {
await page.goto('http://localhost:3001');
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('http://localhost:3000/controller');
await page.waitForTimeout(500);
const results = await new AxeBuilder({ page })
@@ -42,7 +43,8 @@ test.describe('Accessibility (a11y)', () => {
});
test('Controller: i touch target dovrebbero avere dimensione minima', async ({ page }) => {
await page.goto('http://localhost:3001');
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('http://localhost:3000/controller');
await page.waitForSelector('.conn-bar.connected');
// Controlla che i bottoni principali abbiano dimensione minima 44x44px
@@ -57,7 +59,8 @@ test.describe('Accessibility (a11y)', () => {
});
test('Controller: i bottoni punteggio dovrebbero avere dimensione adeguata', async ({ page }) => {
await page.goto('http://localhost:3001');
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('http://localhost:3000/controller');
await page.waitForSelector('.conn-bar.connected');
const scoreButtons = page.locator('.team-score');
+25 -5
View File
@@ -7,7 +7,8 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await expect(displayPage).toHaveTitle(/Segnapunti/);
await expect(controllerPage).toHaveTitle(/Controller/);
@@ -15,7 +16,8 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
test('il punteggio iniziale dovrebbe essere 0-0', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
// Attende la connessione WebSocket
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -31,7 +33,8 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
// Attende la connessione WebSocket del controller
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -42,6 +45,11 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(200);
// Click +1 Home
@@ -60,7 +68,8 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -70,6 +79,11 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(200);
// Click +1 Guest
@@ -88,7 +102,8 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -98,6 +113,11 @@ test.describe('Basic Flow: Controller ↔ Display', () => {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(200);
// Home +1, Guest +1, Home +1
+11 -3
View File
@@ -7,6 +7,11 @@ async function resetGame(controllerPage) {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(300);
}
@@ -36,7 +41,8 @@ test.describe('Full Match Simulation', () => {
test('Partita 2/3: Home vince 2 set a 0', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
@@ -64,7 +70,8 @@ test.describe('Full Match Simulation', () => {
test('Set decisivo 2/3: vittoria a 15 punti', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
@@ -101,7 +108,8 @@ test.describe('Full Match Simulation', () => {
test('Set normale: punti oltre 25 fino ai vantaggi', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
+19 -7
View File
@@ -7,6 +7,11 @@ async function resetGame(controllerPage) {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(300);
}
@@ -14,7 +19,8 @@ test.describe('Game Operations', () => {
test('Undo: dovrebbe annullare l\'ultimo punto', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
@@ -32,7 +38,8 @@ test.describe('Game Operations', () => {
test('Reset: dovrebbe azzerare tutto dopo conferma', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
// Imposta qualche punto
@@ -54,7 +61,8 @@ test.describe('Game Operations', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
// Apri config
@@ -84,7 +92,8 @@ test.describe('Game Operations', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
// Inizialmente mostra punteggio, non formazione
@@ -103,7 +112,8 @@ test.describe('Game Operations', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
// Inizialmente la striscia è visibile
@@ -125,7 +135,8 @@ test.describe('Game Operations', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
@@ -157,7 +168,8 @@ test.describe('Game Operations', () => {
test('Cambi: dovrebbe mostrare errore per giocatore non in formazione', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
+14 -7
View File
@@ -7,7 +7,8 @@ test.describe('Game Simulation', () => {
const controllerPage = await context.newPage();
await displayPage.goto('http://localhost:3000');
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
// Selettori (basati su ID ipotetici o classi, adattali al tuo HTML reale)
// Assumo che nel DOM ci siano elementi con ID o classi riconoscibili
@@ -23,6 +24,12 @@ test.describe('Game Simulation', () => {
await btnConfirmReset.click();
}
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(200);
// 2. Loop per vincere il primo set (25 punti)
// In ControllerPage.vue, il click su .team-score.home-bg incrementa i punti home
@@ -55,15 +62,15 @@ test.describe('Game Simulation', () => {
// 2. Cliccare "SET HOME".
// 3. Verificare che Set Home = 1.
// Verifica che siamo a 25
// A 25-0 compare automaticamente il dialog "SET VINTO"
await expect(controllerPage.locator('.team-score.home-bg .team-pts')).toHaveText('25');
await controllerPage.waitForSelector('.dialog-winner');
// Clicca bottone SET
const btnSetHome = controllerPage.locator('.btn-set.home-bg');
await btnSetHome.click();
// Procedi al set successivo: registra il set vinto da Home
await controllerPage.getByText('VAI AL SET SUCCESSIVO').click();
await controllerPage.waitForTimeout(300);
// Verifica che il set sia incrementato
// Nota: display potrebbe chiamarsi diversamente, controlliamo Controller per coerenza
// Verifica che il set Home sia incrementato a 1
await expect(controllerPage.locator('.team-score.home-bg .team-set')).toContainText('SET 1');
});
});
+13 -4
View File
@@ -7,6 +7,11 @@ async function resetGame(controllerPage) {
if (await btnConfirm.isVisible()) {
await btnConfirm.click();
}
// doReset apre automaticamente il dialog di configurazione: chiudilo
const cfgCancel = controllerPage.locator('.dialog-config .btn-cancel');
if (await cfgCancel.isVisible()) {
await cfgCancel.click();
}
await controllerPage.waitForTimeout(300);
}
@@ -16,7 +21,8 @@ test.describe('Visual Regression', () => {
const controllerPage = await context.newPage();
const displayPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await displayPage.goto('http://localhost:3000');
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -35,7 +41,8 @@ test.describe('Visual Regression', () => {
const controllerPage = await context.newPage();
const displayPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await displayPage.goto('http://localhost:3000');
await controllerPage.waitForSelector('.conn-bar.connected');
@@ -59,7 +66,8 @@ test.describe('Visual Regression', () => {
test('Controller: screenshot stato iniziale', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
await resetGame(controllerPage);
@@ -71,7 +79,8 @@ test.describe('Visual Regression', () => {
test('Controller: screenshot con modal config aperta', async ({ context }) => {
const controllerPage = await context.newPage();
await controllerPage.goto('http://localhost:3001');
await controllerPage.setViewportSize({ width: 390, height: 844 });
await controllerPage.goto('http://localhost:3000/controller');
await controllerPage.waitForSelector('.conn-bar.connected');
// Apri config