QA Visuelle comme Étape du Pipeline CI
J’ai fusionné une PR le mois dernier. La revue de code était bonne. Les tests ont réussi. Puis j’ai ouvert le site sur mon téléphone et la barre latérale était complètement cassée.
La correction était triviale—une media query manquante. Le bug était évident dès qu’on regardait réellement la vue mobile. Personne ne l’a fait.
Alors j’ai ajouté une étape du pipeline qui regarde.
J’ouvre une issue GitHub qui dit :
Implémenter l’option d’Entrée Manuelle pour ajouter des clients. Lorsqu’elle est cliquée, ouvre un tiroir coulissant large avec un formulaire pour créer un nouveau client.
Un commit plus tard, la PR arrive avec plus de 30 captures d’écran prouvant que chaque état fonctionne dans chaque viewport. Zéro test manuel. Le seul effort a été d’écrire la description de la fonctionnalité.
Le Concept
À chaque push vers une PR, un workflow GitHub Actions :
- Démarre une stack Docker Compose (Django + Postgres) depuis la branche de la PR
- Exécute les migrations et insère un utilisateur de test
- Passe l’URL locale à Claude Code avec Playwright MCP (Chromium headless)
- Teste les éléments interactifs en se basant sur le diff de la PR
- Capture chaque état dans trois viewports
- Publie les résultats comme commentaire de la PR
Claude Code contrôle le navigateur. Docker Compose fournit l’application. GitHub Actions relie le tout. Les réviseurs voient la preuve de la fonctionnalité sans exécuter quoi que ce soit localement.
%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 3, "bottom": 10}}} }%%
graph TD
A["Push PR"] --> B
subgraph GHA["GitHub Actions Runner"]
B["Checkout + Docker Compose"] --> C["Migrations + Données de Test"]
C --> Agent
subgraph Agent["Claude Code + Playwright"]
direction LR
D["Lire Diff PR"] --> E["Mapper Fichiers → URLs"]
E --> F["Connexion + Navigation"]
F --> G["Capture ×3 Viewports"]
end
end
Agent --> H["Commentaire PR"]
style GHA fill:transparent,stroke:#888
style Agent fill:transparent,stroke:#888
Le workflow lui-même est simple :
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
# ... Configuration Docker Compose, migrations, création utilisateur de 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 de Viewports
| Appareil | Taille | Pourquoi |
|---|---|---|
| Bureau | 1440×900 | Ordinateur portable standard |
| Tablette | 1024×768 | iPad portrait |
| Mobile | 640×1136 | iPhone SE |
Chaque état interactif est capturé sur les trois. Un composant de barre latérale génère plus de 12 captures d’écran :
- Chargement de page (fermé) — 3 viewports
- Clic du déclencheur — 3 viewports
- Complètement ouvert — 3 viewports
- Animation de fermeture — 3 viewports
Multipliez par les thèmes clair/sombre et vous regardez 24 captures d’écran pour un composant. C’est le but. Preuve visuelle exhaustive comme artefact CI.
Ce Que Ça Produit Réellement
Voici la sortie réelle d’une PR qui a ajouté une page d’entrée manuelle. L’agent publie un commentaire de PR avec un résumé structuré, des captures d’écran à chaque viewport et une liste de vérification des tests :

Sous ce résumé, l’agent ajoute des captures d’écran de chaque état qu’il a testé. Voici la vue bureau—liste de clients avec données, plus le tiroir Ajouter Client ouvert :

L’agent a testé :
- État du formulaire vide
- Formulaire avec erreurs de validation (champ nom requis manquant)
- Soumission réussie du formulaire
- Rendu de la liste de clients avec données
- Solutions de repli d’avatar sans image
- Transitions d’ouverture/fermeture du modal
- Les trois viewports
J’ai sauté les états de focus des champs individuels pour cette PR—rendements décroissants sur un formulaire simple. L’agent peut le faire, mais 50 captures d’écran d’entrées de texte recevant le focus ne sont pas un signal utile.
Le commentaire de la PR devient un changelog visuel. Chaque exécution de test se termine par une liste de vérification :

Dans six mois, je peux regarder n’importe quelle PR et voir exactement à quoi ressemblait l’UI quand elle a été livrée.
Le Manifeste de Test
Le prompt dans le workflow ci-dessus est générique — il fonctionne pour n’importe quelle PR. La spécificité vient du diff lui-même. L’agent lit gh pr diff, découvre quels templates et vues ont changé, les mappe aux URLs et décide quoi tester.
Pour un composant de formulaire, cela signifie qu’il testera :
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
Aucun plan de test manuel nécessaire. L’agent déduit la surface de test à partir des changements de code.
Pourquoi des Captures d’Écran dans les Commentaires de PR
Les captures d’écran en ligne dans le commentaire de la PR ont quelques avantages :
- Visibles — les réviseurs les voient sans cliquer sur les artefacts
- Contextuelles — juste à côté du diff qu’ils examinent
- Comparables — chaque push met à jour le commentaire, donc vous voyez avant/après
- Auditables — l’historique des commentaires montre ce qui a été testé à chaque commit
Ce Que Ça Attrape
Bugs réels attrapés la première semaine :
- Menu déroulant coupé par parent avec
overflow: hidden(visible uniquement sur tablette) - Texte du bouton invisible en mode sombre (mauvaise variable CSS)
- Labels de formulaire mal alignés sur iPhone (problème de gap flexbox)
- État de survol bloqué après touch (nécessitait
@media (hover: hover)) - Arrière-plan du modal ne couvrant pas tout le viewport sur tablette paysage
Chacun d’eux a passé la revue de code. Chacun était évident dans les captures d’écran.
Le Coût
Exécuter Claude avec Playwright MCP en CI prend 2-4 minutes selon le nombre d’états à tester. Pour une PR typique touchant un composant, c’est environ 90 secondes.
Comparez avec : déployer en production et apprendre des utilisateurs que la mise en page mobile est cassée. Inestimable.
Le Changement
La QA visuelle a toujours été le goulot d’étranglement. Vous pouvez automatiser les tests unitaires, les tests d’intégration, même les flux de bout en bout — mais quelqu’un doit toujours regarder l’UI. C’est vrai depuis des décennies.
Ce n’est plus vrai. Les agents avec navigateurs n’exécutent pas seulement des scripts. Ils interprètent, naviguent, interagissent et jugent. La surface de test n’est pas codée en dur — elle est déduite du changement. Chaque PR obtient une couverture visuelle exhaustive qu’aucun processus manuel ne pourrait égaler.
Cela change le QE d’une porte à une garantie. Pas “quelqu’un l’a-t-il vérifié” mais “le pipeline l’a vérifié, voici la preuve”. Chaque PR, chaque push, chaque viewport. Le rôle de QE ne disparaît pas. Il se déplace en amont. Au lieu d’exécuter des plans de test, vous définissez ce dont l’agent devrait se soucier. Au lieu d’attraper des bugs, vous concevez le système qui les attrape.