5be0fa787c
"Scarica in PDF" non apre piu' la stampa del browser: e' un link a /calendario/<id>.pdf che restituisce un documento disegnato dal dato con PDFKit, con Content-Disposition attachment. Serve perche' il calendario si gira su WhatsApp, ed e' cosi' che questo centro parla coi clienti: chi lo riceve non ha il sito aperto davanti. Niente browser headless: Chromium in un container da 2 vCPU costa centinaia di megabyte e un processo per richiesta, per una griglia di sette colonne. Le linee della griglia si tracciano in una passata a parte, dopo i riempimenti: disegnandole riga per riga, il riempimento della riga seguente copriva quella appena fatta e due lezioni uguali in fasce consecutive si leggevano come un blocco unico lungo il doppio. Quattro test: intestazione %PDF- e coda %%EOF (un documento non chiuso arriva monco e il browser mostra una pagina bianca invece di un errore), una pagina sola, nomi di file distinti. Un id sconosciuto e' 404, non un PDF vuoto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
36 lines
1.7 KiB
TypeScript
36 lines
1.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { calendari } from '../src/data/calendari';
|
|
import { generaCalendarioPdf, nomeFilePdf } from '../src/lib/calendario-pdf';
|
|
|
|
describe('PDF del calendario', () => {
|
|
for (const cal of calendari) {
|
|
it(`${cal.id}: produce un PDF valido`, async () => {
|
|
const pdf = await generaCalendarioPdf(cal);
|
|
// Un PDF comincia sempre con %PDF- e finisce con %%EOF: se il documento non venisse
|
|
// chiuso (`doc.end()` mancante, o un errore a metà) il file arriverebbe monco al
|
|
// browser, che mostrerebbe una pagina bianca invece di un errore.
|
|
expect(pdf.subarray(0, 5).toString()).toBe('%PDF-');
|
|
expect(pdf.subarray(-8).toString()).toContain('%%EOF');
|
|
expect(pdf.length).toBeGreaterThan(2000);
|
|
});
|
|
}
|
|
|
|
// La griglia deve stare in una pagina sola: l'incrocio giorno/ora è proprio ciò che si va a
|
|
// cercare, e spezzarlo a metà rende il documento inutile. Il conteggio si legge dall'oggetto
|
|
// /Type /Pages, che dichiara quante pagine contiene.
|
|
it('sta in una pagina sola', async () => {
|
|
for (const cal of calendari) {
|
|
const pdf = await generaCalendarioPdf(cal);
|
|
const conteggio = /\/Type\s*\/Pages[^>]*?\/Count\s+(\d+)/s.exec(pdf.toString('latin1'));
|
|
expect(conteggio, `${cal.id}: numero di pagine non dichiarato`).not.toBeNull();
|
|
expect(Number(conteggio![1]), `${cal.id}: il calendario occupa più di una pagina`).toBe(1);
|
|
}
|
|
});
|
|
|
|
it('il nome del file distingue i due periodi', () => {
|
|
const nomi = calendari.map(nomeFilePdf);
|
|
expect(new Set(nomi).size).toBe(nomi.length);
|
|
for (const n of nomi) expect(n).toMatch(/^insanitylab-calendario-[a-z0-9-]+\.pdf$/);
|
|
});
|
|
});
|