Files
InsanityLab-web/tests/modifiche-agosto.test.ts
Adriano 3741ce34ac Modifiche chieste dal cliente il 17 agosto
Il documento MODIFICHE_PER_SITO.pdf porta cinque cose.

1. "Small Group" si chiama "Personalized Training Group". Il nome vive anche
   nel database dei contenuti, dove `resolveContent` legge prima della copia
   nel codice: `scripts/testi-17-agosto.mjs` aggiorna le 19 righe rimaste,
   una volta sola, correggendo anche gli articoli ("Lo Small Group" -> "Il
   Personalized Training Group"). Lo slug della scheda resta `small-group`:
   cambiarlo romperebbe i link gia' girati su WhatsApp.

2. Dalla pagina Servizi le card instradano dove serve. "One to One" apre la
   vista Training con One to One, Personalized Training Group e Rehab;
   "Class" quella con Holistic e Reformer. Le viste sono `/training/<vista>`;
   una vista inventata e' 404, non l'elenco intero, altrimenti un indirizzo
   rotto continuerebbe a sembrare buono.

3. La Performance Class non si eroga piu': la scheda che la spiegava
   risponde 404, la card "Class" resta in griglia. Con la scheda e' caduta la
   tabella prezzi, che era solo sua.

4. Il Check-up racconta Base e Avanzato: trafiletto nuovo nella card e
   sezione "Check-up Avanzato" nella scheda, con la Wellness Tower e i cinque
   punti. Il calendario delle class si apre ora anche dalla card Class in
   griglia e dalla scheda Reformer, oltre che da Holistic.

5. "Promo ed eventi" diventa una pagina di scelta: le promozioni restano su
   /promo-eventi/promo, l'inaugurazione del 19 settembre sta su
   /promo-eventi/eventi. Il testo dice che il programma della giornata manca
   ancora, ed e' editabile dal pannello.

Corretti due link del footer che portavano a schede inesistenti
(`/services/personal-training` non e' mai stato uno slug).

181 test verdi (157 + 24 nuovi), build ok, `astro check` invariato.
2026-08-18 17:06:05 +02:00

190 lines
8.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { createDb } from '../src/lib/db';
import { services, getService } from '../src/data/services';
import { trainings, trainingDaMostrare, VISTE_TRAINING } from '../src/data/trainings';
import { contentSeed } from '../src/data/content-seed';
import { site } from '../src/data/site';
import { PAGINE_CON_CALENDARIO, CARD_CON_CALENDARIO } from '../src/data/calendari';
import { rinominaSmallGroup, calcola, applica, TESTI } from '../scripts/testi-17-agosto.mjs';
// Le modifiche chieste dal cliente il 17/08/2026 (MODIFICHE_PER_SITO.pdf).
describe('Small Group è diventato Personalized Training Group', () => {
it('non resta traccia del vecchio nome nei dati né nel seed', () => {
const testi = [
...services.flatMap((s) => [s.title, s.subtitle, s.excerpt, s.longDescription, ...s.features, ...s.faq.flatMap((f) => [f.q, f.a])]),
...trainings.flatMap((t) => [t.title, t.caption, t.description]),
...site.footerTraining.map((v) => v.label),
...contentSeed.map((e) => e.value),
];
expect(testi.filter((v) => /small group/i.test(v))).toEqual([]);
});
// Lo slug resta `small-group`: è l'indirizzo della scheda, e cambiarlo romperebbe i link
// già girati su WhatsApp senza che nessuno se ne accorga.
it('lo slug della scheda non cambia', () => {
expect(getService('small-group')?.title).toBe('Personalized Training Group');
});
it('gli articoli restano corretti, e passarci due volte non cambia niente', () => {
const casi: [string, string][] = [
['Lo Small Group di InsanityLab è un allenamento', 'Il Personalized Training Group di InsanityLab è un allenamento'],
['nuovi format come gli small group e una', 'nuovi format come i Personalized Training Group e una'],
['One to One / Small Group / Rehab', 'One to One / Personalized Training Group / Rehab'],
['Small Groups', 'Personalized Training Group'],
['in formula small group (massimo 4 persone)', 'in formula Personalized Training Group (massimo 4 persone)'],
];
for (const [prima, dopo] of casi) {
expect(rinominaSmallGroup(prima)).toBe(dopo);
expect(rinominaSmallGroup(dopo)).toBe(dopo);
}
});
});
describe('lo script porta i testi nuovi in un database già scritto', () => {
it('aggiorna le righe vecchie e al secondo giro non ha più niente da fare', () => {
const db = createDb(':memory:');
const scrivi = db.prepare('INSERT OR REPLACE INTO content_blocks (tag, type, value) VALUES (?, ?, ?)');
scrivi.run('service.one-to-one.subtitle', 'text', 'One to One / Small Group / Rehab');
scrivi.run('service.profilazione.subtitle', 'text', 'Profilazione e valutazione fisica');
scrivi.run('footer.services.4.label', 'text', 'Profilazione');
const cambi = applica(db);
const dopo = (tag: string) => (db.prepare('SELECT value FROM content_blocks WHERE tag = ?').get(tag) as { value: string }).value;
expect(dopo('service.one-to-one.subtitle')).toBe('One to One / Personalized Training Group / Rehab');
expect(dopo('service.profilazione.subtitle')).toBe('Base e avanzato');
expect(dopo('footer.services.4.label')).toBe('Check-up');
expect(cambi.length).toBeGreaterThanOrEqual(3);
expect(calcola(db)).toEqual([]);
});
// Un `--dry-run` che scrivesse renderebbe inutile guardare prima di toccare la produzione.
it('con --dry-run non tocca niente', () => {
const db = createDb(':memory:');
db.prepare('INSERT OR REPLACE INTO content_blocks (tag, type, value) VALUES (?, ?, ?)')
.run('promo.card.2.title', 'text', 'Small Group');
expect(applica(db, { dryRun: true }).length).toBe(1);
expect((db.prepare('SELECT value FROM content_blocks WHERE tag = ?').get('promo.card.2.title') as { value: string }).value)
.toBe('Small Group');
});
it('i testi riscritti a mano corrispondono a quelli del seed', () => {
const seed = new Map(contentSeed.map((e) => [e.tag, e.value]));
for (const [tag, valore] of Object.entries(TESTI)) expect(seed.get(tag)).toBe(valore);
});
});
describe('viste di /training', () => {
it('senza vista si vedono tutti i training', () => {
expect(trainingDaMostrare()?.map((t) => t.id)).toEqual(trainings.map((t) => t.id));
});
it('One to One porta a One to One, Personalized Training Group e Rehab', () => {
expect(trainingDaMostrare('one-to-one')?.map((t) => t.id)).toEqual(['one-to-one', 'small-groups', 'rehab']);
});
it('Class porta a Holistic e Reformer', () => {
expect(trainingDaMostrare('class')?.map((t) => t.id)).toEqual(['holistic-class', 'reformer-class']);
});
// Un filtro sbagliato che mostra tutto farebbe passare per buono un indirizzo rotto.
it('una vista inventata è 404, non l\'elenco intero', () => {
expect(trainingDaMostrare('inesistente')).toBeNull();
});
it('ogni vista nomina solo training che esistono', () => {
for (const v of Object.values(VISTE_TRAINING)) {
for (const id of v.blocchi) expect(trainings.some((t) => t.id === id)).toBe(true);
}
});
});
describe('instradamento delle card di /services', () => {
const href = (slug: string) => { const s = getService(slug)!; return s.href ?? `/services/${s.slug}`; };
it('One to One e Class portano alle viste di training', () => {
expect(href('one-to-one')).toBe('/training/one-to-one');
expect(href('performance-class')).toBe('/training/class');
});
it('Check-up porta alla propria scheda', () => {
expect(href('profilazione')).toBe('/services/profilazione');
});
// La Performance Class non si eroga più: la card resta, la scheda che la spiegava no.
it('la Performance Class non ha più una scheda ma resta in griglia', () => {
expect(getService('performance-class')?.page).toBe(false);
expect(getService('performance-class')?.grid).not.toBe(false);
});
it('nessun link del sito manda a una scheda che non esiste', () => {
const schede = new Set(services.filter((s) => s.page !== false).map((s) => `/services/${s.slug}`));
const interni = [...site.footerServices, ...site.footerTraining, ...site.navigation]
.map((v) => v.href)
.filter((h) => h.startsWith('/services/'));
for (const h of interni) expect(schede.has(h)).toBe(true);
});
});
describe('Check-up', () => {
const checkup = getService('profilazione')!;
it('il trafiletto della card racconta Base e Avanzato', () => {
expect(checkup.title).toBe('Check-up');
expect(checkup.subtitle).toBe('Base e avanzato');
expect(checkup.excerpt).toContain('Check-up Base');
expect(checkup.excerpt).toContain('Check-up Avanzato');
});
it('la scheda porta la sezione avanzata con i suoi cinque punti', () => {
expect(checkup.extra?.title).toBe('Check-up Avanzato');
expect(checkup.extra?.body).toContain('Wellness Tower');
expect(checkup.extra?.items).toHaveLength(5);
});
it('i testi della sezione avanzata sono modificabili dal pannello', () => {
const tags = new Set(contentSeed.map((e) => e.tag));
expect(tags.has('service.profilazione.extra.title')).toBe(true);
expect(tags.has('service.profilazione.extra.body')).toBe(true);
for (let i = 1; i <= 5; i++) expect(tags.has(`service.profilazione.extra.item.${i}`)).toBe(true);
});
});
describe('calendario delle class', () => {
// Il calendario si apre da due punti diversi: le schede delle class che si fanno, e la
// card «Class» della griglia servizi, che ha slug `performance-class`.
it('le schede sono Holistic e Reformer, la Performance Class è fuori', () => {
expect(PAGINE_CON_CALENDARIO).toEqual(['holistic-class', 'reformer-class']);
});
it('in griglia il bottone sta sulla card Class', () => {
expect(CARD_CON_CALENDARIO).toEqual(['performance-class']);
});
it('ogni slug nominato esiste davvero', () => {
for (const slug of [...PAGINE_CON_CALENDARIO, ...CARD_CON_CALENDARIO]) expect(getService(slug)).toBeDefined();
});
});
describe('promo ed eventi', () => {
it('i testi della pagina di scelta e degli eventi sono nel seed', () => {
const tags = new Set(contentSeed.map((e) => e.tag));
for (const tag of ['promo-eventi.hero.title', 'promo-eventi.promo.label', 'promo-eventi.eventi.label',
'eventi.hero.title', 'eventi.inaugurazione.title', 'eventi.inaugurazione.body']) {
expect(tags.has(tag)).toBe(true);
}
});
it('l\'inaugurazione dice la data e che il programma manca ancora', () => {
const testo = contentSeed.find((e) => e.tag === 'eventi.inaugurazione.body')!.value;
expect(contentSeed.find((e) => e.tag === 'eventi.inaugurazione.title')!.value).toContain('19 settembre 2026');
expect(testo).toContain('programma completo');
});
// La voce di menu continua a puntare qui: è l'indirizzo già girato ai clienti.
it('il menu punta alla pagina di scelta', () => {
expect(site.navigation.some((v) => v.href === '/promo-eventi')).toBe(true);
});
});