The project had no automated verification at all, so the "Test" step in the AGENTS.md workflow rested entirely on clicking through the app. The suite runs on bun with node:assert and adds no dependency: every file is a script that exits non-zero when a check fails, and test/run.ts runs each one in its own process. Unit tests cover the rules that decide what players see (badges, streaks, ball duty rotation, ratings, MVP ties, scouting totals, goals, notifications, CSI parsing); integration and end-to-end tests drive the real dev server. Nothing writes to the database, so both can be pointed at a live environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
/** Check dell'export CSV dello scout: `bun test/unit/scout-export.test.ts`. */
|
|
import assert from "node:assert/strict";
|
|
import { giocatori } from "@/lib/crapp-data";
|
|
import { csvScoutMatch } from "@/lib/scout-export";
|
|
import type { Azione, ScoutMatch } from "@/lib/scout-store";
|
|
|
|
const g1 = giocatori.find((g) => g.id === "g1")!;
|
|
const g2 = giocatori.find((g) => g.id === "g2")!;
|
|
|
|
const a = (tipo: Azione["tipo"], giocatoreId?: string, set = 1): Azione => ({
|
|
id: `${tipo}-${set}-${giocatoreId ?? "team"}`,
|
|
tipo,
|
|
...(giocatoreId ? { giocatoreId } : {}),
|
|
set,
|
|
ts: 0,
|
|
});
|
|
|
|
const match: ScoutMatch = {
|
|
id: "m1",
|
|
data: "2026-09-01",
|
|
avversario: "Volley; Bologna",
|
|
casa: true,
|
|
setNostri: 3,
|
|
setLoro: 1,
|
|
parziali: [
|
|
[25, 19],
|
|
[23, 25],
|
|
[25, 21],
|
|
[25, 18],
|
|
],
|
|
mvp: g1.nome,
|
|
azioni: [a("attacco", g1.id), a("ace", g1.id, 2), a("errore", g2.id, 2), a("punto_avv")],
|
|
};
|
|
|
|
const csv = csvScoutMatch(match);
|
|
const righe = csv.split("\n");
|
|
|
|
// --- intestazione ------------------------------------------------------------
|
|
assert.equal(
|
|
righe[0],
|
|
'Partita;CRAP Volley;vs;"Volley; Bologna"',
|
|
"il ; nel nome va tra virgolette",
|
|
);
|
|
assert.equal(righe[1], "Data;2026-09-01;Set;3-1");
|
|
assert.ok(csv.includes("Set;Parziale nostro;Parziale loro"));
|
|
assert.ok(csv.includes("1;25;19"), "primo parziale");
|
|
assert.ok(csv.includes("4;25;18"), "ultimo parziale");
|
|
|
|
// --- totali per giocatore ----------------------------------------------------
|
|
assert.ok(csv.includes("Numero;Giocatore;Ruolo;Punti;Ace;Muri;Errori"));
|
|
assert.ok(
|
|
csv.includes(`${g1.numero};${g1.nome};${g1.ruolo};2;1;0;0`),
|
|
"attacco + ace = 2 punti di cui 1 ace",
|
|
);
|
|
assert.ok(csv.includes(`${g2.numero};${g2.nome};${g2.ruolo};0;0;0;1`));
|
|
assert.ok(
|
|
!righe.some((r) => r.startsWith(`${giocatori[2]!.numero};${giocatori[2]!.nome};`)),
|
|
"chi non ha azioni non compare tra i totali",
|
|
);
|
|
|
|
// --- dettaglio azioni --------------------------------------------------------
|
|
assert.ok(csv.includes("Set;Giocatore;Azione"));
|
|
assert.ok(csv.includes(`1;${g1.nome};Attacco vincente`) || csv.includes(`1;${g1.nome};`));
|
|
assert.ok(
|
|
righe.some((r) => r.startsWith("1;") && r.includes("—")),
|
|
"l'azione senza giocatore usa il trattino",
|
|
);
|
|
|
|
// --- trasferta e match vuoto -------------------------------------------------
|
|
const fuori = csvScoutMatch({ ...match, casa: false, avversario: "Ospiti" });
|
|
assert.equal(fuori.split("\n")[0], "Partita;Ospiti;vs;CRAP Volley", "in trasferta si invertono");
|
|
|
|
const vuoto = csvScoutMatch({ ...match, azioni: [], parziali: [] });
|
|
assert.ok(vuoto.includes("Data;2026-09-01"), "un match senza azioni resta esportabile");
|
|
assert.ok(!vuoto.includes(`;${g1.nome};`), "nessun totale senza azioni");
|
|
|
|
// Le virgolette nel testo vanno raddoppiate (regola CSV).
|
|
const conVirgolette = csvScoutMatch({ ...match, avversario: 'Team "X"' });
|
|
assert.ok(conVirgolette.includes('"Team ""X"""'));
|
|
|
|
console.log("scout-export: ok");
|