Retour au blog
Tests

PrestaFlow : sauter le login BO grâce aux cookies pré-injectés

PrestaEdit •
PrestaFlow : sauter le login BO grâce aux cookies pré-injectés

Le coût invisible du login()

Les suites BO de la série commencent presque toutes par le même préambule :

->it('se connecte au BO', function () use ($backOfficeLoginPage) {
    $backOfficeLoginPage->goToPage('login');
    $backOfficeLoginPage->login();
})

Quelques secondes par suite : environ deux contre une boutique Flashlight locale, davantage sur une preprod chargée ou lointaine. Multipliées par le nombre de suites BO, elles s’additionnent à chaque run, dépensées à re-jouer le même formulaire. En CI multi-versions × multi-locales, on multiplie encore par le nombre de cellules de la matrice.

PrestaFlow expose un mécanisme pour supprimer ce coût : PRESTAFLOW_COOKIES, une variable d’environnement qui injecte un jeu de cookies dans Chrome avant toute navigation. Vous arrivez sur la page cible avec la session déjà ouverte, comme si l’utilisateur venait de se connecter.

PRESTAFLOW_COOKIES en un paragraphe

C’est un tableau JSON d’objets cookie, avec les champs standards de l’API cookies de Chrome :

PRESTAFLOW_COOKIES=[{"name":"...","value":"...","domain":"...","path":"/","secure":true,"httpOnly":true,"sameSite":"Strict","expires":1770000000}]
  • name — obligatoire (un objet sans name est ignoré). value — vide par défaut, donc à fournir en pratique.
  • domain — obligatoire en pratique pour un cookie de session.
  • path — défaut /.
  • secure, httpOnly, sameSite, expires, url — optionnels, transmis tels quels à Chrome.

L’injection a lieu au début de chaque suite, dans TestsSuite::before(), juste après le nettoyage des cookies laissés par la suite précédente. Elle se fait en best-effort : si le JSON est invalide ou si Chrome refuse un cookie (cookie mal formé, domaine incohérent), le run continue sans erreur. C’est confortable, mais ça oblige à valider au moins une fois que le cookie est bien pris, sinon on croit sauter le login alors que les suites tombent sur le formulaire de connexion.

Le code de référence est la méthode presetEnvCookies() de TestsSuite.php, lignes 706 à 746 dans la v1.7.1.

Cas 1 : neutraliser un bandeau RGPD

Le plus simple. Un bandeau cookie Knowband (ou équivalent) masque le sélecteur de votre it et fait échouer l’assertion. Vous pré-réglez le cookie de consentement, le bandeau ne s’affiche jamais.

PRESTAFLOW_COOKIES=[{"name":"___kbgdcc","value":"eyIxIjp0cnVlfQ==","domain":"preprod.example.com"}]

Aucun changement dans les scénarios. Le bandeau disparaît de tous les runs qui utilisent ce .env.

Cas 2 : login BO déjà fait

Le vrai gain. Reprenons la suite UpdateTitle de l’article d’introduction, réécrite avec la Page Configuration de l’annexe Factoriser ses Pages. Elle enchaîne quatre étapes :

  1. se connecte au BO ;
  2. met à jour le titre du bloc ;
  3. affiche le nouveau titre sur la home ;
  4. remet le titre par défaut.

Avec un cookie de session admin pré-injecté :

PRESTAFLOW_BO_URL=https://preprod.example.com/admin-dev/
PRESTAFLOW_COOKIES=[{"name":"PrestaShop-a1b2c3d4e5f6","value":"def50200a1b2...","domain":"preprod.example.com","path":"/","httpOnly":true}]

Le nom du cookie de session BO est PrestaShop- suivi d’un hash propre à la boutique ; relevez-le tel quel, avec son path (voir plus bas comment le récupérer). Sur PrestaShop 8, ce seul cookie suffit (vérifié sur 8.1.7). Sur PrestaShop 9, l’authentification BO passe aussi par la session Symfony : ajoutez le cookie PHPSESSID au tableau (vérifié sur 9.0.0 : sans lui, la page de configuration renvoie au formulaire de connexion).

La suite peut alors s’écrire sans le premier it :

<?php

namespace Tests\Suites;

use PrestaFlow\Library\Expects\Expect;
use PrestaFlow\Library\Tests\TestsSuite;

class UpdateTitleWithSession extends TestsSuite
{
    public function init()
    {
        $this->importPage('Modules\Psflowdemo\Configuration', domain: 'Tests');
        $this->importPage('Modules\Psflowdemo\Home', domain: 'Tests');

        extract($this->pages);

        $newTitle = 'Titre mis à jour par PrestaFlow';
        $defaultTitle = 'Bienvenue sur notre boutique';
        $boUrl = $this->getGlobals()['BO']['URL'];

        // Plus de goToPage('login') / login() : le cookie de session est déjà là.
        $this
        ->describe('Modification du titre depuis le BO (session pré-injectée)')
        ->it('met à jour le titre du bloc', function () use ($modulesPsflowdemoConfigurationPage, $boUrl, $newTitle) {
            $modulesPsflowdemoConfigurationPage->openConfiguration($boUrl);
            $modulesPsflowdemoConfigurationPage->fillTitle($newTitle);
            $modulesPsflowdemoConfigurationPage->save();

            // Garde-fou : si le cookie a expiré, on est sur le formulaire de
            // connexion et ce message n'apparaît pas.
            Expect::that($modulesPsflowdemoConfigurationPage->hasSuccessMessage())
                ->isTheSameAs(true);
        })
        ->it('affiche le nouveau titre sur la home', function () use ($modulesPsflowdemoHomePage, $newTitle) {
            $modulesPsflowdemoHomePage->goToPage('home');

            Expect::that($modulesPsflowdemoHomePage->getBlockTitle())
                ->contains($newTitle);
        })
        ->it('remet le titre par défaut', function () use ($modulesPsflowdemoConfigurationPage, $boUrl, $defaultTitle) {
            $modulesPsflowdemoConfigurationPage->openConfiguration($boUrl);
            $modulesPsflowdemoConfigurationPage->fillTitle($defaultTitle);
            $modulesPsflowdemoConfigurationPage->save();
        });
    }
}

De quatre it à trois, et c’est le formulaire de connexion qui disparaît. L’assertion sur hasSuccessMessage() n’est pas décorative : sans session valide, fillTitle() et save() ne lèvent pas d’erreur sur la page de connexion, et c’est elle qui fait tomber la suite au bon endroit. Le gain total dépend du nombre de suites BO qui bénéficient du même traitement.

Trois méthodes, dans l’ordre de facilité :

Chrome DevTools (le plus rapide, pour un one-shot local). Connectez-vous manuellement à votre BO, ouvrez DevTools → Application → Cookies → votre domaine. Copiez name, value, domain, path du cookie PrestaShop-… (et de PHPSESSID sur PrestaShop 9).

Dump depuis un run PrestaFlow (utile pour capter la session telle que PrestaFlow l’ouvre). Ajoutez temporairement une suite qui se connecte puis écrit tous les cookies du navigateur dans un fichier. getPage() renvoie la page chrome-php, dont getAllCookies() renvoie une collection de cookies ; chaque cookie se convertit en tableau avec iterator_to_array() :

->it('se connecte au BO et exporte les cookies', function () use ($backOfficeLoginPage) {
    $backOfficeLoginPage->goToPage('login');
    $backOfficeLoginPage->login();

    $cookies = [];
    foreach ($backOfficeLoginPage->getPage()->getAllCookies() as $cookie) {
        $cookies[] = iterator_to_array($cookie);
    }
    file_put_contents('cookies.json', json_encode($cookies, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
})

Vous récupérez la liste complète (session PrestaShop, session PHP, éventuels cookies tiers). Gardez ceux qui vous intéressent, avec leurs champs name, value, domain, path et httpOnly, et ne versionnez pas cookies.json.

Un login par run plutôt que par suite (le plus propre pour la CI). Rangez cette suite de dump dans son propre dossier (en v1.7.1, run attend un dossier, pas un fichier) et lancez-la seule, dans une première commande, puis écrivez le résultat dans PRESTAFLOW_COOKIES pour la seconde commande, celle qui exécute vos suites. Vous payez un seul login par run, et le cookie est toujours frais : c’est la seule méthode qui tient dans le temps sans rotation manuelle.

Ajoutez PRESTAFLOW_COOKIES en secret du dépôt (Settings → Secrets and variables → Actions), puis dans votre workflow :

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

# ...

- name: Run PrestaFlow
  env:
    PRESTAFLOW_COOKIES: ${{ secrets.PRESTAFLOW_COOKIES }}
  run: composer prestaflow -- run ./tests/prestaflow

ini-values: variables_order=EGPCS n’est pas un détail : la bibliothèque v1.7.1 lit PRESTAFLOW_COOKIES dans $_ENV, que PHP laisse vide sans le E. Sans ce réglage, la variable passée par env: n’atteint pas PrestaFlow, sans aucun message.

Cas 3 : forcer une variante ou bypass une whitelist

Le mécanisme s’applique à n’importe quel cookie, pas seulement d’auth.

  • Variante A/B — votre boutique lit ab_variant=B pour servir la version B, sinon la A. Injectez le cookie, testez la variante isolément.
  • IP whitelist — votre boutique redirige les IP inconnues vers une page maintenance sauf si un cookie bypass_maintenance=<token> est présent. Injectez le token, PrestaFlow entre.
  • Locale forcée — un cookie qui court-circuite la détection de langue par header.

Rien de spécifique à PrestaShop dans ces trois cas : ce sont des cookies ordinaires, posés par l’API cookies de Chrome.

Notes

Dans la Série PrestaFlow — article 16 sur 23