Files
CRAPP/test/unit/scout-store.test.ts
T
davideandClaude Sonnet 5.5 d86b38d47b Scout live: salvataggio immediato e chiusura di set e partita senza punti
Lo stato in corso si salva a ogni tocco (in coda, con avviso se fallisce). «Chiudi set» e
«Fine partita» funzionano con le sole valutazioni; le partite senza punteggio (haPunteggio) non
compaiono come risultato in Home, Classifica e pagina partita. Il pulsante di ripresa riconosce
valutazioni e set chiusi.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 08:36:51 +01:00

200 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Check dello scout: `bun test/unit/scout-store.test.ts`. */
import assert from "node:assert/strict";
import { giocatori } from "@/lib/crapp-data";
import {
azioniMeta,
contaPiuSenzaRicezione,
haPunteggio,
parzialiFinali,
contaValutazioni,
fondamentali,
giocatoriConScout,
totaliPerGiocatore,
totaliSquadra,
type Azione,
type Valutazione,
type ScoutMatch,
} from "@/lib/scout-store";
const a = (tipo: Azione["tipo"], giocatoreId?: string, set = 1): Azione => ({
id: `${tipo}-${giocatoreId ?? "team"}-${set}-${Math.random()}`,
tipo,
...(giocatoreId ? { giocatoreId } : {}),
set,
ts: 0,
});
const match = (
id: string,
setNostri: number,
setLoro: number,
azioni: Azione[] = [],
): ScoutMatch => ({
id,
data: "2026-09-01",
avversario: "Avversari",
casa: true,
setNostri,
setLoro,
parziali: [],
azioni,
valutazioni: [],
});
// --- totaliPerGiocatore: ace e muri valgono anche come punto ------------------
const azioni = [
a("attacco", "g1"),
a("ace", "g1"),
a("muro", "g1"),
a("errore", "g1"),
a("attacco", "g2"),
a("punto_avv"),
a("errore_avv"),
];
const totali = totaliPerGiocatore(azioni);
assert.deepEqual(totali.get("g1"), { punti: 3, ace: 1, muri: 1, errori: 1 });
assert.deepEqual(totali.get("g2"), { punti: 1, ace: 0, muri: 0, errori: 0 });
assert.equal(totali.size, 2, "le azioni senza giocatore non creano righe");
assert.equal(totaliPerGiocatore([]).size, 0);
// --- totaliSquadra -----------------------------------------------------------
assert.deepEqual(totaliSquadra([match("m1", 3, 0, azioni), match("m2", 3, 1, azioni)]), {
punti: 8,
ace: 2,
muri: 2,
errori: 2,
});
assert.deepEqual(totaliSquadra([]), { punti: 0, ace: 0, muri: 0, errori: 0 });
// --- giocatoriConScout: solo MVP da scout, presenze separate -----------------
const base = giocatori.find((g) => g.id === "g1")!;
const conScout = giocatoriConScout([match("m1", 3, 0, azioni)], { m1: base.nome });
const dopo = conScout.find((g) => g.id === "g1")!;
assert.equal(dopo.presenze, 0, "le azioni scout non alimentano le presenze personali");
assert.equal(dopo.mvp, 1, "l'MVP eletto viene contato");
assert.equal(dopo.totaliEventi, 0, "totaliEventi arriva dagli eventi CrAPP, non dallo scout");
const assente = conScout.find((g) => g.id === "g3")!;
assert.equal(assente.presenze, 0, "senza risposte presenze resta a zero");
assert.equal(assente.mvp, 0);
assert.equal(assente.totaliEventi, 0);
const senzaPartite = giocatoriConScout([]);
assert.ok(
senzaPartite.every((g) => g.presenze === 0 && g.mvp === 0 && g.totaliEventi === 0),
"senza partite scout le statistiche restano a zero",
);
// --- metadati azioni ---------------------------------------------------------
for (const [tipo, meta] of Object.entries(azioniMeta)) {
assert.ok(meta.label && meta.short, `${tipo}: etichette presenti`);
}
console.log("scout-store: ok");
// --- valutazioni − / +: per giocatore, fondamentale e set; fuori dal punteggio -------
const v = (
giocatoreId: string,
fondamentale: Valutazione["fondamentale"],
segno: Valutazione["segno"],
set = 1,
): Valutazione => ({
id: `${giocatoreId}-${fondamentale}-${segno}-${set}-${Math.random()}`,
giocatoreId,
fondamentale,
segno,
set,
ts: 0,
});
assert.deepEqual(
fondamentali.map((f) => f.id),
["battuta", "ricezione", "attacco", "muro"],
);
const val = [
v("g1", "battuta", "+"),
v("g1", "battuta", "+"),
v("g1", "battuta", "-"),
v("g1", "ricezione", "-", 2),
v("g1", "muro", "+", 2),
v("g2", "attacco", "+"),
];
assert.deepEqual(contaValutazioni(val, "g1", 1), {
battuta: { piu: 2, meno: 1 },
ricezione: { piu: 0, meno: 0 },
attacco: { piu: 0, meno: 0 },
muro: { piu: 0, meno: 0 },
});
assert.deepEqual(
contaValutazioni(val, "g1", 2).ricezione,
{ piu: 0, meno: 1 },
"il set 2 è separato",
);
assert.deepEqual(
contaValutazioni(val, "g1").battuta,
{ piu: 2, meno: 1 },
"senza set: tutta la partita",
);
assert.deepEqual(contaValutazioni(val, "g1").ricezione, { piu: 0, meno: 1 });
assert.deepEqual(
contaValutazioni(val, "g3", 1).battuta,
{ piu: 0, meno: 0 },
"giocatore senza valutazioni",
);
assert.equal(totaliPerGiocatore([]).size, 0, "le valutazioni non sono azioni: nessun punto");
// --- + del riquadro giocatore: tutti i fondamentali tranne la ricezione, nel set -------
const piuVal = [
v("g1", "battuta", "+"),
v("g1", "attacco", "+"),
v("g1", "muro", "+"),
v("g1", "ricezione", "+"),
v("g1", "attacco", "-"),
v("g1", "battuta", "+", 2),
v("g2", "attacco", "+"),
];
assert.equal(
contaPiuSenzaRicezione(piuVal, "g1", 1),
3,
"niente ricezione, niente −, solo il set 1",
);
assert.equal(contaPiuSenzaRicezione(piuVal, "g1", 2), 1);
assert.equal(contaPiuSenzaRicezione(piuVal, "g3", 1), 0);
// --- parziali finali: il set in corso conta solo se ha dati ------------------------
assert.deepEqual(
parzialiFinali([[25, 20]], [0, 0], true),
[
[25, 20],
[0, 0],
],
"set con sole valutazioni",
);
assert.deepEqual(
parzialiFinali([[25, 20]], [0, 0], false),
[[25, 20]],
"set vuoto: non si archivia",
);
assert.deepEqual(parzialiFinali([], [0, 0], false), [], "nessun set da salvare");
// --- haPunteggio: le partite di sole valutazioni non sono un risultato -------------
assert.equal(
haPunteggio({
parziali: [
[0, 0],
[0, 0],
],
}),
false,
);
assert.equal(haPunteggio({ parziali: [] }), false);
assert.equal(
haPunteggio({
parziali: [
[0, 0],
[25, 18],
],
}),
true,
);