Dockerfile diventa multi-stage con target `dev` (Vite + bind-mount) oltre
a `builder`/`runtime`; docker-compose.dev.yml lo usa per un ambiente di
sviluppo containerizzato equivalente a `npm run dev`, senza toccare il
compose di produzione (che resta pull-only dal registry). Documentato in
README e CLAUDE.md.
Riflette i test aggiunti nei commit precedenti: rilevamento
piattaforma di server-utils, avvio effettivo di startServer, rami di
errore del WebSocket handler, cleanup/riconnessione di wsMixin,
side-effect di generaReferto e layout esteso del controller.
Aggiorna anche i conteggi di esempio (15 file/362 test Vitest, 81
E2E) e documenta come verificare la copertura (>99% statement).
Aggiunge 65 test per ControllerPage.vue (layout esteso/landscape,
popup automatico di set vinto con soglia decisiva, modal di
configurazione, validazione completa dei cambi giocatore, modal
time out con selezione video/ticker/countdown, cleanup dei listener)
e 35 per DisplayPage.vue (countdown overlay, riallineamento video su
riconnessione, fallback muto per autoplay bloccato, selezione voce
per la sintesi vocale). Nessun bug riscontrato nei componenti.
startServer() non era mai invocato nei test: solo createApp() (routing)
era coperto. Mocka src/persist.js per non toccare .segnapunti/state.json
reale, e verifica l'ascolto HTTP, l'accettazione dell'upgrade su /ws,
il rifiuto su altri percorsi e la persistenza dello stato dopo
un'azione del controller.
Aggiunge test per beforeUnmount con connessione OPEN e con ws.close
che lancia, l'annullamento del reconnectTimeout allo smontaggio, la
guardia isConnecting, la chiusura della WebSocket precedente prima
di riconnettersi (incluso il caso in cui close lancia), gli errori
di registrazione/parsing nei gestori onopen/onmessage e la guardia
di scheduleReconnect contro timer doppi.
Aggiunge scenari in cui ws.send o ws.terminate lanciano eccezioni e
in cui applyAction fallisce a metà azione: verifica che l'errore
venga loggato e lo stato precedente ripristinato senza propagare
l'eccezione al chiamante.
getNetworkIPs() senza parametri usa isWSL()/execSync/os.networkInterfaces,
codice mai esercitato perché tutti gli altri test iniettano `nets`
per bypassarlo. Mocka fs/child_process/os per coprire in modo
deterministico i rami WSL, non-WSL, fallimento PowerShell e lettura
/proc fallita.
buildRefertoHtml mancava il caso vinc nullo con guest in vantaggio;
generaReferto (window.open/print) non era testato affatto perché
richiede un DOM — aggiunto un test component in happy-dom che mocka
window.open.
Aggiunge casi per team 'guest' in nuovoSet, cambi senza array
`cambi`, resetta con striscia vuota e startTimeout senza startedAt:
gameState.js passa dal 100% al 100% statement ma sale al 99% branch.
Cornice nera, barre teal "SET N" per set, badge A/B per squadre e
box risultato finale in stile tabella, per avvicinare il referto
stampabile all'aspetto del referto cartaceo FIPAV mantenendo solo
i dati che l'app traccia realmente.
La striscia salva ora storico cambi formazione e time out per ogni
set, con riferimento al punteggio al momento dell'evento anziché a
un timestamp: startTimeout accumula in set.timeouts, confermaCambi
in set.cambi. Il referto elenca entrambi ordinati per punteggio.
Copre il percorso completo controller -> display: apertura modal,
vincolo squadra su Avvia, avvio con "nessun video" (deterministico,
non dipende dal contenuto di spot/), overlay e banner con countdown,
chiusura anticipata e annullamento della modal.
Aggiunge i casi non ancora coperti: il setTimeout di scheduleReconnect
riapre davvero la connessione, un costruttore WebSocket che lancia
programma comunque il retry, onerror azzera lo stato, sendWs ritorna
false se send lancia.
Copre apertura modal con fetch di /spot/list (incluso il fallback di
rete), il vincolo squadra obbligatoria su Avvia, l'invio di
startTimeout/stopTimeout e il banner con countdown durante il time
out attivo.
Nessun test component copriva ancora l'overlay introdotto con
startTimeout/stopTimeout: placeholder senza video, riproduzione con
src corretto, fallback a fine video, countdown e dissolvenza oltre i
30s. Stub di play/pause/load su HTMLMediaElement, non implementati da
happy-dom.
Aggiunge una cartella spot temporanea ai fixture del routing: elenco
ordinato dei soli .mp4, file serviti da /spot/<file>, [] se la
cartella non esiste.
La documentazione descriveva ancora il vecchio toggleTimeout (video in
loop/sequenza); aggiornata a startTimeout/stopTimeout con scelta
squadra/video, countdown e auto-chiusura server-side a 30s.
Aggiunge la selezione squadra/video per il time out con modal dedicato,
countdown sincronizzato, timer di auto-chiusura a 30s lato server,
fallback su immagine per video più corti e dissolvenza per quelli più
lunghi, oltre alla relativa copertura di test.
Unit: attivazione con squadra/video/inizio, rifiuto di squadre non valide,
azzeramento completo con stopTimeout. Integration (fake timers): startedAt
iniettato dal server, auto-chiusura a 30s con broadcast e persistenza,
annullamento del timer su stop manuale e riarmo su nuovo avvio.
L'overlay ora riproduce il video scelto dal controller mostrando un
countdown 00:30→00:00 calcolato dallo stato condiviso. Se il video finisce
prima dei 30s subentra un'immagine "TIME OUT + nome squadra" fino allo
scadere; se dura più di 30s si applica una dissolvenza CSS negli ultimi
istanti. In caso di riconnessione a metà time out il video viene
riallineato (currentTime) al punto corretto.
Il bottone Time Out apre un modal invece di attivare direttamente
l'overlay: si sceglie la squadra che lo ha richiesto e uno dei video
in spot/ (durata rilevata lato client via <video preload=metadata>).
A time out attivo il bottone diventa "Termina" con countdown, per
poterlo interrompere manualmente prima dei 30s.
Serve una squadra richiedente e un video selezionabile, non più un semplice
flag. Il timer dei 30s è armato lato server (non nel controller) così
sopravvive a refresh/disconnessione/standby del tablet che pilota il gioco.
Il tabellone (display) usava blocchi di colore pieni e testo semplice, molto
diverso dallo stile scuro con card e accenti oro/blu del controller.
Riallinea la palette (#f5c518/#2196f3), la tipografia (Inter, grassetto,
maiuscolo) e i colori di stato connessione/time out, poi aggiunge dettagli da
tabellone reale (linea centrale, badge SET, bagliore sul punteggio, striscia
punti come pannello con bordo/ombra) senza toccare layout o struttura.
I file del service worker generati da vite-plugin-pwa in modalità dev erano
committati nonostante dev-dist sia nel .gitignore. Vengono rigenerati a ogni
`npm run dev`, quindi non vanno versionati.
- README: sezione Funzionalità riscritta per spiegare in modo completo
display e controller (layout, punteggio/set, config, cambi, servizio,
voce, time out/spot, reset/referto, regole partita e amichevole).
- CLAUDE.md: descrizione di toggleTimeout, flag `timeout`, cartella `spot/`,
endpoint `/spot/list` e nota sull'autoplay con audio in kiosk.
Aggiunge il flag `timeout` nello stato e l'action `toggleTimeout`: dal
controller il pulsante Time Out commuta la modalità e il display copre il
tabellone con i video .mp4 della cartella `spot/` (non versionata).
- un solo video → loop; più video → sequenza alfabetica con ritorno al primo;
cartella vuota/assente → overlay "TIME OUT".
- `src/spot-utils.js` (listSpotVideos) elenca i .mp4; endpoint `/spot/list`
+ file statici `/spot/<file>.mp4` serviti sia in prod (server.js) sia in
dev (vite-plugin-websocket.js).
- il display avvia i video con audio e ripiega su muto se il browser blocca
l'autoplay con suono.
- il controller resta operativo ma evidenzia la modalità con banner e
pulsante arancione pulsante, in entrambi i layout (mobile ed esteso).
- ripara la suite Vitest allineandola alla forma attuale dello stato
- migra e completa gli e2e all'architettura a porta singola
- aggiunge copertura per referto, formInizio, persist, wsMixin, routing
- allinea README, CLAUDE.md e tests/README
- README: referto tra le funzionalità del controller; comando test:ui e aree
coperte dalle suite
- CLAUDE.md: referto.js (buildRefertoHtml/generaReferto), wsMixin/persist/
server-utils, createApp/startServer; nota su formInizio e stato derivato
dalla striscia
- tests/README: conteggi aggiornati (212 Vitest / 75 e2e) e nuove suite
- screenshot stabili: animations disabilitate e mask su .connection-status
- migrata agli helper condivisi
- baseline rigenerate per Chromium, Firefox e Mobile Chrome
- usa openController/openDisplay + resetGame per stato pulito
- esclude la regola color-contrast anche sul controller (colori brand
giallo/blu valutati separatamente, come già per il display)
Porta una partita 2/3 a conclusione, intercetta il popup aperto dal bottone
REFERTO e verifica il contenuto (titolo, set, risultato). window.print è
neutralizzato per non bloccare il test.
- nuovo tests/e2e/helpers.cjs: openController (viewport portrait per il layout
mobile), openDisplay, resetGame, addPoints
- resetGame gestisce il dialog di vittoria persistente e reimposta i nomi di
default (lo stato è condiviso e persistente: serve per il determinismo)
- full-match/game-simulation: gestito il dialog SET VINTO automatico a 25/15
- basic-flow/game-operations: migrati agli helper condivisi
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
Aggiunge un bottone "REFERTO" nel modal PARTITA FINITA che apre una
nuova scheda con il referto di gara in HTML e avvia il dialogo di
stampa del browser (Salva come PDF).
Il referto include: punteggio per set, formazione di partenza per ogni
set (salvata automaticamente al primo punto), andamento punto per punto
con colori per squadra, data e ora di generazione.
Nota: funzionalità prototipale — layout, contenuti e dettagli sono
ancora da perfezionare sulla base dell'utilizzo reale.
Le card punteggio mobile adottano lo stesso linguaggio visivo della
modalità estesa: pannello scuro #161618, stripe colorata in cima
(giallo home, blu guest), score come testo colorato senza riquadro.
I bottoni SET diventano a colore pieno; btn-ctrl e btn-danger
uniformati ai token colore della dashboard landscape.
Aggiunge una seconda modalità dashboard per schermi orizzontali,
rilevata automaticamente dall'orientamento (landscape → estesa,
portrait → mobile) tramite resize/orientationchange listener.
Layout estesa: due pannelli affiancati con stripe colorata in cima
(giallo/blu per identità squadra), score grande come elemento eroe,
diagramma campo in verde scuro, SET button a colori pieni in fondo.
Barra azioni compatta (40px) con tutti i controlli secondari.
Il pannello usa position:fixed ancorato al viewport per impedire
qualsiasi scroll, con @wheel.prevent e @touchmove.prevent.
Il pannello punteggio ora rispecchia l'ordine delle squadre definito da
state.order: premendo Inverti le card si scambiano di lato anche sul
controller, allineandosi al comportamento visivo del display.
Dopo aver azzerato la partita il dialog di configurazione si apre
subito, con nomi e modalità correnti e formazioni resettate ai default
— stesso comportamento già presente al termine di ogni set.
- Aggiunge modalità "Amichevole" in config: i set si vincono normalmente
ma la partita non termina mai automaticamente (checkVittoriaPartita
restituisce false), consentendo di giocare set illimitati
- Dev server ora espone su tutte le interfacce (vite --host)
- printServerInfo mostra Display + Controller per dispositivi remoti
- Su WSL2 getNetworkIPs() interroga PowerShell per ottenere gli IP
Windows reali invece degli IP interni WSL (172.x)
La striscia è ora l'unica source of truth: punt, set vinti e servizio
vengono calcolati via helper puri (punteggio/servizio/setVinti) invece
di essere mantenuti come campi ridondanti nello stato.
Il boilerplate WebSocket (~150 righe identiche in entrambi i componenti)
è estratto in src/wsMixin.js con createWsMixin(role); i componenti
diventano solo logica specifica del loro ruolo.