Add CI, a pre-commit hook, and an accessibility pass on the panel's own views
CI (.github/workflows/tests.yml) reuses docker-compose.test.yml verbatim for the Postgres job rather than re-describing that environment in YAML, so the Docker image stays the single source of truth for it. Fixes a real mismatch found while wiring PHP versions into the workflow: composer.json declared "php": "^8.3", but the locked Symfony packages require >=8.4.1 — installing on 8.3 would fail at `composer install`, exactly what the Dockerfile's own comment already warned about. Bumped the constraint and resynced composer.lock's content-hash (no package versions changed). The pre-commit hook (.githooks/pre-commit, activated via `git config core.hooksPath` in post-autoload-dump) runs `pint --dirty --test` and refuses to commit unformatted files rather than silently rewriting them. The accessibility pass only touches this app's own hand-written Blade views, not Filament's components: aria-hidden on icons that repeat adjacent text, an aria-label on the one icon that is the only signal of a health check's severity, rel="noopener" and a labelled announcement on preview links that open in a new tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
69
README.md
69
README.md
@@ -56,6 +56,15 @@ php artisan key:generate
|
||||
php artisan migrate --seed # crea roles, permisos y contenido de ejemplo
|
||||
```
|
||||
|
||||
`composer install` activa también [`.githooks/pre-commit`](.githooks/pre-commit)
|
||||
(`git config core.hooksPath .githooks`, dentro de `post-autoload-dump`): antes
|
||||
de cada commit corre `pint --dirty --test` sobre lo que has tocado y lo
|
||||
rechaza si algo no está formateado. Solo avisa, nunca reescribe por su cuenta
|
||||
un fichero que estás a punto de comitear — el arreglo es `pint --dirty` a
|
||||
mano, ver el diff, y comitear otra vez. Nada de esto sustituye a la CI: el
|
||||
hook protege el minuto a minuto de quien escribe, la CI protege lo que llega
|
||||
al remoto por si alguien comitea con `--no-verify`.
|
||||
|
||||
### 1. Registrar la aplicación en el servidor de identidad
|
||||
|
||||
En `https://oauth.pireon.dev` crea un cliente OAuth con:
|
||||
@@ -1340,6 +1349,14 @@ php artisan site:maintenance --up
|
||||
php artisan test # 391 tests, 1121 aserciones
|
||||
```
|
||||
|
||||
En cada push y cada pull request, [`.github/workflows/tests.yml`](.github/workflows/tests.yml)
|
||||
corre tres cosas: `pint --test`, esta suite contra SQLite (la vuelta rápida) y
|
||||
`docker compose -f docker-compose.test.yml run --rm test` — el mismo comando de
|
||||
más abajo, no una reimplementación en YAML — contra Postgres (la que de verdad
|
||||
importa). Reutiliza el fichero de Docker en vez de describir el entorno dos
|
||||
veces: un cambio en las extensiones de PHP o en la versión de Postgres se
|
||||
corrige en un solo sitio.
|
||||
|
||||
Cubren el acceso con email y contraseña (credenciales incorrectas
|
||||
indistinguibles de una dirección desconocida, cuenta desactivada, pendiente de
|
||||
aprobación o sin rol, límite de intentos y rotación de sesión), el flujo PKCE
|
||||
@@ -1357,19 +1374,18 @@ detectan, los permisos que ocultan resultados y los códigos de salida), las
|
||||
redirecciones (que se crean solas, que no encadenan ni dan vueltas y que la web
|
||||
las recibe de una vez), los 404 que reporta el frontend (que el endpoint no se
|
||||
abre sin secreto, que cuenta en vez de acumular filas, que normaliza igual que
|
||||
una redirección, que tira los escaneos y que crear la regla cierra el aviso), el
|
||||
la revisión (que se puede pedir sin permiso de publicar, a
|
||||
quién avisa, que devolver exige motivo, que publicar la cierra y programar no),
|
||||
las ediciones simultáneas (que el segundo guardado se rechaza, que guardarse a
|
||||
uno mismo no cuenta como conflicto y que la presencia se olvida sola),
|
||||
las cifras de lectura (que suman en una fila por día, que cada idioma cuenta
|
||||
aparte y que en la tabla no cabe nada de un visitante), el contrato de la API
|
||||
contra su especificación campo a campo y los tipos generados de ella, las
|
||||
traducciones que se quedaron atrás (que el slug no cuenta, que una vacía no
|
||||
cuenta y que ponerse al día lo limpia), las ediciones simultáneas y sus
|
||||
acciones en lote, las sesiones activas (que no se puede cerrar la propia) y el
|
||||
aviso de títulos parecidos,
|
||||
el historial (quién publicó, el diff y restaurar), la
|
||||
una redirección, que tira los escaneos y que crear la regla cierra el aviso), la
|
||||
revisión (que se puede pedir sin permiso de publicar, a quién avisa, que
|
||||
devolver exige motivo, que publicar la cierra y programar no) y sus acciones en
|
||||
lote, las ediciones simultáneas (que el segundo guardado se rechaza, que
|
||||
guardarse a uno mismo no cuenta como conflicto y que la presencia se olvida
|
||||
sola), las cifras de lectura (que suman en una fila por día, que cada idioma
|
||||
cuenta aparte y que en la tabla no cabe nada de un visitante), el contrato de
|
||||
la API contra su especificación campo a campo y los tipos generados de ella,
|
||||
las traducciones que se quedaron atrás (que el slug no cuenta, que una vacía
|
||||
no cuenta y que ponerse al día lo limpia), las sesiones activas (que no se
|
||||
puede cerrar la propia) y el aviso de títulos parecidos, el historial (quién
|
||||
publicó, el diff y restaurar), la
|
||||
limpieza (qué se conserva siempre y qué nunca se borra sin pedirlo), las
|
||||
ventanas de publicación (que se respetan sin nada corriendo, que preview las
|
||||
salta y que el cierre de convocatorias es idempotente), la sincronización con
|
||||
@@ -1394,6 +1410,33 @@ inyecta `Accept-Language: en-us` por defecto, y la API respeta la negociación d
|
||||
idioma. Si un test espera castellano tiene que pedirlo explícitamente
|
||||
(`Accept-Language: es` o `?lang=es`), como hace `PublicApiTest`.
|
||||
|
||||
## Accesibilidad del propio panel
|
||||
|
||||
El texto alternativo de la web pública tiene su sección propia; esto es sobre
|
||||
la herramienta, no sobre lo que publica. Filament ya se ocupa de lo suyo —
|
||||
contraste, roles ARIA, navegación por teclado entre pestañas— y no se ha
|
||||
tocado nada de eso. Lo que se ha revisado son las **pocas vistas Blade
|
||||
escritas a mano** en `resources/views/filament/`, donde nada garantiza esas
|
||||
cosas por defecto.
|
||||
|
||||
Dos reglas, aplicadas donde tocaba:
|
||||
|
||||
**Un icono junto a un texto que ya dice lo mismo lleva `aria-hidden="true"`.**
|
||||
El aviso de mantenimiento, el de salud del sitio: el icono de aviso al lado de
|
||||
«La web pública está en mantenimiento» no añade nada que un lector de
|
||||
pantalla necesite oír dos veces.
|
||||
|
||||
**Un icono que es la única señal de algo lleva su propio `aria-label`.** El de
|
||||
cada comprobación de salud es el único caso: la gravedad —fallo o aviso— no
|
||||
está escrita en ningún sitio, solo se ve en la forma y el color del icono.
|
||||
Ocultarlo habría sido perder esa información, no limpiarla.
|
||||
|
||||
De paso, los enlaces de vista previa que abren en pestaña nueva llevan
|
||||
`rel="noopener"` (la página de destino, en otro origen, no debe quedarse con
|
||||
una referencia a esta) y el campo de solo lectura que copia la URL tiene su
|
||||
propio `aria-label`, porque el texto que lo identifica visualmente es un
|
||||
párrafo aparte sin relación programática con él.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user