import { render, screen } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { Hero } from '../Hero'; describe('', () => { it('renders the eyebrow', () => { render(); expect(screen.getByText(/claridad estratégica para tu negocio/i)).toBeInTheDocument(); }); it('renders the headline copy', () => { const { container } = render(); expect(container.textContent).toMatch(/Tu negocio/i); expect(container.textContent).toMatch(/más esfuerzo/i); expect(container.textContent).toMatch(/más claridad/i); }); it('renders the CTA', () => { render(); expect(screen.getByRole('link', { name: /agenda una sesión de claridad/i })).toHaveAttribute('href', '#contacto'); }); it('offers a secondary route into the process for readers not ready to convert', () => { render(); expect(screen.getByRole('link', { name: /ver cómo trabajamos/i })).toHaveAttribute('href', '#proceso'); }); it('does NOT render scarcity (cupos/cohorte)', () => { const { container } = render(); expect(container.textContent).not.toMatch(/cupos|cohorte|generación fundadora/i); }); it('does not pre-empt the Dolores section with its own copy', () => { const { container } = render(); // The hero used to rotate the same four quotes that Dolores renders in // full further down. Duplicating them spent the emotional beat early and // competed with the headline. expect(container.textContent).not.toMatch(/Trabajo más y gano casi lo mismo/i); }); // El hero es el LCP de la página, y la página se abre desde un enlace de // WhatsApp con datos móviles. Mientras el titular naciera en opacity 0 // esperando a que hidratara React, montara el IntersectionObserver y corriera // el stagger, lo primero que veía el lector era una pantalla en blanco — y si // el JS fallaba, se quedaba así. El export estático llegó a salir con 126 // elementos en opacity 0, el

entre ellos. it('entra pintado: nada del hero nace oculto', () => { const { container } = render(); const ocultos = Array.from(container.querySelectorAll('*')).filter( (el) => el.style.opacity === '0', ); expect(ocultos).toHaveLength(0); }); it('el titular no se difumina palabra a palabra', () => { const { container } = render(); const difuminados = Array.from(container.querySelectorAll('*')).filter((el) => el.style.filter.includes('blur'), ); expect(difuminados).toHaveLength(0); }); // El edificio es three.js: ~185 kB que el titular no puede esperar. Vive tras // next/dynamic con ssr:false, así que el hero se pinta entero sin él y el // export estático no lleva ni un . Cuando el dynamic() vivía en el // propio Hero — un server component — Next 14 se negaba a construir. it('el hero se pinta sin esperar al edificio', () => { const { container } = render(); expect(container.querySelector('h1')).toBeInTheDocument(); expect(container.querySelector('canvas')).toBeNull(); }); it('renders the Dream Outcome below the headline', () => { const { container } = render(); expect(container.textContent).toMatch(/plan a 12 meses/i); }); });