Tester une fonctionnalité Instance AI
Utilise cette skill pour tester une fonctionnalité UI de l'Assistant n8n de bout en bout. En code, l'Assistant s'appelle « Instance AI ». Tu rédiges les chemins de test, l'utilisateur les approuve, et la skill les exécute tous en même temps. Une page locale affiche chaque exécution en direct. À la fin, tu juges chaque exécution et tu génères un rapport unique.
La skill utilise les scripts de testmax-n8n-ai pour l'API n8n, l'utilisation de LangSmith, le serveur de page en direct et le modèle de rapport. Ne copie pas ces scripts.
Ce que fait la skill
flowchart LR
A[Feature + diff] --> B[Draft scenarios.json<br/>and scripts]
B --> T[--try and --estimate]
T --> C{User approves<br/>plan + cost}
C --> D[run.py]
D --> E[One fresh branch instance<br/>for each feature + regression]
D --> F[Fresh branch instance<br/>visual]
D --> G[Fresh master instance<br/>visual, or a kept run]
E & F & G --> H[Live page]
F & G --> I[Pixel + accessibility tree diff]
E & F & G --> J[summary.txt with triage]
J --> K[You judge: verdicts.json]
K --> L[Report rebuilds itself]
- Chaque exécution est un scénario : un script Playwright qui pilote la véritable UI de l'Assistant dans Chrome, avec un véritable LLM.
- Il y a trois sortes de scénario :
feature: teste un chemin de la nouvelle fonctionnalité.regression: teste un flux existant que la fonctionnalité touche.visual: capture des écrans. La skill l'exécute sur la branche et sur master, puis compare les captures.
- Toutes les instances sont neuves : un nouveau dossier de données, un nouveau propriétaire, et aucun workflow ni thread. Ainsi les écrans de la branche et de master sont identiques quand le code est identique. Une instance neuve démarre en environ 5 secondes, car elle copie un dossier de données déjà migré et semé.
- Chaque scénario feature et regression obtient sa propre instance neuve, ainsi les scénarios ne voient pas les données les uns des autres. Les scénarios visual partagent une instance de branche et une instance de master.
- Les feature flags sont identiques sur toutes les instances. Les instances ne demandent pas à PostHog les flags, donc aucune instance n'obtient de variante d'expérience aléatoire. Définis les flags que la fonctionnalité nécessite dans le plan, avec
featureFlags. run.pycompile d'abord ce checkout (avec turbo, une compilation à jour prend environ 1 seconde). L'instance master démarre depuis un git worktree à~/.cache/n8n-feature-test/master, à la base de fusion deHEADetorigin/master. Ainsi la vérification visual montre uniquement les changements de cette branche, pas ceux que master a reçus après le démarrage de la branche.- Une exécution master visual qui a réussi est gardée. La prochaine exécution avec la même base de fusion, le même script et les mêmes flags la réutilise, et ne démarre pas master.
Conditions préalables
Exécute python3 .agents/skills/testmax-assistant-ui/scripts/run.py --check avant la première exécution. Les commandes ci-dessous utilisent $S pour ce dossier de scripts. --check vérifie les outils, Chrome, les paramètres, l'espace disque et la compilation master, et affiche le dossier de plan pour cette branche.
- Google Chrome. Les scripts utilisent le paquet Playwright du repository.
- Les paramètres du modèle Instance AI dans
.env.localou.env.ai-surfaces, comme pour ta propre instance. Les instances neuves obtiennent les mêmes paramètres, sans la licence, la base de données, le port et la clé de chiffrement. LANGSMITH_API_KEYet le tracing LangSmith, pour les tokens et le coût. Sans eux, le rapport n'affiche ni tokens ni coût. Ne définis pasN8N_DIAGNOSTICS_ENABLED=false: alors Instance AI n'envoie pas de traces.- Environ 3 Go d'espace disque pour le worktree master et sa compilation.
Ne mets jamais une clé sur la ligne de commande. Lis les paramètres depuis les fichiers env et l'environnement seulement.
1. Comprendre la fonctionnalité
Trouve ce qui a changé et ce que l'utilisateur veut tester :
git diff origin/master...HEAD --stat -- packages/frontend packages/@n8n/instance-ai packages/cli/src/modules/instance-ai
git diff origin/master...HEAD -- <the files of the feature>
Lis les composants Vue modifiés, les stores et les événements backend qu'ils utilisent. Trouve les valeurs data-test-id des nouveaux éléments. Si l'utilisateur n'a pas décrit la fonctionnalité, demande une ou deux phrases à ce sujet.
Si le plan a des scénarios visual, démarre la ligne de base master maintenant, en arrière-plan. La première compilation prend 15 à 20 minutes. Les compilations ultérieures prennent moins d'une minute quand le cache turbo a les fichiers. Tu peux rédiger le plan pendant qu'il compile :
S=.agents/skills/testmax-assistant-ui/scripts
python3 $S/run.py --check # the setup, and the plan folder
python3 $S/baseline.py prepare # in the background
python3 $S/baseline.py status # later: is it ready?
2. Rédiger les chemins de test
Écris le dossier de plan que --check affiche : ~/.cache/n8n-feature-test/plans/<branch>/<feature>/. Le dossier reste après un redémarrage. Il contient scenarios.json et un script pour chaque scénario. Le nom du dossier est le nom des dossiers d'exécution.
Rédige ces chemins :
- Chemins feature. Le chemin principal, puis chacune de ses branches : autres choix de l'utilisateur, entrées vides et longues, annulation et arrêt, un rechargement au milieu, un second thread, erreurs que l'utilisateur peut voir. Un chemin pour chaque scénario.
- Chemins regression. Les flux existants près du changement : une réponse de chat simple, les approbations, l'examen du plan, le panneau d'aperçu, la liste des threads. La bibliothèque a
smoke-chatpour la simple réponse de chat. - Écrans visual. Les écrans que le changement peut affecter, sur la branche et sur master. Capture chaque écran dans un état stable. Masque tout texte provenant du LLM et toutes les données qui changent entre les exécutions. La bibliothèque a
visual-assistant-homeetvisual-thread-reply.
Garde chaque scénario court et concentré. Un scénario qui échoue doit montrer où il a échoué. Utilise plus de scénarios, pas de plus longs. Un scénario visual ne doit pas nécessiter la nouvelle fonctionnalité, car il s'exécute aussi sur master.
Certains états UI ne s'affichent qu'un instant, par exemple une ligne de statut qui se replie quand l'exécution se termine. Vérifie-les pendant que l'exécution est active, pas après.
Si la fonctionnalité est derrière un feature flag ou une expérience, définis le flag dans featureFlags. Sans lui, les instances utilisent la valeur par défaut de chaque flag.
Avant de montrer le plan à l'utilisateur, vérifie les nouveaux scripts et obtiens l'estimation (voir Vérifie les scripts). Puis montre à l'utilisateur la liste des chemins : l'id, le type, le titre et le résultat attendu de chacun. Montre aussi l'estimation : le nombre d'exécutions LLM, le coût et le temps. Change le plan jusqu'à ce que l'utilisateur l'approuve.
scenarios.json
{
"title": "Stop button in the Assistant",
"feature": "The user can stop a run while the Assistant works.",
"scenarios": [
{
"id": "stop-mid-run",
"title": "Stop a run in the middle",
"kind": "feature",
"script": "stop-mid-run.mjs",
"expected": "The stop button shows during the run. A click ends the run without an error message.",
"timeoutMin": 6
},
{
"id": "smoke-chat",
"title": "A prompt gets a reply",
"kind": "regression",
"script": "lib:smoke-chat.mjs",
"expected": "A short prompt gets the reply OK."
}
]
}
Le plan a ces champs : title (obligatoire), feature (la fonctionnalité, pour le juge) et featureFlags (optionnel). featureFlags mappe un nom de flag à true, false ou un nom de variante, par exemple { "<flag name>": "variant" }. Chaque instance obtient ces flags, sur la branche et sur master. Ils vont dans N8N_FEATURE_FLAG_OVERRIDES et remplacent les valeurs dans tes fichiers env.
Chaque scénario a ces champs :
| Champ | Obligatoire | Description |
|---|---|---|
id |
oui | Lettres minuscules, chiffres et -. Pas de --. Unique. |
title |
oui | Un court nom pour le rapport. |
kind |
oui | feature, regression ou visual. |
script |
oui | Un chemin relatif à scenarios.json, ou lib:<file> pour un script de bibliothèque. |
expected |
oui | Le résultat attendu, pour le juge. |
autoConfirm |
non | Réponds automatiquement aux confirmations de l'Assistant. Par défaut true. Les cartes de credential et setup obtiennent « Later ». Définis false quand le script répond à une confirmation ou une carte. |
isolated |
non | Exécute le scénario sur sa propre instance neuve. Par défaut true. Les instances démarrent avant leur scénario, donc cela coûte presque zéro temps. Définis false pour exécuter le scénario sur une instance de branche qu'il partage avec les autres scénarios false. Les scénarios visual ignorent ce champ. |
timeoutMin |
non | La limite de temps du scénario. Par défaut 10. |
viewport |
non | La taille de la fenêtre, par exemple 1600x1000 (par défaut). |
examples/stop-button/ est un exemple complet de plan.
Scripts de scénario
Un script exporte une fonction async. Il obtient le kit t :
export default async function (t) {
await t.step('Open the Assistant', () => t.goto());
await t.step('Send a prompt', () => t.send('Reply with exactly the word OK and nothing else.'));
await t.step('Wait until the run ends', () => t.waitForIdle({ timeout: 180_000 }));
await t.check('The reply says OK', () => t.expect(t.ai.assistantMessages().last()).toContainText('OK'));
await t.capture('thread', { mask: [t.ai.assistantMessages()] });
}
| Membre du kit | Ce qu'il fait |
|---|---|
t.step(title, fn) |
Exécute une action. Enregistre une capture et le temps. Un échec arrête le scénario. |
t.check(title, fn) |
Exécute une assertion, avec une capture. Un échec est enregistré, le scénario continue. |
t.note(title) |
Ajoute une étape avec une capture et aucune action. |
t.goto(path = '/assistant') |
Ouvre une page et attend l'entrée de chat. Ferme la boîte de dialogue « Try new n8n Assistant ». |
t.send(text) |
Tape dans l'entrée de chat, envoie et attend l'URL du thread. Enregistre le thread pour la trace. |
t.waitForIdle({timeout, settle}) |
Attend que le thread n'ait pas d'exécution active. Retourne 'idle', ou 'suspended' quand l'exécution attend l'utilisateur. |
t.capture(name, {mask, maskText, root, fullPage}) |
Enregistre un écran et son arborescence d'accessibilité pour la vérification visual. mask prend des locators, maskText prend des textes : tous les éléments avec ce texte. Les zones masquées s'affichent d'une couleur. Dans l'arborescence, le sous-arbre d'un élément mask et chaque texte masqué s'affichent comme [masked]. |
t.api(method, path, body) |
Appelle l'API REST n8n comme l'utilisateur connecté. Retourne data. |
t.threadStatus(threadId) |
Retourne {active, suspended, background} : le thread a une exécution, l'exécution attend l'utilisateur et le nombre de tâches de fond en cours d'exécution. |
t.ai.* |
Locators pour l'Assistant : chatInput, sendButton, stopButton, userMessages, assistantMessages, assistantText(text), panelText(text), sidebarToggle, threadList, newThreadButton, approveButton, denyButton, planApproveButton, previewPanel, previewCanvasNodes, et plus dans scripts/kit.mjs. |
t.expect, t.page, t.baseUrl, t.sleep(ms) |
expect Playwright, la page, l'URL de l'instance et une pause. |
Règles pour les scripts :
- Utilise les locators
data-test-id(page.getByTestId). Utilise le texte seulement pour le texte que le test contrôle. - Utilise
t.expect(...).toBeVisible()et d'autres assertions qui attendent. N'utilise past.sleeppour attendre l'UI. - Les réponses LLM changent d'une exécution à l'autre. Vérifie l'état UI, pas les mots exacts. Demande une réponse fixe, telle que « Reply with exactly the word OK », quand tu as besoin d'une réponse connue.
- Pour tester une approbation, définis
"autoConfirm": false, attends avect.waitForIdle()qu'il retourne'suspended', puis clique sur le bouton.
Vérifier les scripts
Vérifie les nouveaux scripts sur des instances neuves avant de montrer le plan :
python3 $S/run.py --plan <plan>/scenarios.json --try --only id1,id2
--try n'exécute pas master, ne démarre pas la page en direct, et ne génère pas de rapport. Il affiche un tableau avec le statut, les étapes et la première erreur de chaque exécution. Les exécutions sont dans /tmp/feature-test/try/. Corrige les scripts qui échouent à cause du script, puis essaie-les à nouveau. Chaque scénario LLM dans un essai coûte aussi de l'argent, donc essaie seulement les nouveaux scripts.
Puis obtiens l'estimation pour l'exécution complète :
python3 $S/run.py --plan <plan>/scenarios.json --estimate
L'estimation utilise le coût et le temps des exécutions antérieures des mêmes ids de scénario. Un scénario sans exécution antérieure affiche * et la médiane de toutes les exécutions antérieures. Une exécution master gardée affiche (kept) et ne coûte rien.
3. Exécuter
Ouvre la page en direct pour l'utilisateur à chaque exécution. Ne saute pas cette étape. Fais ceci :
-
Découvre si ton harnais a un outil de navigateur qui ouvre un onglet dans l'éditeur. Dans Cursor, c'est
browser_navigatedu serveur MCPcursor-ide-browser. -
S'il en a un, démarre l'exécution en arrière-plan avec
--no-open:python3 $S/run.py --plan <plan>/scenarios.json --no-openAttends la ligne
live page: <url>dans la sortie, qui arrive en quelques secondes. Puis ouvre cette URL dans un nouvel onglet de l'éditeur que l'utilisateur peut voir (dans Cursor :newTab: trueetposition: "active"). Si l'onglet ne s'ouvre pas, exécuteopen <url>(macOS) ouxdg-open <url>(Linux). -
S'il n'en a pas, démarre l'exécution en arrière-plan sans
--no-open.run.pyouvre la page en direct dans le navigateur par défaut :python3 $S/run.py --plan <plan>/scenarios.json -
Donne l'URL à l'utilisateur dans les deux cas.
run.py ne lit pas $BROWSER, ne le définis donc pas.
run.py fait ces étapes :
- Affiche l'estimation et vérifie la configuration. Il s'arrête quand la configuration a une erreur, par exemple un Chrome manquant.
- Copie le plan dans un nouveau dossier d'exécution,
/tmp/feature-test/<time>-<plan folder name>/. - Démarre la page en direct et affiche son URL. Sans
--no-open, il ouvre la page dans le navigateur par défaut. - Compile ce checkout avec turbo. Un erreur de compilation arrête l'exécution, la sortie affiche la fin de
build.log. - Démarre les instances : une pour chaque scénario feature et regression, une instance de branche pour les scénarios visual, et une instance master. Il démarre les instances avant leur scénario. Master ne démarre que pour les exécutions master qu'il ne garde pas d'une exécution antérieure. Il prépare d'abord la compilation master, si elle n'est pas prête.
- Exécute les scénarios, 4 à la fois. Chaque exécution enregistre une vidéo, les étapes avec captures, les erreurs du navigateur, les événements du thread, et les ids LangSmith.
- Arrête les instances, compare les captures visual, écrit
summary.txtet génère le rapport sans verdicts. - Affiche un tableau de résultats, la commande pour exécuter à nouveau le plan, et un objet JSON sur plusieurs lignes :
runDir,report,live,summary,results(le statut de chaque exécution),visual(le nombre de captures, les changées, et l'erreur quand la comparaison visual a échoué),rerunetnext. - Obtient les tokens et le coût de LangSmith, puis régénère le rapport. LangSmith reçoit les traces avec un délai, donc cela peut prendre jusqu'à 90 secondes. Tu peux commencer à juger pendant qu'il s'exécute.
| Option | Utilisation |
|---|---|
--check |
Vérifie la configuration et affiche le dossier de plan, puis arrête. |
--estimate |
Affiche le coût et le temps attendus, puis arrête. |
--try |
Vérifie les nouveaux scripts : pas de master, pas de page en direct, pas de rapport. Voir Vérifier les scripts. |
--only id1,id2 |
Exécute seulement ces scénarios. |
--rerun last |
Exécute à nouveau le plan de la dernière exécution, dans un nouveau dossier. |
--rerun <run-dir> |
Exécute à nouveau le plan de cette exécution antérieure, dans un nouveau dossier. Si le dossier de plan de cette exécution existe toujours, il utilise le plan et les scripts actuels de là. Ainsi un script corrigé s'exécute dans sa version corrigée. |
--concurrency N |
Exécute en même temps. Par défaut 4, ou FEATURE_TEST_CONCURRENCY. |
--branch-url URL |
Utilise une instance en cours d'exécution pour toutes les exécutions de branche, pas des instances neuves. Les threads restent sur cette instance. Les scénarios partagent ses données et la vérification visual n'est pas fiable. |
--no-build |
Ne compile pas ce checkout. La sortie avertit quand les fichiers sources sont plus récents que leur compilation. |
--no-baseline |
N'exécute pas master. Il n'y a pas de vérification visual. |
--no-cache |
Exécute master à nouveau, aussi quand une exécution master gardée existe. |
--no-fetch |
N'extrait pas origin/master. Utilise la base de fusion avec le origin/master que git a. |
--no-open |
N'ouvre pas la page en direct dans le navigateur par défaut. Utilise ceci quand tu l'ouvres dans l'onglet de l'éditeur. |
--no-live |
Ne démarre pas la page en direct. Utilise ceci seulement quand l'utilisateur le demande. |
--keep-instances |
Garde les instances neuves en cours d'exécution après l'exécution, pour une vérification manuelle. La sortie donne instancePorts. Chaque instance est à http://localhost:<port>. Arrête-les toi-même. |
--run-dir DIR |
Utilise ce dossier d'exécution. |
| Paramètre | Par défaut |
|---|---|
FEATURE_TEST_RUNS_DIR |
/tmp/feature-test |
FEATURE_TEST_CACHE |
~/.cache/n8n-feature-test : les plans, le worktree master, les exécutions master gardées, et les modèles de données d'instance |
FEATURE_TEST_CONCURRENCY |
4 |
Regarder les exécutions
La page en direct montre chaque exécution comme une ligne, avec son statut et sa dernière capture. La capture change tous les quelques secondes. Quand l'exécution est terminée, la même adresse affiche le rapport. La page s'arrête 30 minutes après sa dernière requête.
4. Juger
Lis <runDir>/summary.txt. Il commence par un triage : chaque exécution est clean, ou look closer avec les raisons (une étape ou vérification échouée, une erreur de navigateur, un appel d'outil échoué, ou un changement visual). Puis il a, pour chaque exécution : le résultat attendu, les étapes et leur statut avec le chemin de chaque capture, les prompts, la dernière réponse, les erreurs du navigateur et les résultats visual.
Pour une exécution clean, regarde la dernière capture et vérifie-la par rapport au résultat attendu. Pour une exécution à examiner de plus près, regarde les captures des étapes dans les raisons, et les images diff dans visual/<id>/.
Un script qui a réussi n'est pas toujours une réussite. Vérifie que l'exécution affiche le résultat attendu, pas seulement que le script s'est terminé. Vérifie ces problèmes :
- une erreur de navigateur ou une requête échouée qui provient de la fonctionnalité
- une étape qui a réussi, mais la capture affiche le mauvais état
- un changement visual que la fonctionnalité ne prévoyait pas
- un script qui a échoué à cause du script, pas de la fonctionnalité. Dis-le, et corrige le script
Écris <runDir>/verdicts.json. Utilise les noms d'exécution comme clés. Une exécution master porte le nom <id>--master et n'a pas besoin de verdict :
{
"stop-mid-run": {
"verdict": "pass",
"summary": "One sentence about the result.",
"checks": [
{ "ok": true, "text": "The stop button ends the run" },
{ "ok": false, "text": "The send button comes back after 9 seconds" },
{ "ok": null, "text": "Could not confirm the stopped label" }
]
}
}
Utilise pass quand tous les contrôles réussissent, fail quand l'objectif principal n'est pas atteint, et partial pour tous les autres résultats. Pour un scénario visual, juge chaque changement : prévu par la fonctionnalité, une régression ou du bruit (par exemple, un timestamp non masqué). Sois strict et factuel.
Écris chaque vérification comme une courte phrase avec un seul fait. Mets les sélecteurs, les test IDs et le texte UI entre backticks. La page les affiche en tant que code. La page affiche d'abord les vérifications échouées.
5. Le rapport
Pendant que la page en direct s'exécute, elle régénère le rapport chaque fois que verdicts.json change. L'onglet ouvert charge le nouveau rapport automatiquement et garde l'exécution sélectionnée et la position de défilement. Ainsi tu n'as pas besoin de charger la page à nouveau. Quand la page en direct ne s'exécute pas, génère le rapport toi-même :
python3 $S/report.py <runDir>
Le rapport est <runDir>/report/index.html. C'est un dossier avec tous ses fichiers, tu peux donc le copier ou le partager. Il contient, de haut en bas :
- les relecture vidéo de toutes les exécutions
- un tableau de toutes les exécutions : type, résultat, étapes réussies, temps, appels LLM, appels d'outil, tokens, coût et erreurs du navigateur. Le résultat est ton verdict. Une exécution master affiche
Baseline, une exécution sans verdict afficheNot judgedouFailedquand son script a échoué - la vérification visual : l'écran de branche, l'écran de master, et l'image diff de chaque capture, avec le diff d'arborescence d'accessibilité
- pour chaque exécution : la vidéo de l'exécution, le résultat attendu et le verdict, les étapes avec leurs captures, les erreurs du navigateur et les étapes LLM de la trace. Un clic sur une ligne du tableau affiche le panneau et la vidéo de cette exécution, et met en pause la vidéo des autres exécutions
Chaque boîte s'affiche fermée. Elle s'ouvre et se ferme quand tu cliques sur son titre. Sur la page en direct, une boîte que tu ouvres ou fermes reste comme ça quand la page se met à jour.
Une exécution master gardée d'une exécution antérieure affiche « Reused » dans sa boîte d'exécution.
Donne à l'utilisateur le chemin et l'URL live. Dis à l'utilisateur le résultat de chaque scénario, les changements visual, et les problèmes que tu as trouvés.
Étapes isolées
| Étape | Commande |
|---|---|
| Préparer la ligne de base master | python3 $S/baseline.py prepare |
| Afficher l'état de la ligne de base | python3 $S/baseline.py status |
| Exécuter un script sur n'importe quelle instance | N8N_BASE_URL=<url> node $S/run_scenario.mjs --out <dir> --script <file> |
| Comparer les captures visual à nouveau | node $S/visual_diff.mjs <runDir> |
| Afficher le résumé du juge | python3 $S/report.py <runDir> --summary |
| Servir la page en direct d'une exécution | python3 $S/live_page.py --run-dir <runDir> |
Limites
- Les réponses LLM changent d'une exécution à l'autre, une exécution peut échouer à cause du LLM. Exécute à nouveau un scénario échoué avec
--rerun last --only <id>avant de l'appeler un bug. - Un appel LLM arrêté ne s'affiche pas dans LangSmith. Le coût d'une exécution avec un arrêt inclut seulement les appels qui se sont terminés.
- Chaque scénario LLM coûte de l'argent. Les tokens et le coût s'affichent dans le rapport.
- Les exécutions parallèles partagent le CPU et les limites de débit du LLM. Les temps incluent la charge partagée.
- La vérification visual compare les pixels avec une petite tolérance. Les polices et les animations peuvent causer de petits changements. Les masques masquent les parties qui changent entre les exécutions. Le diff d'arborescence d'accessibilité montre les changements de structure et de texte.
- Le worktree s'affiche dans
git worktree list. Pour le supprimer, exécutegit worktree remove ~/.cache/n8n-feature-test/master. - Les exécutions master gardées de plus de 14 jours sont supprimées. Les modèles de données d'instance dans
~/.cache/n8n-feature-test/templateschangent quand les migrations changent. Tu peux supprimer les vieux modèles à tout moment. - Un dossier d'exécution fait environ 35 Mo. Les dossiers d'exécution sont dans
/tmp, le système les supprime donc au redémarrage.