import { render, screen } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { Proceso } from '../Proceso'; import { proceso, creditosFotos } from '@/lib/content'; describe('', () => { it('renders the three approved phases as a strip', () => { render(); expect(proceso.fases).toHaveLength(3); for (const fase of proceso.fases) { expect(screen.getByText(fase)).toBeInTheDocument(); } }); it('renders all eight stages, numbered 01 through 08', () => { render(); expect(proceso.etapas).toHaveLength(8); for (const etapa of proceso.etapas) { expect(screen.getByText(etapa.name)).toBeInTheDocument(); expect(screen.getByText(etapa.n)).toBeInTheDocument(); } }); // The argument of the section: "claridad estratégica" is abstract until it // is a document you can hold. The lámina labels this block on all eight of // its cards and marks every line — losing either turns the deliverables back // into a list of service names. it('labels the deliverables block on every card and marks every line', () => { const { container } = render(); expect(screen.getAllByText(proceso.etiquetaEntregables)).toHaveLength(8); for (const entregable of proceso.etapas.flatMap((e) => e.entregables)) { expect(screen.getByText(entregable)).toBeInTheDocument(); } // One
    of deliverables per stage, every line of it carrying an icon. const listas = container.querySelectorAll('ul'); expect(listas).toHaveLength(proceso.etapas.length); listas.forEach((lista, i) => { const esperados = proceso.etapas[i].entregables.length; expect(lista.querySelectorAll(':scope > li')).toHaveLength(esperados); expect(lista.querySelectorAll(':scope > li > svg')).toHaveLength(esperados); }); }); // The icon map is keyed by the copy itself, so rewording a deliverable in // lib/content.ts without updating the map would render a blank space where // its mark should be. Nothing else would fail. it('gives every deliverable its own icon, none of them empty', () => { const { container } = render(); const items = Array.from(container.querySelectorAll('ul > li')); expect(items).toHaveLength(proceso.etapas.reduce((n, e) => n + e.entregables.length, 0)); for (const item of items) { const svg = item.querySelector(':scope > svg'); expect(svg, `«${item.textContent}» se quedó sin ícono`).not.toBeNull(); expect(svg!.children.length, `el ícono de «${item.textContent}» está vacío`).toBeGreaterThan(0); } }); // Stage 01's medallion was the magnifier, and so was its own "Diagnóstico // ejecutivo" line — the same glyph twice in one card, which reads as a // mistake. Nothing but the eye catches this, so the eye gets help. it('never repeats a glyph inside the same card', () => { const { container } = render(); const tarjetas = Array.from(container.querySelectorAll('ol'))[1].querySelectorAll(':scope > li'); tarjetas.forEach((tarjeta, i) => { const glifos = Array.from(tarjeta.querySelectorAll('svg')).map((s) => s.innerHTML); expect(new Set(glifos).size, `la etapa ${proceso.etapas[i].n} repite un ícono`).toBe(glifos.length); }); }); it('closes on the One Year Plan, the payoff the other seven point at', () => { render(); expect(screen.getByText('One Year Plan')).toBeInTheDocument(); expect(screen.getByText('Roadmap a 12 meses')).toBeInTheDocument(); expect(screen.getByText('Gobernanza del crecimiento')).toBeInTheDocument(); }); // The stages are a sequence, so the order has to reach assistive tech // through the markup — otherwise it lives only in the drawn number, which is // decorative and never announced. it('carries the order in the markup, not just in the drawn number', () => { const { container } = render(); const listas = container.querySelectorAll('ol'); expect(listas).toHaveLength(2); // the phase strip and the eight stages expect(listas[1].querySelectorAll(':scope > li')).toHaveLength(8); }); // The footer is the only place in the project where the photo licences are // honoured, and it renders `creditosFotos` verbatim. An image added here // without an entry there would go uncredited and nothing else would notice. it('credits every photograph it shows', () => { const { container } = render(); const fotos = Array.from(container.querySelectorAll('img')).map((i) => i.getAttribute('src')); expect(fotos).toHaveLength(proceso.etapas.length); for (const src of fotos) { expect( creditosFotos.some((c) => 'archivo' in c && c.archivo === src), `«${src}» no tiene crédito en creditosFotos`, ).toBe(true); } }); it('loads the photographs lazily, since none of them is above the fold', () => { const { container } = render(); for (const img of Array.from(container.querySelectorAll('img'))) { expect(img).toHaveAttribute('loading', 'lazy'); } }); // Everything here is adapted from the dueña's working deck for a real // client. None of that client's detail may reach a public page. it('leaks nothing that identifies the client the deck was built for', () => { const { container } = render(); expect(container.textContent).not.toMatch( /cápsulas|gomitas|tabletas|planta|sell in|sell out|mercado mexicano|líderes de la categoría/i, ); }); });