Struttura: il sito esistente si sposta sotto apps/sito
Primo passo della separazione sito / piattaforme decisa da Adriano il 03/09: due applicazioni autosufficienti, senza workspace di root, cosi' che ognuna resti un progetto Astro completo ed estraibile. Qui non cambia niente del codice: solo la radice. I pattern del .gitignore che avevano una barra erano ancorati alla radice e dopo lo spostamento non avrebbero piu' coperto niente (data/*.db*, uploads/*): resi validi a qualunque profondita'. Suite: 45 file, 347 test verdi prima e dopo lo spostamento. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { avvisi, avvisiAttivi, calendari, calendariDaMostrare, DISCIPLINE, GIORNI, type Giorno } from '../src/data/calendari';
|
||||
|
||||
const d = (iso: string) => new Date(iso);
|
||||
|
||||
describe('calendariDaMostrare', () => {
|
||||
const idDi = (quando: string) => calendariDaMostrare(d(quando)).map((m) => `${m.cal.id}:${m.stato}`);
|
||||
|
||||
// Si vedono tutti insieme, non solo quello del momento: chi guarda ad agosto vuole sapere
|
||||
// com'è settembre per intero, e una griglia sola gli farebbe credere che valga per sempre.
|
||||
it('prima dell\'inizio: entrambi, marcati futuri', () => {
|
||||
expect(idDi('2026-08-17T10:00:00+02:00'))
|
||||
.toEqual(['settembre-1-18:futuro', 'settembre-21-in-poi:futuro']);
|
||||
});
|
||||
|
||||
it('durante il primo periodo: il primo è in corso, il secondo resta annunciato', () => {
|
||||
expect(idDi('2026-09-05T10:00:00+02:00'))
|
||||
.toEqual(['settembre-1-18:in-corso', 'settembre-21-in-poi:futuro']);
|
||||
});
|
||||
|
||||
// Un orario scaduto non deve restare in pagina: farebbe cercare lezioni che non ci sono più.
|
||||
it('finito il primo periodo, il primo orario sparisce', () => {
|
||||
expect(idDi('2026-09-25T10:00:00+02:00')).toEqual(['settembre-21-in-poi:in-corso']);
|
||||
expect(idDi('2027-03-01T10:00:00+02:00')).toEqual(['settembre-21-in-poi:in-corso']);
|
||||
});
|
||||
|
||||
// 19 e 20 settembre non stanno in nessuna griglia perché quei due giorni non hanno class:
|
||||
// il 19 c'è la presentazione della nuova apertura, il 20 si sta chiusi (Adriano, 17/08).
|
||||
it('nei due giorni fuori griglia resta il solo orario del 21, annunciato', () => {
|
||||
expect(idDi('2026-09-19T10:00:00+02:00')).toEqual(['settembre-21-in-poi:futuro']);
|
||||
expect(idDi('2026-09-20T23:00:00+02:00')).toEqual(['settembre-21-in-poi:futuro']);
|
||||
});
|
||||
|
||||
it('confini al secondo, in ora italiana', () => {
|
||||
expect(idDi('2026-09-18T23:59:59+02:00'))
|
||||
.toEqual(['settembre-1-18:in-corso', 'settembre-21-in-poi:futuro']);
|
||||
expect(idDi('2026-09-19T00:00:00+02:00')).toEqual(['settembre-21-in-poi:futuro']);
|
||||
expect(idDi('2026-09-21T00:00:00+02:00')).toEqual(['settembre-21-in-poi:in-corso']);
|
||||
});
|
||||
|
||||
it('l\'ordine dell\'elenco di partenza non cambia il risultato', () => {
|
||||
const rovescio = [...calendari].reverse();
|
||||
expect(calendariDaMostrare(d('2026-09-05T10:00:00+02:00'), rovescio).map((m) => m.cal.id))
|
||||
.toEqual(['settembre-1-18', 'settembre-21-in-poi']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('giornate fuori griglia', () => {
|
||||
it('la presentazione e la chiusura si vedono prima di arrivarci', () => {
|
||||
const oggi = avvisiAttivi(d('2026-08-17T12:00:00+02:00')).map((a) => a.id);
|
||||
expect(oggi).toEqual(['presentazione-19-settembre', 'chiusura-20-settembre']);
|
||||
});
|
||||
|
||||
// Un avviso passato è peggio di nessun avviso: dice che il centro è chiuso un giorno che è
|
||||
// già trascorso, e chi legge non sa più se valga per quest'anno o per il prossimo.
|
||||
it('ogni avviso sparisce da solo quando è passato', () => {
|
||||
expect(avvisiAttivi(d('2026-09-19T23:59:59+02:00')).map((a) => a.id))
|
||||
.toEqual(['presentazione-19-settembre', 'chiusura-20-settembre']);
|
||||
expect(avvisiAttivi(d('2026-09-20T00:00:00+02:00')).map((a) => a.id))
|
||||
.toEqual(['chiusura-20-settembre']);
|
||||
expect(avvisiAttivi(d('2026-09-21T00:00:00+02:00'))).toEqual([]);
|
||||
});
|
||||
|
||||
it('ogni avviso ha una scadenza leggibile e un testo', () => {
|
||||
for (const a of avvisi) {
|
||||
expect(Number.isNaN(Date.parse(a.to)), `${a.id}: scadenza illeggibile`).toBe(false);
|
||||
if (a.from !== null) expect(Date.parse(a.to)).toBeGreaterThanOrEqual(Date.parse(a.from));
|
||||
expect(a.quando.length).toBeGreaterThan(0);
|
||||
expect(a.testo.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('coerenza dei calendari trascritti', () => {
|
||||
for (const c of calendari) {
|
||||
describe(c.id, () => {
|
||||
it('la finestra è leggibile e ha un verso', () => {
|
||||
expect(Number.isNaN(Date.parse(c.from))).toBe(false);
|
||||
if (c.to !== null) {
|
||||
expect(Number.isNaN(Date.parse(c.to))).toBe(false);
|
||||
expect(Date.parse(c.to)).toBeGreaterThan(Date.parse(c.from));
|
||||
}
|
||||
});
|
||||
|
||||
it('le fasce orarie sono uniche e in ordine crescente', () => {
|
||||
const ore = c.righe.map((r) => r.ora);
|
||||
expect(new Set(ore).size, 'fascia ripetuta').toBe(ore.length);
|
||||
const minuti = ore.map((o) => {
|
||||
const [h, m] = o.split('.');
|
||||
return Number(h) * 60 + Number(m);
|
||||
});
|
||||
expect(minuti.every((v, i) => i === 0 || v > minuti[i - 1]), `fasce fuori ordine: ${ore.join(' ')}`).toBe(true);
|
||||
});
|
||||
|
||||
it('ogni cella sta su un giorno esistente e porta una disciplina nota', () => {
|
||||
for (const r of c.righe) {
|
||||
for (const [giorno, disciplina] of Object.entries(r.celle)) {
|
||||
expect(GIORNI, `${r.ora}: giorno ${giorno} inesistente`).toContain(giorno as Giorno);
|
||||
expect(DISCIPLINE, `${r.ora} ${giorno}: disciplina ${disciplina} sconosciuta`).toContain(disciplina!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Una lezione in un giorno di chiusura è la contraddizione che nessuno nota leggendo la
|
||||
// griglia: la colonna è barrata e la cella ci finisce dentro lo stesso.
|
||||
it('nessuna lezione cade in un giorno di chiusura', () => {
|
||||
for (const r of c.righe) {
|
||||
for (const g of c.chiusi) {
|
||||
expect(r.celle[g], `${r.ora}: lezione di ${g}, che è chiuso`).toBeUndefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('le note dei giorni si riferiscono a giorni aperti', () => {
|
||||
for (const g of Object.keys(c.noteGiorno ?? {}) as Giorno[]) {
|
||||
expect(GIORNI).toContain(g);
|
||||
expect(c.chiusi, `nota su ${g}, che è dichiarato chiuso`).not.toContain(g);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user