Retour au blog
Tests

PrestaFlow : gérer les scénarios sur plusieurs locales

PrestaEdit •
PrestaFlow : gérer les scénarios sur plusieurs locales

Le problème

Plusieurs annexes de cette série ont contourné un même sujet en le repoussant à plus tard :

  • Dans Tester un module tiers, l’assertion contains('subscription') a été volontairement affaiblie en isNotEmpty() — parce que sur une boutique française le message contient « inscription », pas « subscription », et qu’aucune assertion en dur ne peut couvrir les deux.
  • Dans Un scénario qui tient de PS 1.7 à 9, on a noté que multi-versions rime souvent avec multi-locale et qu’il faudrait “paramétrer le texte attendu selon la locale du run”.

Dès qu’une boutique existe en plusieurs langues — ou qu’on la teste en anglais aujourd’hui puis dans sa version localisée demain — l’assertion en dur casse. Il faut un mécanisme.

Il en existe deux dans PrestaFlow, distincts et complémentaires : URLs friendly (navigation) et Translations (assertions).

Deux mécanismes, deux dossiers

  • src/Urls/{locale}.json — mappe les slugs canoniques (anglais) vers leur variante localisée. Un goToPage('login') sait aller sur /login en anglais et sur /connexion en français.
  • src/Translations/{locale}.json — mappe les messages canoniques (anglais) vers leur traduction. $this->translate('The employee does not exist...') retourne la version française si la locale courante est fr.

Les deux systèmes sont pilotés par la même variable d’environnement : PRESTAFLOW_LOCALE (en par défaut).

Dans la version 1.7.1, la lib livre des catalogues pour deux locales seulement : en et fr. Pour toute autre langue, c’est à vous de fournir les fichiers (voir plus bas) : sans eux, les slugs restent en anglais et translate() renvoie le texte anglais.

URLs friendly : la partie déjà gérée pour vous

Les URLs friendly des pages standard PrestaShop sont déjà cataloguées côté lib. Voici src/Urls/fr.json en entier (le fichier en.json est vide : en anglais, le slug canonique sert tel quel) :

{
    "guest-tracking": "suivi-commande-invite",
    "login": "connexion",
    "cart": "panier?action=show",
    "order": "commande"
}

Un scénario qui écrit :

$frontOfficeCartPage->goToPage('cart');

atterrit sur /cart avec PRESTAFLOW_LOCALE=en et sur /panier?action=show avec PRESTAFLOW_LOCALE=fr. Rien à faire côté scénario.

Traductions : le vrai travail

Reprenons la Page Modules\PsEmailsubscription\NewsletterBlock de l’annexe Tester un module tiers. Le point qui coinçait, c’est cette assertion :

Expect::that($page->getResultMessage())
    ->contains('subscription');   // ← casse en français

La solution PrestaFlow tient en deux ingrédients : la méthode defineMessages() de la Page et la fonction translate() du parent.

Déclarer les messages attendus

Sur la Page, on ajoute :

public function defineMessages(): array
{
    return [
        'successText' => $this->translate('You have successfully subscribed'),
    ];
}

translate('You have successfully subscribed') consulte le catalogue de la locale courante et retourne :

  • "You have successfully subscribed" si PRESTAFLOW_LOCALE=en (ou si aucun catalogue ne connaît cette clé — c’est l’identité)
  • la traduction française si PRESTAFLOW_LOCALE=fr et que ce message est dans le catalogue français (on l’y ajoute plus bas).

Les messages résolus sont pré-calculés au chargement de la Page et disponibles via getMessage('successText').

Exposer une assertion sémantique

Toujours sur la Page, on ajoute une méthode qui compare le message affiché au message attendu :

public function subscriptionSucceeded(): bool
{
    return str_contains(
        $this->getResultMessage(),
        (string) $this->getMessage('successText')
    );
}

Le scénario devient enfin lisible :

Expect::that($modulesPsEmailsubscriptionNewsletterBlockPage->subscriptionSucceeded())
    ->isTheSameAs(true);

Il passe en français, en anglais, et sur toute locale pour laquelle le catalogue contient la traduction. Une seule chaîne canonique (l’anglaise) dans le code, la traduction vit à côté dans un JSON — même modèle mental que la traduction PrestaShop elle-même.

Ajouter ses propres traductions

Le catalogue de la lib couvre ses propres Pages (Login, Product, Listing, etc.). Pour un message qui vous appartient ("You have successfully subscribed" n’est pas un texte PrestaShop, c’est celui du module tiers ou de votre wrapper), vous ajoutez un catalogue custom à la racine de votre projet :

<racine-projet>/
├─ tests/prestaflow/       ← code PHP (Pages, Suites) — inchangé
├─ Tests/
│  └─ Translations/
│     ├─ fr.json
│     └─ en.json
├─ composer.json
└─ .env

Contenu de Tests/Translations/fr.json :

{
    "You have successfully subscribed": "Vous êtes bien abonné à la newsletter."
}

Recopiez la traduction telle que votre boutique l’affiche, au caractère près : c’est elle que str_contains() cherchera dans le message.

Le fichier en.json peut rester {} (l’identité) ou vous permettre de surcharger des messages de la lib si nécessaire.

La lib fusionne les catalogues dans cet ordre : default → major → minor → patch → custom. Vos traductions gagnent toujours sur celles de la lib, ce qui vous permet aussi bien de compléter que de corriger.

Tester la même suite contre plusieurs locales, en local

Même approche que le script de l’annexe multi-versions : une boucle shell qui change une variable d’environnement, la suite ne change pas.

Un détail compte : la version 1.7.1 de la lib lit PRESTAFLOW_LOCALE dans $_ENV, qui reste vide quand PHP est configuré sans le E de variables_order (le réglage des php.ini fournis avec PHP). Un simple PRESTAFLOW_LOCALE=fr composer prestaflow -- … peut donc être ignoré sans message. Le plus sûr est de passer par un fichier que la lib charge. Elle lit .env.local à la place de .env quand il existe : le script en génère un à chaque tour, à partir de votre .env, et le supprime à la fin.

#!/usr/bin/env bash
set -e

if [ -f .env.local ]; then
    echo ".env.local existe déjà : renommez-le avant de lancer ce script." >&2
    exit 1
fi
trap 'rm -f .env.local' EXIT

for locale in fr en; do
    echo "=== Locale $locale ==="

    # Copie du .env + la locale du tour (la dernière valeur d'une clé l'emporte).
    { cat .env; echo; echo "PRESTAFLOW_LOCALE=$locale"; } > .env.local
    composer prestaflow -- run ./tests/prestaflow
done

Prérequis : que votre boutique de test ait bien les langues installées et activées. Sinon vos navigations aboutissent en 404, et vos assertions traduites tombent sur un message resté en anglais.

En CI, on ajoute une dimension locale à la matrice, et on la relie à PRESTAFLOW_LOCALE. Pour la même raison qu’en local, on écrit la variable dans le .env lors d’une étape préalable plutôt que dans un bloc env: :

jobs:
  e2e:
    name: E2E — locale ${{ matrix.locale }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        locale: ['fr', 'en']
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
      - run: composer install --prefer-dist --no-progress

      - name: Préparer le .env PrestaFlow
        run: |
          if [ -f .env.local ]; then
            echo "::error::.env.local présent, le .env ne serait pas chargé"
            exit 1
          fi
          {
            echo "PRESTAFLOW_FO_URL=https://preprod.example.com/"
            echo "PRESTAFLOW_LOCALE=${{ matrix.locale }}"
          } >> .env

      - name: Run PrestaFlow
        run: composer prestaflow -- run ./tests/prestaflow

Deux jobs parallèles, un par locale, contre la même preprod. Si elle est protégée, ajoutez les variables des annexes htpasswd ou Cloudflare dans la même étape.

Notes

Dans la Série PrestaFlow — article 11 sur 23