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 enisNotEmpty()— 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. UngoToPage('login')sait aller sur/loginen anglais et sur/connexionen 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 estfr.
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"siPRESTAFLOW_LOCALE=en(ou si aucun catalogue ne connaît cette clé — c’est l’identité)- la traduction française si
PRESTAFLOW_LOCALE=fret 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