Skip to content

Automatizar pruebas E2E de contenido: a11y + frontmatter render + no scroll horizontal (con límite configurable) #164

Description

@Scot3004

Motivación

  • Detectar automáticamente posts que provoquen regresiones de accesibilidad (a11y), frontmatter no renderizado o scroll horizontal inesperado.
  • Ejecutar una única visita por post y validar 3 comprobaciones para reducir tiempo de test.
  • Añadir una env var para controlar el número máximo de posts por colección (útil para PRs vs nightly).

Criterios de aceptación

  • Existe tools/getAllPosts.ts que devuelve [{ slug, title, locale, path }] y respeta la env var MAX_POSTS_PER_COLLECTION.
  • Hay una suite E2E tests/e2e/content.spec.ts que por cada post hace:
    • Auditoría a11y con @axe-core/playwright.
    • Verifica que el h1 (o título) y metatags se rendericen según frontmatter.
    • Verifica que no haya scroll horizontal (document.documentElement.scrollWidth <= window.innerWidth).
  • En CI:
    • PRs usan subset configurable (ej. MAX_POSTS_PER_COLLECTION=20).
    • Job nightly recorre todo el contenido (sin límite).
  • Fallos generan artefactos (JSON/HTML) para debugging.

Checklist

  • Añadir env var: MAX_POSTS_PER_COLLECTION (default: unset => full run; en PRs setear 20).
  • Crear/actualizar tools/getAllPosts.ts para respetar la env var.
  • Añadir dependencia @axe-core/playwright.
  • Implementar tests/e2e/content.spec.ts (parametrizado por slugs).
  • Configurar CI:
    • PR workflow: ejecutar subset (usar env var).
    • Nightly workflow: full run + subir artefactos.
  • Añadir tags/labels a tests: @content, @a11y, @slow según corresponda.
  • Guardar reportes en test-results/e2e/content/ cuando falle.

Snippet: lectura de env var en tools/getAllPosts.ts

// tools/getAllPosts.ts
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'

export function getAllPosts(collectionDir: string) {
  const files = fs.readdirSync(collectionDir).filter(f => f.endsWith('.md'))
  const max = process.env.MAX_POSTS_PER_COLLECTION ? Number(process.env.MAX_POSTS_PER_COLLECTION) : undefined
  const selected = typeof max === 'number' ? files.slice(0, max) : files
  return selected.map(file => {
    const content = fs.readFileSync(path.join(collectionDir, file), 'utf-8')
    const { data } = matter(content)
    const slug = file.replace(/\.md$/, '')
    return { slug, title: data.title, locale: data.lang || 'es', path: file }
  })
}

Snippet: uso en tests/e2e/content.spec.ts

import { test, expect } from '@playwright/test'
import { injectAxe, checkA11y } from '@axe-core/playwright'
import { getAllPosts } from '../../tools/getAllPosts'

const posts = getAllPosts('src/content/blog/es') // respetará MAX_POSTS_PER_COLLECTION

for (const p of posts) {
  test(`content: ${p.slug} — a11y + render + no horizontal scroll`, async ({ page }) => {
    await page.goto(`/es/posts/${p.slug}`)
    await injectAxe(page)
    await checkA11y(page)
    await expect(page.locator('h1')).toHaveText(p.title)
    const hasHorizontalScroll = await page.evaluate(() =>
      document.documentElement.scrollWidth > window.innerWidth
    )
    expect(hasHorizontalScroll).toBe(false)
  })
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions