test — test de fonctionnalité via le vrai navigateur
Pilote l'app en cours d'exécution à travers les flux modifiés comme le ferait un utilisateur, vérifie les résultats et mets en cache ce qui a fonctionné sous forme de playbook. Un changement orienté utilisateur qui n'a pas passé ceci (ou une preuve de test unitaire équivalente) n'est pas terminé.
Annonce au démarrage : « Using the test skill — browser-testing {feature/flows}. »
Arguments
- Description de la fonctionnalité :
/test creating a purchase order - Issue GitHub :
/test #1234 - Rien : déduis les cibles à partir du diff de la branche
Étape 1 : Vérifier d'abord un playbook en cache
ls .ai/playbooks/
Si un playbook correspond à la fonctionnalité (p. ex. create-purchase-order.md), lis-le et utilise directement ses étapes, notes de sélecteur et valeurs de champ — passe à l'étape 4. Construis un plan de zéro uniquement quand aucun playbook ne convient.
Étape 2 : Décide quoi tester
- Fonctionnalité donnée → c'est la cible.
- Issue donnée →
gh issue view <number> --json title,body. - Rien donné → lis le diff :
git diff $(git merge-base origin/main HEAD) --stat
git log --oneline $(git merge-base origin/main HEAD)..HEAD
Signaux testables : routes nouvelles/modifiées sous routes/x+/ (pages orientées utilisateur), changements de service sous modules/ (logique métier), nouvelles migrations (schéma → formulaires). Sélectionne 1–3 flux utilisateur concrets (« créer un bon de commande », « mettre à jour les détails d'une tâche »).
Étape 3 : Écris le plan de test
Pour chaque flux, liste les étapes qu'un utilisateur effectue : naviguer → remplir → soumettre → vérifier (redirection, toast, enregistrement dans la liste). Affiche le plan avant d'exécuter pour que l'utilisateur voit ce que tu as l'intention de faire.
Étape 4 : Connexion
Invoque /auth. Si cela échoue, ARRÊTE et rapporte.
Étape 5 : Exécute chaque test
Lis ERP_URL de .env.local, puis par flux :
5a. Naviguer
agent-browser open ${ERP_URL}/<route> && agent-browser wait --load networkidle && agent-browser snapshot -i
5b. Interagir — Règles de formulaire Carbon (ce sont des éléments porteurs)
Champs de texte :
agent-browser fill @eN "value"
Champs Combobox/select (Carbon utilise des comboboxes personnalisés) :
agent-browser click @eN # ouvre la liste déroulante
agent-browser snapshot -i # trouve les références des options
agent-browser click @eM # sélectionne l'option — cela met à jour l'état React de manière fiable
Champs nombre / devise / date (react-aria) — remplis, puis BLUR. Ils affichent un champ de saisie formaté visible plus un champ d'entrée masqué qui porte la valeur du formulaire ; le champ masqué se valide au blur, pas par frappe :
agent-browser fill @eN "300" # champ visible
agent-browser click @eM # clique sur un autre champ pour blur → valide le champ masqué
agent-browser eval "document.querySelector('input[name=amount]').value" # vérifie => "300"
agent-browser type n'atteint souvent PAS ces champs — utilise toujours fill + blur.
Soumettre — requestSubmit, jamais un clic. Les formulaires Carbon sont @carbon/form ValidatedForm ; le gestionnaire de soumission s'exécute uniquement sur un événement submit natif porteur d'un submitter — un simple clic sur Enregistrer ne fait rien (silencieusement) :
agent-browser eval "(()=>{const b=[...document.querySelectorAll('button')].find(x=>x.type==='submit'&&x.textContent.trim()==='Save');const f=b.closest('form');f.requestSubmit(b);return 'submitted'})()"
agent-browser wait --load networkidle
agent-browser snapshot -i
Ajuste le texte du bouton (Save/Create/Submit) au formulaire. Pour les formulaires en tiroir (routes enfants en superposition), fais requestSubmit du propre formulaire du tiroir, pas celui de la page parent.
Une soumission qui « ne fait rien » est presque toujours : (a) tu as cliqué au lieu de faire
requestSubmit, ou (b) le champ masqué d'un champ react-aria ne s'est jamais validé (tu n'as pas blur). Vérifie les champs masqués avant de blâmer le formulaire.
5c. Vérifie le résultat
Succès : redirection vers une page de détail / changement d'URL, toast de succès, enregistrement visible dans la liste. Échec : erreurs de validation, toast d'erreur, « Something went wrong », pas de changement. En cas d'échec → invoque /error pour capturer les diagnostics, puis continue avec les tests restants.
5d. Enregistre
PASS (soumis, résultat attendu vérifié) · FAIL (erreur/inattendu) · SKIP (prérequis manquant — p. ex. pas de fournisseurs seed ; indique ce qui manque).
Étape 6 : Cache le playbook (après chaque PASS)
Écris/mets à jour .ai/playbooks/<feature-slug>.md (kebab-case). C'est ce qui rend les exécutions futures rapides — ne saute pas cette étape.
# <Feature Name>
Last tested: <YYYY-MM-DD>
Route: /x/<route>
## Prerequisites
- <required data, p. ex. "at least one supplier exists">
## Steps
### 1. Navigate — URL, expected form fields
### 2. Fill — Field "<label>" (<hint: "first combobox">): "<value>"
### 3. Submit — requestSubmit the form whose button reads "<label>" (NOT a click)
### 4. Verify — expected redirect/toast
## Selector Notes
- <how to find tricky fields: by label, position, role>
## Common Failures
- <validation errors hit on the way to PASS and their causes>
Règles : décris les sélecteurs par label/position/role, jamais par références en cache comme @e5 (elles changent par session) ; cache uniquement les PASS ; mets à jour les playbooks existants au lieu de dupliquer ; enregistre les observations sur les données préalables.
Étape 7 : Rapporte, puis nettoie
| # | Test | Status | Notes |
|---|---|---|---|
| 1 | Create purchase order | PASS | PO-000123 created, redirected to detail |
| 2 | Create job | FAIL | "Location is required" — capture: .ai/scratch/e2e/… |
Inclus les chemins de capture /error pour les échecs. Puis agent-browser close.
Gestion des échecs
- La page ne charge pas →
/error, passe à la suivante. - Champ introuvable → snapshot, note-le, passe à la suivante.
- Données préalables manquantes → SKIP avec explication.
- N'abandonne jamais toute l'exécution pour un seul échec — termine tous les tests prévus.