Retour au blog
TestsGitHub

Automatiser ses scénarios PrestaFlow en CI

PrestaEdit •
Automatiser ses scénarios PrestaFlow en CI

Préambule

Dans l’article précédent, nous avons mis en place trois suites PrestaFlow pour le module psflowdemo — un parcours d’achat fourni, plus deux suites custom — et nous les avons lancées en local avec composer prestaflow -- run ./tests/prestaflow. La librairie y est installée dans vendor-dev/, avec un script Composer prestaflow : voir la section Installation de l’article précédent.

Fonctionnel, mais rapidement fastidieux : à chaque commit, sur chaque version de PrestaShop qu’on prétend supporter, il faudrait relancer ces scénarios à la main. C’est très exactement le même argumentaire que celui du précédent article sur PHPStan en CI, transposé au fonctionnel.

Voyons comment déléguer cette exécution à GitHub Actions — d’abord en pilotant la CLI PrestaFlow depuis un workflow (sans dépendance externe), puis en passant à l’Action officielle pour bénéficier des remontées sur la plateforme.

Le défi propre au E2E en CI

Faire tourner PHPStan en CI est facile : composer install, puis on lance l’analyse statique. Rien ne s’exécute vraiment.

Pour du E2E, il faut une boutique PrestaShop réellement démarrée, contre laquelle PrestaFlow va naviguer. C’est là que se joue tout le confort du CI.

La solution standard, à l’heure de cet article, est Flashlight : une image Docker officielle de la communauté PrestaShop qui livre une instance PS installée, prête à répondre en HTTP, sans persistance. Idéale pour les tests : on démarre, on teste, on jette.

Toute la suite de l’article repose dessus.

Niveau 1 : la CLI PrestaFlow dans un workflow

Objectif : lancer nos scénarios à chaque push, sans compte prestaflow.io, sans token. Juste le workflow, Docker, et la CLI.

Créez .github/workflows/prestaflow.yml :

name: PrestaFlow

on:
  push:
  pull_request:

jobs:
  e2e:
    name: E2E — PrestaShop 8.1.7
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP 8.2
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: gd
          tools: composer:v2
          ini-values: variables_order=EGPCS

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Start MariaDB
        run: |
          docker network create prestashop
          docker run -d --name mysql --network prestashop \
            -e MARIADB_ROOT_PASSWORD=prestashop \
            -e MARIADB_DATABASE=prestashop \
            -e MARIADB_USER=prestashop \
            -e MARIADB_PASSWORD=prestashop \
            --health-cmd "healthcheck.sh --connect" --health-interval 5s \
            mariadb:11
          for i in $(seq 1 30); do
            [ "$(docker inspect -f '{{.State.Health.Status}}' mysql)" = "healthy" ] && exit 0
            sleep 2
          done
          echo "MariaDB never became healthy" && exit 1

      - name: Start PrestaShop (Flashlight)
        run: |
          docker run -d --name ps --network prestashop \
            -p 80:80 \
            -e PS_DOMAIN=localhost \
            -e MYSQL_HOST=mysql \
            -v "$PWD":/var/www/html/modules/psflowdemo \
            -v "$PWD/tests/flashlight-init":/tmp/init-scripts:ro \
            prestashop/prestashop-flashlight:8.1.7

      - name: Wait for PrestaShop to be ready
        run: |
          for i in $(seq 1 60); do
            code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost/admin-dev/ || true)
            if [ "$code" = "302" ] || [ "$code" = "200" ]; then
              echo "Le back office répond $code"
              exit 0
            fi
            sleep 2
          done
          docker logs ps
          echo "Timeout" && exit 1

      - name: Run PrestaFlow scenarios
        env:
          PRESTAFLOW_PS_VERSION: 8.1.7
          PRESTAFLOW_LOCALE: en
          PRESTAFLOW_FO_URL: http://localhost/
          PRESTAFLOW_BO_URL: http://localhost/admin-dev/
          PRESTAFLOW_BO_EMAIL: admin@prestashop.com
          PRESTAFLOW_BO_PASSWD: prestashop
          PRESTAFLOW_HEADLESS: 'true'
        run: composer prestaflow -- run ./tests/prestaflow

      - name: Upload PrestaFlow screenshots on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: prestaflow-output
          path: prestaflow/
          if-no-files-found: ignore

Ce workflow fait, dans l’ordre :

  1. Cloner le module (actions/checkout).
  2. Installer PHP et Composer, puis les dépendances du module (shivammathur/setup-php, composer install, qui les range dans vendor-dev/). Le réglage variables_order=EGPCS n’est pas décoratif : la librairie v1.7.1 lit sa configuration dans $_ENV, que le PHP installé par setup-php ne remplit pas par défaut. Sans lui, les variables du bloc env: n’atteignent pas PrestaFlow, qui retombe sur ses valeurs par défaut (https://localhost/, identifiants de la boutique de démo).
  3. Démarrer une base MariaDB. Flashlight embarque nginx et php-fpm, mais pas MySQL : sans base à côté, le conteneur boucle indéfiniment sur « Cannot connect to MySQL, retrying » et la boutique ne répond jamais. Attendre le healthcheck de la base fait échouer le job tout de suite, avec un message clair, si elle ne démarre pas.
  4. Démarrer Flashlight sur le même réseau Docker, avec l’image PS 8.1.7, en y montant le module (le dépôt cloné) dans modules/psflowdemo. PS_DOMAIN est obligatoire (sans lui, le conteneur s’arrête aussitôt avec le code 2 : « Missing PS_DOMAIN ») et doit correspondre à l’URL vue depuis le runner, port compris : ici localhost, puisque -p 80:80 expose le port 80.
  5. Installer le module : monter le dossier ne suffit pas, PrestaShop doit encore l’installer. C’est le rôle de l’init-script monté dans /tmp/init-scripts, décrit juste après.
  6. Attendre que PrestaShop réponde en interrogeant /admin-dev/ jusqu’à obtenir un 302 (redirection vers la page de connexion). Un 200 sur / ne suffit pas : il prouve seulement que quelque chose écoute sur le port, et le front répond avant la fin des post-scripts éventuels.
  7. Lancer PrestaFlow via le script Composer, avec les variables d’environnement pointant vers http://localhost/. La locale est en : l’image Flashlight n’installe que l’anglais. Le ./ devant tests/prestaflow est indispensable sous Linux : la CLI v1.7.1 met en majuscule la première lettre du chemin, et Tests/prestaflow n’existe pas.
  8. Uploader le dossier prestaflow/ en artifact si le job échoue. Il n’y a pas de rapport HTML : on y trouve les captures pleine page prises au moment où une assertion a échoué (prestaflow/screens/errors/), à inspecter depuis l’interface GitHub sans avoir à rejouer. Ajoutez --junit à la commande si vous voulez aussi un prestaflow/junit.xml.

L’init-script, à versionner dans le dépôt du module sous tests/flashlight-init/10-install-module.sh (et à rendre exécutable avec chmod +x, sinon Flashlight le saute) :

#!/bin/sh
set -eu
php /var/www/html/bin/console prestashop:module install psflowdemo

Flashlight exécute les init-scripts au premier démarrage, une fois la base restaurée et avant de servir la boutique : quand le back office répond, le module est installé.

Pour aller plus loin sur l’image elle-même (choix du tag, post-scripts, matrice de versions en docker compose, pièges), voir l’annexe PrestaShop Flashlight : des boutiques jetables pour tester.

C’est déjà utile en l’état : chaque push est testé, les échecs d’assertion sont visibles, les captures sont téléchargeables. Le tout sans avoir créé un seul compte externe.

Niveau 2 : l’Action officielle PrestaFlow/github-action

Le niveau 1 marche, mais il laisse plusieurs choses à désirer :

  • Aucune synthèse dans la PR. Il faut aller lire les logs pour savoir ce qui a échoué.
  • Pas d’historique. Chaque run repart de zéro, sans comparaison possible avec les précédents.
  • Pas de suivi des régressions visuelles. Les références d’images ne sont pas partagées d’un run à l’autre : on ne détecte pas qu’un template s’est cassé si aucun sélecteur ne bouge.
  • Le workflow YAML gonfle dès qu’on ajoute des versions de PS.

L’Action officielle PrestaFlow résout tout cela, en s’appuyant sur la plateforme prestaflow.io.

Prérequis

Trois choses à faire, une seule fois :

  1. Créer un compte via la waitlist — dès validation, la plateforme vous fournit un projectId de la forme pk_01ABCDEF... pour chaque projet créé.
  2. Générer un token d’API depuis les réglages du projet.
  3. Ajouter le token en secret GitHub du dépôt, sous le nom PRESTAFLOW_TOKEN (Settings → Secrets and variables → Actions).

Déclarer le script Composer

L’Action attend un script Composer nommé prestaflow:json:file, qui lance PrestaFlow avec le format de sortie qu’elle sait consommer (prestaflow/results.json). Ajoutez-le à côté du script prestaflow déclaré dans l’article précédent :

"scripts": {
    "prestaflow": "./vendor-dev/prestaflow/php-library/bin/prestaflow",
    "prestaflow:json:file": "@prestaflow run --output=JSON ./tests --file"
}

Le workflow

Créez (ou remplacez) .github/workflows/prestaflow.yml :

name: PrestaFlow

on:
  push:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  e2e:
    name: E2E — PrestaShop 8.1.7
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP 8.2
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: gd
          tools: composer:v2
          ini-values: variables_order=EGPCS

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run PrestaFlow
        id: prestaflow
        uses: PrestaFlow/github-action@v2
        env:
          PRESTAFLOW_LOCALE: en
          PRESTAFLOW_BO_EMAIL: admin@prestashop.com
          PRESTAFLOW_BO_PASSWD: prestashop
        with:
          token: ${{ secrets.PRESTAFLOW_TOKEN }}
          projectId: 'pk_01ABCDEFGHIJKLMNOP'
          flashlight: true
          ps-version: '8.1.7'
          flashlight-init-scripts: tests/flashlight-init

Nettement plus court que le niveau 1. Ce que l’Action fait à votre place :

  • Démarre Flashlight et sa base MariaDB avec la version demandée via l’input ps-version, et monte le dépôt dans modules/ : le type prestashop-module et le name prestaflow/psflowdemo de votre composer.json donnent modules/psflowdemo. Plus de docker run manuel, plus de boucle curl. Le module est monté, pas installé : l’input flashlight-init-scripts réutilise l’init-script du niveau 1 pour l’installer.
  • Transmet l’URL de la boutique et la version : l’Action ne fournit que PRESTAFLOW_FO_URL et PRESTAFLOW_PS_VERSION (elle les écrit aussi dans un .env.local). L’URL du BO s’en déduit (/admin-dev/ est le dossier par défaut de la lib, et celui de Flashlight). Les identifiants BO et la locale, eux, ne sont pas injectés : c’est le rôle du bloc env: de l’étape, qui n’atteint la librairie qu’avec variables_order=EGPCS côté setup-php, comme au niveau 1.
  • Lance le script Composer prestaflow:json:file puis pousse le résultat à la plateforme.
  • Poste un commentaire de synthèse sur la PR si le workflow tourne sur un pull_request (d’où le bloc permissions: pull-requests: write).
  • Téléverse prestaflow/results.json et les captures d’échec en artifact (input upload-artifacts, actif par défaut).
  • Synchronise les régressions visuelles avec la plateforme, si vos suites posent des points de contrôle visuels (visualCheckpoint()) : les baselines sont téléchargées avant l’exécution, les nouvelles captures et les diffs sont envoyés à la fin. Un template modifié qui casse le rendu apparaît alors dans le rapport visuel de la plateforme, même si aucun it ne tombe.

Consommer les outputs

L’Action expose des outputs consommables par les étapes suivantes :

      - name: Show summary
        if: always()
        run: |
          echo "Status : ${{ steps.prestaflow.outputs.status }}"
          echo "Passed : ${{ steps.prestaflow.outputs.passed }}"
          echo "Failed : ${{ steps.prestaflow.outputs.failed }}"
          echo "Report : ${{ steps.prestaflow.outputs.report-url }}"

Utile pour brancher une notification Slack, un statut custom, ou un gate sur status == 'success' avant de publier votre module.

Matrix multi-versions

C’est ici que le passage à l’Action prend tout son sens. Pour tester votre module contre 1.7.8.11, 8.1.7 et 9.0.0 en parallèle :

jobs:
  e2e:
    name: E2E — PrestaShop ${{ matrix.ps-version }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        ps-version: ['1.7.8.11', '8.1.7', '9.0.0']
    steps:
      - uses: actions/checkout@v4
      # ... setup PHP (avec variables_order=EGPCS), composer install ...
      - uses: PrestaFlow/github-action@v2
        env:
          PRESTAFLOW_LOCALE: en
          PRESTAFLOW_BO_EMAIL: admin@prestashop.com
          PRESTAFLOW_BO_PASSWD: prestashop
        with:
          token: ${{ secrets.PRESTAFLOW_TOKEN }}
          projectId: 'pk_01ABCDEFGHIJKLMNOP'
          flashlight: true
          ps-version: ${{ matrix.ps-version }}
          flashlight-init-scripts: tests/flashlight-init

Côté plateforme, chaque job de la matrice envoie son propre run. Sur la PR, en revanche, il n’y a qu’un seul commentaire PrestaFlow : chaque job le retrouve et le réécrit avec son propre bilan. Seul le bilan du dernier job terminé reste visible (le commentaire indique sa version PS). Pour comparer les trois versions, passez par la plateforme ou par le statut de chaque job dans l’onglet Checks : c’est là que vous voyez que votre nouveau template casse spécifiquement en 1.7 sans toucher au reste.

C’est exactement le pattern que nous avions installé pour PHPStan en 2024, transposé au fonctionnel.

Notes

Le niveau 1 reste une option légitime pour un projet expérimental ou personnel, ou pour un module dont vous ne souhaitez pas remonter les résultats sur la plateforme. Il fait ce qu’il promet : rougir le CI quand un scénario casse, avec la réserve du code de sortie vue plus haut.

Dès que le module vit en production, que plusieurs personnes contribuent, ou que vous supportez plus d’une version de PrestaShop, le niveau 2 s’amortit vite. Le commentaire PR évite d’aller lire les logs pour savoir si ça passe, l’historique remplace la mémoire humaine, et les régressions visuelles rattrapent la classe de bugs “j’ai touché un CSS et je ne l’ai pas vu”.

Dans la Série PrestaFlow — article 2 sur 23