Eran cuatro tarjetas alineadas describiendo desorden — lo contrario de lo que dicen. Ahora cruzan la sección en dos filas lentas y en direcciones opuestas, a sangre, mientras el remate en dorado no se mueve. Ese contraste es el argumento de la sección. Siguen legibles a propósito. Bajarlas a un susurro habría dado mejor textura y habría tirado a la basura la voz del cliente: son palabras suyas, verbatim de la diapositiva 3. El murmullo lo hace el movimiento. Cada frase se pinta cuatro veces —dos por fila para que el bucle no tenga costura, y dos filas— pero solo una llega a los lectores de pantalla. Sin eso recitarían la lista entera de dolores y luego la recitarían otra vez. Hay una prueba que lo exige. Con prefers-reduced-motion no hay rótulo: las cuatro frases se leen quietas a dos columnas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
import { render, screen } from '@testing-library/react';
|
|
import { describe, it, expect } from 'vitest';
|
|
import { Dolores } from '../Dolores';
|
|
import { dolores } from '@/lib/content';
|
|
|
|
describe('<Dolores />', () => {
|
|
it('renders the four pains verbatim, in the client\'s first person', () => {
|
|
render(<Dolores />);
|
|
for (const dolor of dolores.quotes) {
|
|
expect(screen.getAllByText(new RegExp(dolor.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))).length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('says each pain only once out loud, however many times it is painted', () => {
|
|
const { container } = render(<Dolores />);
|
|
// The drifting rows repeat the four sentences four times over: twice per
|
|
// row so the loop has no seam, and twice again because there are two rows.
|
|
// Only one of those runs may reach assistive tech — otherwise a screen
|
|
// reader recites the whole list of pains, and then recites it again.
|
|
for (const dolor of dolores.quotes) {
|
|
const apariciones = Array.from(container.querySelectorAll('span')).filter(
|
|
(el) => el.textContent?.includes(dolor),
|
|
);
|
|
const audibles = apariciones.filter((el) => !el.closest('[aria-hidden="true"]'));
|
|
expect(apariciones.length).toBeGreaterThan(1);
|
|
expect(audibles).toHaveLength(1);
|
|
}
|
|
});
|
|
it('closes with the resolution, not with more effort', () => {
|
|
const { container } = render(<Dolores />);
|
|
expect(container.textContent).toContain('Esto no se resuelve con más esfuerzo.');
|
|
expect(container.textContent).toContain('Se resuelve con una estrategia clara.');
|
|
});
|
|
it('centres the knot so the conductor thread runs through it', () => {
|
|
const { container } = render(<Dolores />);
|
|
// The knot is the centred decorative svg of the section; the conductor
|
|
// thread runs down the centre, so only a centred knot gets traversed by
|
|
// it — stitching the pain to the resolution below.
|
|
const centrados = Array.from(container.querySelectorAll('svg')).filter((s) => {
|
|
const cls = s.getAttribute('class') ?? '';
|
|
return cls.includes('left-1/2') && cls.includes('-translate-x-1/2');
|
|
});
|
|
expect(centrados.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
it('does NOT introduce scarcity', () => {
|
|
const { container } = render(<Dolores />);
|
|
expect(container.textContent).not.toMatch(/cupos|cohorte|generación fundadora/i);
|
|
});
|
|
});
|