QA Visivo come Fase della Pipeline CI
Ho fatto il merge di una PR il mese scorso. La code review era andata bene. I test erano passati. Poi ho aperto il sito sul mio telefono e la sidebar era completamente rotta.
La soluzione era banale—mancava una media query. Il bug era ovvio una volta che effettivamente guardavi la vista mobile. Nessuno l’ha fatto.
Così ho aggiunto una fase della pipeline che guarda.
Apro una issue su GitHub che dice:
Implementa l’opzione di Inserimento Manuale per aggiungere clienti. Quando cliccata, apre un ampio drawer scorrevole con un form per creare un nuovo cliente.
Un commit dopo, la PR arriva con oltre 30 screenshot che dimostrano che ogni stato funziona in ogni viewport. Zero test manuali. L’unico sforzo è stato scrivere la descrizione della funzionalità.
Il Concetto
Ad ogni push su una PR, un workflow di GitHub Actions:
- Avvia uno stack Docker Compose (Django + Postgres) dal branch della PR
- Esegue le migrazioni e inserisce un utente di test
- Passa l’URL locale a Claude Code con Playwright MCP (Chromium headless)
- Testa elementi interattivi basandosi sul diff della PR
- Cattura screenshot di ogni stato su tre viewport
- Pubblica i risultati come commento alla PR
Claude Code controlla il browser. Docker Compose fornisce l’app. GitHub Actions collega tutto. I reviewer vedono prove di funzionalità senza eseguire nulla localmente.
%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 3, "bottom": 10}}} }%%
graph TD
A["Push alla PR"] --> B
subgraph GHA["GitHub Actions Runner"]
B["Checkout + Docker Compose"] --> C["Migrazioni + Dati di Test"]
C --> Agent
subgraph Agent["Claude Code + Playwright"]
direction LR
D["Leggi Diff PR"] --> E["Mappa File → URL"]
E --> F["Login + Naviga"]
F --> G["Screenshot ×3 Viewport"]
end
end
Agent --> H["Commento PR"]
style GHA fill:transparent,stroke:#888
style Agent fill:transparent,stroke:#888
Il workflow stesso è diretto:
name: UI Screenshots
on:
pull_request:
types: [opened, synchronize]
jobs:
screenshots:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
# ... Setup Docker Compose, migrazioni, creazione utente di test ...
- name: Start services
run: |
docker compose -f docker-compose.local.yml up -d --wait
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run Claude for Screenshots
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: >-
--allowed-tools
"mcp__playwright__*,
Bash(gh pr diff:*),
Bash(gh pr comment:*),
Read,Glob,Grep"
prompt: |
You are a visual QA agent. Your job is to visually
verify a UI deployment.
PR: ${{ github.event.pull_request.number }}
The Django app is running at http://localhost:8001
1. Run `gh pr diff` to find changed templates/views
2. Map changed files to URLs — only test pages
affected by this push
3. Login, then screenshot each page at three viewports:
1440x900, 1024x768, 640x1136
4. Save screenshots with descriptive filenames
5. Post a PR comment with:
- Each test performed with pass/fail
- Any visual issues found
- Total screenshots captured
La Matrice dei Viewport
| Dispositivo | Dimensione | Perché |
|---|---|---|
| Desktop | 1440×900 | Laptop standard |
| Tablet | 1024×768 | iPad portrait |
| Mobile | 640×1136 | iPhone SE |
Ogni stato interattivo viene catturato su tutti e tre. Un componente sidebar genera oltre 12 screenshot:
- Caricamento pagina (chiuso) — 3 viewport
- Click sul trigger — 3 viewport
- Completamente aperto — 3 viewport
- Animazione di chiusura — 3 viewport
Moltiplica per temi chiaro/scuro e stai guardando 24 screenshot per un componente. Questo è il punto. Prova visiva esaustiva come artefatto CI.
Cosa Produce Realmente
Ecco l’output reale di una PR che ha aggiunto una pagina di inserimento manuale. L’agente pubblica un commento alla PR con un riepilogo strutturato, screenshot per ogni viewport e una checklist di test:

Sotto quel riepilogo, l’agente aggiunge screenshot di ogni stato che ha testato. Ecco la vista desktop—lista clienti con dati, più il drawer Aggiungi Cliente aperto:

L’agente ha testato:
- Stato form vuoto
- Form con errori di validazione (campo nome richiesto mancante)
- Invio form riuscito
- Rendering lista clienti con dati
- Fallback avatar quando non c’è immagine
- Transizioni apertura/chiusura modal
- Tutti e tre i viewport
Ho saltato gli stati di focus dei singoli campi per questa PR—rendimenti decrescenti su un form semplice. L’agente può farlo, ma 50 screenshot di input di testo che ricevono il focus non è un segnale utile.
Il commento alla PR diventa un changelog visivo. Ogni esecuzione di test termina con una checklist:

Tra sei mesi, posso guardare qualsiasi PR e vedere esattamente come appariva l’UI quando è stata rilasciata.
Il Manifest dei Test
Il prompt nel workflow sopra è generico — funziona per qualsiasi PR. La specificità deriva dal diff stesso. L’agente legge gh pr diff, scopre quali template e view sono cambiati, li mappa agli URL e decide cosa testare.
Per un componente form, questo significa che testerà:
component: ClientForm
states:
- empty form
- filled form (valid data)
- validation error (submit without name)
- submitting (loading state)
- success (confirmation)
interactions:
- submit empty form (trigger validation)
- fill all fields, submit
- verify client appears in list after submit
Nessun piano di test manuale necessario. L’agente deduce la superficie di test dalle modifiche al codice.
Perché Screenshot nei Commenti alle PR
Gli screenshot inline nel commento alla PR hanno alcuni vantaggi:
- Visibili — i reviewer li vedono senza cliccare sugli artefatti
- Contestuali — proprio accanto al diff che stanno revisionando
- Confrontabili — ogni push aggiorna il commento, quindi vedi prima/dopo
- Verificabili — la cronologia dei commenti mostra cosa è stato testato ad ogni commit
Cosa Cattura Questo
Bug reali catturati nella prima settimana:
- Dropdown tagliato dal parent con
overflow: hidden(visibile solo su tablet) - Testo del pulsante invisibile in dark mode (variabile CSS sbagliata)
- Label del form disallineate su iPhone (problema flexbox gap)
- Stato hover bloccato dopo touch (necessario
@media (hover: hover)) - Backdrop del modal non copre l’intero viewport su tablet landscape
Ognuno di questi ha passato la code review. Ognuno era ovvio negli screenshot.
Il Costo
Eseguire Claude con Playwright MCP in CI richiede 2-4 minuti a seconda di quanti stati devono essere testati. Per una PR tipica che tocca un componente, sono circa 90 secondi.
Confronta con: rilasciare in produzione e scoprire dagli utenti che il layout mobile è rotto. Inestimabile.
Il Cambiamento
Il QA visivo è sempre stato il collo di bottiglia. Puoi automatizzare i test unitari, i test di integrazione, persino i flussi end-to-end — ma qualcuno deve ancora guardare l’UI. Questo è stato vero per decenni.
Non è più vero. Gli agenti con browser non eseguono solo script. Interpretano, navigano, interagiscono e giudicano. La superficie di test non è hardcoded — è dedotta dal cambiamento. Ogni PR ottiene una copertura visiva esaustiva che nessun processo manuale potrebbe eguagliare.
Questo cambia il QE da un gate a una garanzia. Non “qualcuno l’ha controllato” ma “la pipeline l’ha controllato, ecco la prova”. Ogni PR, ogni push, ogni viewport. Il ruolo del QE non scompare. Si sposta upstream. Invece di eseguire piani di test, stai definendo a cosa dovrebbe interessarsi l’agente. Invece di catturare bug, stai progettando il sistema che li cattura.