PrestaFlow : consommer le results.json
Le fichier oublié de la boucle CI
L’article 2 mentionne que l’Action officielle lance composer run prestaflow:json:file puis pousse le résultat à la plateforme. Une prochaine annexe, consacrée à l’historique cloud, montrera qu’on peut retélécharger ce même fichier depuis l’app.
Mais on n’a jamais dit ce qu’il y a dedans, ni ce qu’on peut faire avec en dehors de le regarder.
Ce fichier results.json est la sortie machine de PrestaFlow. Il contient ce qu’un humain voit dans la sortie console, dans un format que n’importe quel script sait consommer. Cette annexe montre son schéma et quatre usages pratiques pour brancher PrestaFlow au reste de votre outillage.
Comment le générer
Une seule commande :
composer prestaflow -- run --output=JSON --file ./tests/prestaflow
--output=JSON(ou-o json) — remplace le rapport CLI par du JSON structuré.--file— écrit sur le disque plutôt que surstdout. Le fichier atterrit toujours au même endroit :prestaflow/results.json, à la racine du projet (le dossier depuis lequel vous lancez la commande).
C’est très exactement ce que fait le script Composer prestaflow:json:file, celui que définit le module de démonstration psflowdemo et que l’Action officielle appelle :
"scripts": {
"prestaflow": "./vendor-dev/prestaflow/php-library/bin/prestaflow",
"prestaflow:json:file": "@prestaflow run --output=JSON ./tests --file"
}
Rien ne vous empêche de l’appeler à la main pour explorer. Sans --file, le JSON part sur stdout, mêlé à l’indicateur de progression : pas exploitable tel quel par jq.
Le schéma
Voici un extrait d’un vrai results.json produit par PrestaFlow v1.7.1 (champ steps retiré, noms adaptés au module fil rouge) :
{
"suite": "Tests\\Suites\\UpdateTitle",
"title": "Modification du titre depuis le BO",
"stats": {
"passes": 1,
"failures": 1,
"skips": 0,
"skippeds": 1,
"todos": 0,
"assertions": 2,
"time": 4213
},
"tests": [
{
"title": "enregistre le nouveau titre",
"datasets": [],
"dataset": 1,
"warning": "",
"state": "pass",
"expect": {
"pass": ["expected 'Settings updated' to contain 'updated'"]
},
"debug": [],
"time": 2104,
"visual": []
},
{
"title": "affiche le nouveau titre sur la home",
"datasets": [],
"dataset": 1,
"state": "fail",
"warning": "",
"screen": "error_16E9C8B963AB07ADED3089EF55D8FD13-1790260655.png",
"attachments": [],
"expect": {
"pass": [],
"fail": ["expected 'Bienvenue sur notre boutique' to contain 'Titre mis à jour'"]
},
"debug": [],
"time": 1108,
"visual": []
},
{
"title": "conserve le titre après vidage du cache",
"datasets": [],
"dataset": 1,
"state": "skipped",
"expect": [],
"debug": [],
"time": 0,
"visual": []
}
],
"warnings": ["", ""],
"screens": ["error_16E9C8B963AB07ADED3089EF55D8FD13-1790260655.png"]
}
Les champs qui comptent en pratique :
stats.failures— zéro = suite verte. C’est le seul champ à regarder pour un check automatique de santé.tests[].state—pass/fail/skip/skipped/todo. Ce sont les cinq états possibles vus dans l’annexe debug. Après un échec, lesitsuivants de la suite passent enskipped(comportement par défaut, désactivable avec->skipWhenFailed(false)).tests[].expect— un objet par état :passliste les assertions réussies,failcontient le message de l’échec, du style “expected ‘Bienvenue sur notre boutique’ to contain ‘Titre mis à jour’”. La langue du message suit la locale de la suite. Quand aucune assertion n’a tourné (skipped,todo), c’est un tableau vide[].tests[].screen— nom de fichier de la capture d’erreur, à retrouver dansprestaflow/screens/errors/<nom>. Présent seulement pour unitenfail.tests[].time— durée en millisecondes.tests[].dataset/tests[].datasets— le numéro du jeu de données (à partir de 1) et ses valeurs, quand leitétait exécuté via->with([...])(voir l’annexe scénarios paramétrés). Sans->with(),datasetvaut1etdatasetsest vide.tests[].visual— résultats de régression visuelle pour ceit, sivisualCheckpointa été appelé ; tableau vide sinon.
Le reste (warning, debug, attachments, warnings, screens) est du bonus utile en debug mais rarement consommé par un script. steps est la closure du it, sérialisée en objet vide : ignorez-la.
Cas d’usage 1 : notification Slack riche
Le mail “Job PrestaFlow failed” de GitHub Actions ne dit rien du pourquoi. Un message Slack riche, généré depuis le results.json, dit précisément quelle suite a cassé et sur quelle assertion.
Step à ajouter après Run PrestaFlow dans le workflow (GitHub Actions, adaptable ailleurs) :
- name: Notify Slack on failure
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
run: |
RESULTS=prestaflow/results.json
FAILED=$(jq -r '[(.suites // [.])[] | select(.stats.failures > 0)] | length' "$RESULTS")
SUMMARY=$(jq -r '[(.suites // [.])[] |
select(.stats.failures > 0) |
.title as $suite |
.tests[] |
select(.state == "fail") |
"• *\($suite)* — \(.title)\n ↳ `\(.expect.fail[0] // "pas de message")`"
] | join("\n")' "$RESULTS")
PAYLOAD=$(jq -n --arg failed "$FAILED" --arg summary "$SUMMARY" \
'{text: "❌ *\($failed) suite(s) PrestaFlow en échec*\n\n\($summary)"}')
curl -X POST -H 'Content-Type: application/json' \
-d "$PAYLOAD" "$SLACK_WEBHOOK_URL"
jq -n --arg construit le payload : les guillemets, apostrophes et retours à la ligne des messages d’assertion sont échappés correctement, ce qu’une chaîne JSON assemblée à la main en shell ne garantit pas.
Rendu Slack :
❌ 1 suite(s) PrestaFlow en échec
• Modification du titre depuis le BO — affiche le nouveau titre sur la home
↳ expected 'Bienvenue sur notre boutique' to contain 'Titre mis à jour'
Bien plus utile que le lien vers l’onglet Actions. On sait quoi débugger avant même d’ouvrir le job.
Cas d’usage 2 : badge README dynamique
Shields.io sait générer un badge à partir d’un endpoint JSON qu’on héberge soi-même — un gist, un fichier statique sur S3, un endpoint de projet. On peut publier après chaque run CI un mini-JSON avec le résumé.
Extraction du résumé :
jq '{
schemaVersion: 1,
label: "prestaflow",
message: "\([(.suites // [.])[].stats.passes] | add) passing / \([(.suites // [.])[].stats.failures] | add) failing",
color: (if ([(.suites // [.])[].stats.failures] | add) == 0 then "brightgreen" else "red" end)
}' prestaflow/results.json > badge.json
Publiez badge.json où vous voulez (un gist GitHub public suffit), puis dans votre README :

Le badge se met à jour à chaque run vert / rouge, dès que votre CI republie badge.json. Utile pour un module open-source qui veut signaler visuellement sa couverture E2E.
Cas d’usage 3 : dashboard multi-projets
Quand vous maintenez 10 modules ou plus, un tableau agrégé qui montre l’état de chacun vaut mieux que des workflows GitHub Actions à surveiller un par un.
Pattern :
- Chaque projet CI, après son run,
POSTsonresults.json(ou juste lestatsextrait) vers un endpoint interne - Un backend léger (Node/PHP/Python, une table SQL) stocke l’entrée avec
project_id + timestamp + stats - Un frontend HTML minimal affiche la table avec un statut vert / rouge par projet, dernière date de run, taux de succès sur 7 jours
C’est ce que la plateforme prestaflow.io fait déjà pour vous, mais un dashboard maison a l’avantage de la personnalisation (filtres, groupements, KPI custom, intégration avec vos autres outils internes).
Cas d’usage 4 : auto-création d’issue GitHub
Sur la branche main, une régression PrestaFlow devrait automatiquement créer une issue GitHub avec le contexte du bug. Step CI :
- name: Create issue on regression
if: failure() && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: |
RESULTS=prestaflow/results.json
TITLE=$(jq -r '[(.suites // [.])[] |
select(.stats.failures > 0) |
"PrestaFlow : \(.title)"
] | .[0]' "$RESULTS")
BODY=$(jq -r '[(.suites // [.])[] |
select(.stats.failures > 0) |
.title as $suite |
.tests[] | select(.state == "fail") |
"### \($suite) — \(.title)\n\n**Assertion :** `\(.expect.fail[0] // "n/a")`\n"
] | join("\n---\n")' "$RESULTS")
gh issue create --title "$TITLE" --body "$BODY" --label "regression,e2e"
À réserver au CI de main (via le github.ref check) — sinon toutes vos feature branches génèrent des issues à chaque it qui n’a pas fini d’être stabilisé. Le job doit avoir la permission issues: write, et les labels regression et e2e doivent exister dans le dépôt, sinon gh issue create échoue.
Notes
Dans la Série PrestaFlow — article 15 sur 23