import { describe, it, expect, beforeEach } from 'vitest' import { createInitialState, applyAction, checkVittoria } from '../../src/gameState.js' describe('Game Logic (gameState.js)', () => { let state beforeEach(() => { state = createInitialState() }) // ============================================= // STATO INIZIALE // ============================================= describe('Stato iniziale', () => { it('dovrebbe iniziare con 0-0', () => { expect(state.sp.punt.home).toBe(0) expect(state.sp.punt.guest).toBe(0) }) it('dovrebbe avere i set a 0', () => { expect(state.sp.set.home).toBe(0) expect(state.sp.set.guest).toBe(0) }) it('dovrebbe avere servizio Home', () => { expect(state.sp.servHome).toBe(true) }) it('dovrebbe avere formazione di default [1-6]', () => { expect(state.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) expect(state.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('dovrebbe avere la striscia iniziale a [0]', () => { expect(state.sp.striscia.home).toEqual([0]) expect(state.sp.striscia.guest).toEqual([0]) }) it('dovrebbe avere storico servizio vuoto', () => { expect(state.sp.storicoServizio).toEqual([]) }) it('dovrebbe avere modalità 3/5 di default', () => { expect(state.modalitaPartita).toBe("3/5") }) it('dovrebbe avere visuForm false e visuStriscia true', () => { expect(state.visuForm).toBe(false) expect(state.visuStriscia).toBe(true) }) }) // ============================================= // IMMUTABILITÀ // ============================================= describe('Immutabilità', () => { it('applyAction non dovrebbe mutare lo stato originale', () => { const original = JSON.stringify(state) applyAction(state, { type: 'incPunt', team: 'home' }) expect(JSON.stringify(state)).toBe(original) }) it('dovrebbe restituire un nuovo oggetto', () => { const newState = applyAction(state, { type: 'incPunt', team: 'home' }) expect(newState).not.toBe(state) }) }) // ============================================= // INCREMENTO PUNTI (incPunt) // ============================================= describe('incPunt', () => { it('dovrebbe incrementare i punti Home', () => { const newState = applyAction(state, { type: 'incPunt', team: 'home' }) expect(newState.sp.punt.home).toBe(1) expect(newState.sp.punt.guest).toBe(0) }) it('dovrebbe incrementare i punti Guest', () => { const newState = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(newState.sp.punt.guest).toBe(1) expect(newState.sp.punt.home).toBe(0) }) it('dovrebbe gestire il cambio palla (Guest segna, batteva Home)', () => { state.sp.servHome = true const s1 = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(s1.sp.servHome).toBe(false) }) it('dovrebbe gestire il cambio palla (Home segna, batteva Guest)', () => { state.sp.servHome = false const s1 = applyAction(state, { type: 'incPunt', team: 'home' }) expect(s1.sp.servHome).toBe(true) }) it('non dovrebbe cambiare palla se segna chi batte', () => { state.sp.servHome = true const s1 = applyAction(state, { type: 'incPunt', team: 'home' }) expect(s1.sp.servHome).toBe(true) }) it('dovrebbe ruotare la formazione al cambio palla', () => { state.sp.servHome = true state.sp.form.guest = ["1", "2", "3", "4", "5", "6"] const newState = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(newState.sp.form.guest).toEqual(["2", "3", "4", "5", "6", "1"]) }) it('non dovrebbe ruotare la formazione se non c\'è cambio palla', () => { state.sp.servHome = true state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const newState = applyAction(state, { type: 'incPunt', team: 'home' }) expect(newState.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('dovrebbe aggiornare la striscia per punto Home', () => { const s = applyAction(state, { type: 'incPunt', team: 'home' }) expect(s.sp.striscia.home).toEqual([0, 1]) expect(s.sp.striscia.guest).toEqual([0, " "]) }) it('dovrebbe aggiornare la striscia per punto Guest', () => { const s = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(s.sp.striscia.guest).toEqual([0, 1]) expect(s.sp.striscia.home).toEqual([0, " "]) }) it('dovrebbe registrare lo storico servizio', () => { const s = applyAction(state, { type: 'incPunt', team: 'home' }) expect(s.sp.storicoServizio).toHaveLength(1) expect(s.sp.storicoServizio[0]).toHaveProperty('servHome') expect(s.sp.storicoServizio[0]).toHaveProperty('cambioPalla') }) it('non dovrebbe incrementare i punti dopo vittoria', () => { state.sp.punt.home = 25 state.sp.punt.guest = 23 const s = applyAction(state, { type: 'incPunt', team: 'home' }) expect(s.sp.punt.home).toBe(25) }) }) // ============================================= // DECREMENTO PUNTI (decPunt) // ============================================= describe('decPunt', () => { it('dovrebbe annullare l\'ultimo punto Home', () => { const s1 = applyAction(state, { type: 'incPunt', team: 'home' }) const s2 = applyAction(s1, { type: 'decPunt' }) expect(s2.sp.punt.home).toBe(0) expect(s2.sp.punt.guest).toBe(0) }) it('dovrebbe annullare l\'ultimo punto Guest', () => { const s1 = applyAction(state, { type: 'incPunt', team: 'guest' }) const s2 = applyAction(s1, { type: 'decPunt' }) expect(s2.sp.punt.home).toBe(0) expect(s2.sp.punt.guest).toBe(0) }) it('non dovrebbe fare nulla sullo stato iniziale', () => { const s = applyAction(state, { type: 'decPunt' }) expect(s.sp.punt.home).toBe(0) expect(s.sp.punt.guest).toBe(0) }) it('dovrebbe ripristinare il servizio dopo undo con cambio palla', () => { state.sp.servHome = true const s1 = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(s1.sp.servHome).toBe(false) const s2 = applyAction(s1, { type: 'decPunt' }) expect(s2.sp.servHome).toBe(true) }) it('dovrebbe invertire la rotazione dopo undo con cambio palla', () => { state.sp.servHome = true state.sp.form.guest = ["1", "2", "3", "4", "5", "6"] const s1 = applyAction(state, { type: 'incPunt', team: 'guest' }) expect(s1.sp.form.guest).toEqual(["2", "3", "4", "5", "6", "1"]) const s2 = applyAction(s1, { type: 'decPunt' }) expect(s2.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('dovrebbe ripristinare la striscia', () => { const s1 = applyAction(state, { type: 'incPunt', team: 'home' }) const s2 = applyAction(s1, { type: 'decPunt' }) expect(s2.sp.striscia.home).toEqual([0]) }) it('dovrebbe gestire undo multipli in sequenza', () => { let s = state s = applyAction(s, { type: 'incPunt', team: 'home' }) s = applyAction(s, { type: 'incPunt', team: 'guest' }) s = applyAction(s, { type: 'incPunt', team: 'home' }) expect(s.sp.punt.home).toBe(2) expect(s.sp.punt.guest).toBe(1) s = applyAction(s, { type: 'decPunt' }) expect(s.sp.punt.home).toBe(1) s = applyAction(s, { type: 'decPunt' }) expect(s.sp.punt.guest).toBe(0) s = applyAction(s, { type: 'decPunt' }) expect(s.sp.punt.home).toBe(0) }) }) // ============================================= // INCREMENTO SET (incSet) // ============================================= describe('incSet', () => { it('dovrebbe incrementare il set Home', () => { const s = applyAction(state, { type: 'incSet', team: 'home' }) expect(s.sp.set.home).toBe(1) }) it('dovrebbe incrementare il set Guest', () => { const s = applyAction(state, { type: 'incSet', team: 'guest' }) expect(s.sp.set.guest).toBe(1) }) it('dovrebbe fare wrap da 2 a 0', () => { state.sp.set.home = 2 const s = applyAction(state, { type: 'incSet', team: 'home' }) expect(s.sp.set.home).toBe(0) }) it('dovrebbe incrementare da 1 a 2', () => { state.sp.set.home = 1 const s = applyAction(state, { type: 'incSet', team: 'home' }) expect(s.sp.set.home).toBe(2) }) }) // ============================================= // CAMBIO PALLA (cambiaPalla) // ============================================= describe('cambiaPalla', () => { it('dovrebbe invertire il servizio a 0-0', () => { expect(state.sp.servHome).toBe(true) const s = applyAction(state, { type: 'cambiaPalla' }) expect(s.sp.servHome).toBe(false) }) it('dovrebbe tornare a Home con doppio toggle', () => { let s = applyAction(state, { type: 'cambiaPalla' }) s = applyAction(s, { type: 'cambiaPalla' }) expect(s.sp.servHome).toBe(true) }) it('non dovrebbe cambiare palla se il punteggio non è 0-0', () => { state.sp.punt.home = 1 const s = applyAction(state, { type: 'cambiaPalla' }) expect(s.sp.servHome).toBe(true) }) it('non dovrebbe cambiare palla se Guest ha punti', () => { state.sp.punt.guest = 3 const s = applyAction(state, { type: 'cambiaPalla' }) expect(s.sp.servHome).toBe(true) }) }) // ============================================= // TOGGLE (toggleFormazione, toggleStriscia, toggleOrder) // ============================================= describe('Toggle', () => { it('toggleFormazione: false → true', () => { expect(state.visuForm).toBe(false) const s = applyAction(state, { type: 'toggleFormazione' }) expect(s.visuForm).toBe(true) }) it('toggleFormazione: true → false', () => { state.visuForm = true const s = applyAction(state, { type: 'toggleFormazione' }) expect(s.visuForm).toBe(false) }) it('toggleStriscia: true → false', () => { expect(state.visuStriscia).toBe(true) const s = applyAction(state, { type: 'toggleStriscia' }) expect(s.visuStriscia).toBe(false) }) it('toggleOrder: true → false', () => { expect(state.order).toBe(true) const s = applyAction(state, { type: 'toggleOrder' }) expect(s.order).toBe(false) }) }) // ============================================= // NOMI (setNomi) // ============================================= describe('setNomi', () => { it('dovrebbe aggiornare entrambi i nomi', () => { const s = applyAction(state, { type: 'setNomi', home: 'Volley A', guest: 'Volley B' }) expect(s.sp.nomi.home).toBe('Volley A') expect(s.sp.nomi.guest).toBe('Volley B') }) it('dovrebbe aggiornare solo il nome Home se guest è undefined', () => { const s = applyAction(state, { type: 'setNomi', home: 'Volley A' }) expect(s.sp.nomi.home).toBe('Volley A') expect(s.sp.nomi.guest).toBe('Guest') }) it('dovrebbe aggiornare solo il nome Guest se home è undefined', () => { const s = applyAction(state, { type: 'setNomi', guest: 'Volley B' }) expect(s.sp.nomi.home).toBe('Antoniana') expect(s.sp.nomi.guest).toBe('Volley B') }) }) // ============================================= // MODALITÀ (setModalita) // ============================================= describe('setModalita', () => { it('dovrebbe cambiare in 2/3', () => { const s = applyAction(state, { type: 'setModalita', modalita: '2/3' }) expect(s.modalitaPartita).toBe('2/3') }) it('dovrebbe cambiare in 3/5', () => { state.modalitaPartita = '2/3' const s = applyAction(state, { type: 'setModalita', modalita: '3/5' }) expect(s.modalitaPartita).toBe('3/5') }) }) // ============================================= // FORMAZIONE (setFormazione) // ============================================= describe('setFormazione', () => { it('dovrebbe sostituire la formazione Home', () => { const nuova = ["10", "11", "12", "13", "14", "15"] const s = applyAction(state, { type: 'setFormazione', team: 'home', form: nuova }) expect(s.sp.form.home).toEqual(nuova) }) it('dovrebbe sostituire la formazione Guest', () => { const nuova = ["7", "8", "9", "10", "11", "12"] const s = applyAction(state, { type: 'setFormazione', team: 'guest', form: nuova }) expect(s.sp.form.guest).toEqual(nuova) }) it('non dovrebbe modificare se manca team', () => { const s = applyAction(state, { type: 'setFormazione', form: ["7", "8", "9", "10", "11", "12"] }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('non dovrebbe modificare se manca form', () => { const s = applyAction(state, { type: 'setFormazione', team: 'home' }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) }) // ============================================= // CAMBI GIOCATORI (confermaCambi) // ============================================= describe('confermaCambi', () => { it('dovrebbe effettuare una sostituzione valida', () => { state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "10", out: "3" }] }) expect(s.sp.form.home).toContain("10") expect(s.sp.form.home).not.toContain("3") }) it('dovrebbe gestire doppia sostituzione', () => { state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [ { in: "10", out: "1" }, { in: "11", out: "2" } ] }) expect(s.sp.form.home).toContain("10") expect(s.sp.form.home).toContain("11") expect(s.sp.form.home).not.toContain("1") expect(s.sp.form.home).not.toContain("2") }) it('non dovrebbe accettare input non numerico', () => { state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "abc", out: "1" }] }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('non dovrebbe accettare in == out', () => { const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "1", out: "1" }] }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('non dovrebbe accettare giocatore IN già in formazione', () => { const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "2", out: "1" }] }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('non dovrebbe accettare giocatore OUT non in formazione', () => { const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "10", out: "99" }] }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('dovrebbe saltare cambi con campo vuoto', () => { const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [ { in: "", out: "" }, { in: "10", out: "1" } ] }) expect(s.sp.form.home).toContain("10") }) it('dovrebbe mantenere la posizione del giocatore sostituito', () => { state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [{ in: "10", out: "3" }] }) expect(s.sp.form.home[2]).toBe("10") }) it('dovrebbe gestire cambi sequenziali che dipendono l\'uno dall\'altro', () => { // Sostituisci 1→10, poi 10→20 (il secondo dipende dal risultato del primo) state.sp.form.home = ["1", "2", "3", "4", "5", "6"] const s = applyAction(state, { type: 'confermaCambi', team: 'home', cambi: [ { in: "10", out: "1" }, { in: "20", out: "10" } ] }) expect(s.sp.form.home).toContain("20") expect(s.sp.form.home).not.toContain("1") expect(s.sp.form.home).not.toContain("10") }) }) // ============================================= // VITTORIA SET (checkVittoria) // ============================================= describe('checkVittoria', () => { it('non dovrebbe dare vittoria a 24-24', () => { state.sp.punt.home = 24 state.sp.punt.guest = 24 expect(checkVittoria(state)).toBe(false) }) it('dovrebbe dare vittoria a 25-23', () => { state.sp.punt.home = 25 state.sp.punt.guest = 23 expect(checkVittoria(state)).toBe(true) }) it('non dovrebbe dare vittoria a 25-24 (serve 2 punti di scarto)', () => { state.sp.punt.home = 25 state.sp.punt.guest = 24 expect(checkVittoria(state)).toBe(false) }) it('dovrebbe dare vittoria a 26-24', () => { state.sp.punt.home = 26 state.sp.punt.guest = 24 expect(checkVittoria(state)).toBe(true) }) it('dovrebbe dare vittoria Guest a 25-20', () => { state.sp.punt.home = 20 state.sp.punt.guest = 25 expect(checkVittoria(state)).toBe(true) }) it('dovrebbe dare vittoria ai vantaggi (30-28)', () => { state.sp.punt.home = 30 state.sp.punt.guest = 28 expect(checkVittoria(state)).toBe(true) }) it('non dovrebbe dare vittoria ai vantaggi senza scarto (28-27)', () => { state.sp.punt.home = 28 state.sp.punt.guest = 27 expect(checkVittoria(state)).toBe(false) }) }) // ============================================= // SET DECISIVO (15 punti) // ============================================= describe('Set decisivo', () => { it('modalità 3/5: set decisivo dopo 4 set totali → vittoria a 15', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 2 state.sp.punt.home = 15 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(true) }) it('modalità 3/5: non vittoria a 14-10 nel set decisivo', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 2 state.sp.punt.home = 14 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(false) }) it('modalità 3/5: vittoria a 15-13 nel set decisivo', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 2 state.sp.punt.home = 15 state.sp.punt.guest = 13 expect(checkVittoria(state)).toBe(true) }) it('modalità 3/5: non vittoria a 15-14 nel set decisivo (serve scarto)', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 2 state.sp.punt.home = 15 state.sp.punt.guest = 14 expect(checkVittoria(state)).toBe(false) }) it('modalità 3/5: vittoria a 16-14 nel set decisivo', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 2 state.sp.punt.home = 16 state.sp.punt.guest = 14 expect(checkVittoria(state)).toBe(true) }) it('modalità 2/3: set decisivo dopo 2 set totali → vittoria a 15', () => { state.modalitaPartita = "2/3" state.sp.set.home = 1 state.sp.set.guest = 1 state.sp.punt.home = 15 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(true) }) it('modalità 2/3: non vittoria a 24-20 nel set decisivo (soglia 15)', () => { state.modalitaPartita = "2/3" state.sp.set.home = 1 state.sp.set.guest = 1 state.sp.punt.home = 14 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(false) }) it('modalità 2/3: set non decisivo (1-0) → soglia 25', () => { state.modalitaPartita = "2/3" state.sp.set.home = 1 state.sp.set.guest = 0 state.sp.punt.home = 15 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(false) }) it('modalità 3/5: set non decisivo (2-1) → soglia 25', () => { state.modalitaPartita = "3/5" state.sp.set.home = 2 state.sp.set.guest = 1 state.sp.punt.home = 15 state.sp.punt.guest = 10 expect(checkVittoria(state)).toBe(false) }) }) // ============================================= // RESET // ============================================= describe('Reset', () => { it('dovrebbe resettare punti e set a zero', () => { state.sp.punt.home = 10 state.sp.punt.guest = 8 state.sp.set.home = 1 state.sp.set.guest = 1 const s = applyAction(state, { type: 'resetta' }) expect(s.sp.punt.home).toBe(0) expect(s.sp.punt.guest).toBe(0) expect(s.sp.set.home).toBe(0) expect(s.sp.set.guest).toBe(0) }) it('dovrebbe resettare formazioni a default', () => { state.sp.form.home = ["10", "11", "12", "13", "14", "15"] const s = applyAction(state, { type: 'resetta' }) expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"]) expect(s.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"]) }) it('dovrebbe resettare la striscia', () => { state.sp.striscia = { home: [0, 1, 2, 3], guest: [0, " ", " ", 1] } const s = applyAction(state, { type: 'resetta' }) expect(s.sp.striscia.home).toEqual([0]) expect(s.sp.striscia.guest).toEqual([0]) }) it('dovrebbe resettare lo storico servizio', () => { state.sp.storicoServizio = [{ servHome: true, cambioPalla: false }] const s = applyAction(state, { type: 'resetta' }) expect(s.sp.storicoServizio).toEqual([]) }) it('dovrebbe impostare visuForm a false', () => { state.visuForm = true const s = applyAction(state, { type: 'resetta' }) expect(s.visuForm).toBe(false) }) it('dovrebbe mantenere nomi e modalità', () => { state.sp.nomi.home = "Squadra A" state.modalitaPartita = "2/3" const s = applyAction(state, { type: 'resetta' }) expect(s.sp.nomi.home).toBe("Squadra A") expect(s.modalitaPartita).toBe("2/3") }) }) // ============================================= // AZIONE SCONOSCIUTA // ============================================= describe('Azione sconosciuta', () => { it('dovrebbe restituire lo stato invariato con azione non riconosciuta', () => { const s = applyAction(state, { type: 'azioneInesistente' }) expect(s.sp.punt.home).toBe(0) expect(s.sp.punt.guest).toBe(0) }) }) })