check_circle Pruebas Jest
Cargando resultados Jest...
Calidad sin secretos
Cada push ejecuta la suite unitaria, una suite de flujos en navegador real y un umbral de cobertura antes de que el build llegue a producción. Lo que ves aquí es el resultado real de la última ejecución, más una suite que puedes ejecutar en tu propio navegador ahora mismo.
Catorce comprobaciones se ejecutan en esta misma página: DOM, accesibilidad, i18n, red y tiempos. Nada se simula y nada viene de la caché.
Ejecuta pruebas de humo, estructura DOM, accesibilidad y rendimiento directamente en tu navegador.
Números directos de GitHub Actions. Los resultados de Jest salen con el build; el resumen de Puppeteer se obtiene en vivo de un Gist que el pipeline actualiza en cada ejecución.
Cargando resultados Jest...
Cargando resultados E2E...
Cargando cobertura...
Cuatro capas, cuatro herramientas. Cada fragmento viene directo de este repositorio.
test('closes mobile menu when a nav link is clicked', async () => {
const { container } = render(
<MemoryRouter>
<Navbar />
</MemoryRouter>
);
const toggle = screen.getByLabelText('Toggle navigation');
const menu = container.querySelector('#mobileMenu');
fireEvent.click(toggle);
expect(toggle).toHaveAttribute('aria-expanded', 'true');
expect(menu).toHaveClass('active');
fireEvent.click(screen.getByText(/Personal Projects/i));
await waitFor(() => {
expect(toggle).toHaveAttribute('aria-expanded', 'false');
expect(menu).not.toHaveClass('active');
});
});it('should NOT contain unsafe-inline in script-src', () => {
const headers = fs.readFileSync(headersPath, 'utf-8');
const cspLine = headers.match(/Content-Security-Policy:.*$/m);
expect(cspLine).toBeTruthy();
const scriptSrc = cspLine[0].match(/script-src ([^;]+)/)[1];
expect(scriptSrc).not.toContain("'unsafe-inline'");
});
it('should generate correct SHA-256 hash for JSON-LD script', () => {
const html = fs.readFileSync(htmlPath, 'utf-8');
const [, scriptContent] = html.match(
/<script type="application\/ld\+json">([\s\S]*?)<\/script>/
);
const calculatedHash = crypto
.createHash('sha256')
.update(scriptContent)
.digest('base64');
const hashes = JSON.parse(fs.readFileSync(hashesPath, 'utf-8'));
expect(hashes.jsonLd).toBe(`'sha256-${calculatedHash}'`);
});{
name: 'Cookie consent accept hides banner and stores consent',
run: async (page, step) => {
await step('Navigate to home page', () =>
page.goto(BASE_URL, { waitUntil: 'networkidle2', timeout: NAV_TIMEOUT })
);
await step('Cookie banner is shown', () =>
page.waitForSelector('#cookieConsentBanner', { timeout: NAV_TIMEOUT })
);
await step('Click "Accept all cookies"', () => page.click('#acceptCookies'));
await step('Banner is dismissed', () =>
page.waitForSelector('#cookieConsentBanner', { hidden: true, timeout: NAV_TIMEOUT })
);
await step('Consent cookie is stored', async () => {
const cookie = await page.evaluate(() => document.cookie);
assert(/cookie_consent=accepted/.test(cookie), `Consent cookie not set: "${cookie}"`);
});
},
},{
id: 'dom-safe-links',
nameKey: 'testDashboard.runner.tests.safeLinks.name',
run: async () => {
const externalLinks = Array.from(document.querySelectorAll('a[target="_blank"]'));
const insecureLinks = externalLinks.filter((link) => {
const rel = (link.getAttribute('rel') || '').toLowerCase();
return !rel.includes('noopener') && !rel.includes('noreferrer');
});
if (insecureLinks.length > 0) {
const hrefs = insecureLinks
.slice(0, 3)
.map((l) => l.getAttribute('href'))
.join(', ');
throw new Error(
`Found ${insecureLinks.length} external link(s) without rel="noopener noreferrer": ${hrefs}`
);
}
return {
message: `Verified ${externalLinks.length} external link(s) have secure rel attributes.`,
};
},
},Reportes HTML estáticos, regenerados en cada deploy.