79 lines
4.0 KiB
TypeScript
79 lines
4.0 KiB
TypeScript
// tests/longevity/pagine.test.ts
|
|
import { describe, it, expect } from 'vitest';
|
|
import { readFileSync, existsSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
const leggi = (p: string) => readFileSync(join(process.cwd(), p), 'utf8');
|
|
|
|
describe('il guscio della piattaforma', () => {
|
|
it('il layout esiste e non monta header e footer pubblici', () => {
|
|
const l = leggi('src/layouts/Longevity.astro');
|
|
expect(l).not.toMatch(/import\s+Header/);
|
|
expect(l).not.toMatch(/import\s+Footer/);
|
|
expect(l).toMatch(/noindex/); // area riservata: mai indicizzata
|
|
});
|
|
|
|
it('il foglio di stile e globale e prefissato, non scoped', () => {
|
|
expect(existsSync(join(process.cwd(), 'src/styles/longevity.css'))).toBe(true);
|
|
const css = leggi('src/styles/longevity.css');
|
|
const classi = [...css.matchAll(/^\.([a-z-]+)/gm)].map((m) => m[1]);
|
|
expect(classi.length).toBeGreaterThan(3);
|
|
// "lg" da solo o "lg-qualcosa": il solo startsWith('lg') lascerebbe passare anche "lgfoo".
|
|
expect(classi.every((c) => c === 'lg' || c.startsWith('lg-'))).toBe(true);
|
|
});
|
|
|
|
it('usa i token del sito invece di inventare colori', () => {
|
|
const css = leggi('src/styles/longevity.css');
|
|
expect(css).toMatch(/var\(--c-/);
|
|
});
|
|
|
|
it('nessuna variabile porta un nome da semaforo (verde/buono/ok)', () => {
|
|
const css = leggi('src/styles/longevity.css').toLowerCase();
|
|
// il mattone del fuori range e ammesso; un verde "tutto bene" no
|
|
expect(css).not.toMatch(/--lg-ok|--lg-buono|--lg-verde/);
|
|
});
|
|
|
|
it("nelle regole di un punteggio compaiono solo i colori ammessi: l'accento, il tratteggio, il fuori range", () => {
|
|
// Il test sopra cerca nomi vietati: chi scrivesse un verde in esadecimale, o lo
|
|
// chiamasse con un nome innocuo (var(--lg-buona-salute)), passerebbe indisturbato.
|
|
// Qui si fa il contrario: si elenca ciò che PUò comparire in una regola che riguarda
|
|
// un punteggio (selettore con "lg-score"), e qualunque altro colore — letterale
|
|
// (esadecimale, rgb(), nome CSS) o var() con un altro nome — è una regressione.
|
|
const css = leggi('src/styles/longevity.css');
|
|
const AMMESSI = new Set([
|
|
'--c-accent', '--c-accent-dark', // l'accento del sito: l'unica leva per l'intensità
|
|
'--c-heading', '--c-text', '--c-text-light', // testo neutro, uguale ovunque nel sito
|
|
'--c-bg', '--c-bg-alt', // sfondi neutri
|
|
'--lg-line', '--lg-surface', // struttura locale, neutra
|
|
'--lg-tratteggio', // il perimetro di ciò che non sappiamo
|
|
'--lg-fuori-range', // SOLO una misura sospetta, mai un punteggio
|
|
]);
|
|
const NON_COLORE = /^(transparent|inherit|initial|unset|currentcolor|none)$/i;
|
|
const PROP_COLORE = /^(color|background|background-color|border(-\w+)?-color|outline-color|fill|stroke)$/i;
|
|
|
|
const violazioni: string[] = [];
|
|
for (const regola of css.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
|
const selettore = regola[1].trim();
|
|
if (!/lg-score/i.test(selettore)) continue; // solo le regole che riguardano un punteggio
|
|
for (const dich of regola[2].split(';')) {
|
|
const due = dich.indexOf(':');
|
|
if (due < 0) continue;
|
|
const prop = dich.slice(0, due).trim().toLowerCase();
|
|
const val = dich.slice(due + 1).trim();
|
|
if (!PROP_COLORE.test(prop) || val === '' || NON_COLORE.test(val)) continue;
|
|
const varMatch = val.match(/^var\(\s*(--[a-z0-9-]+)\s*(?:,.*)?\)$/i);
|
|
if (varMatch && AMMESSI.has(varMatch[1])) continue;
|
|
violazioni.push(`${selettore} { ${prop}: ${val} }`);
|
|
}
|
|
}
|
|
expect(violazioni).toEqual([]);
|
|
});
|
|
|
|
it('la rotta del cliente e sotto /longevity, e rimanda davvero al referto', () => {
|
|
const p = join(process.cwd(), 'src/pages/longevity/index.astro');
|
|
expect(existsSync(p)).toBe(true);
|
|
const src = readFileSync(p, 'utf8');
|
|
expect(src).toMatch(/Astro\.redirect\(\s*['"`]\/longevity\/io['"`]/);
|
|
});
|
|
});
|