From fd3405e257aea918aa126e852de64f0e9e2953bb Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:25:50 +0200 Subject: [PATCH 01/11] =?UTF-8?q?docs(spec):=20version=20PrestaShop=20port?= =?UTF-8?q?=C3=A9e=20par=20chaque=20objet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../2026-10-02-version-per-instance-design.md | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-02-version-per-instance-design.md diff --git a/docs/superpowers/specs/2026-10-02-version-per-instance-design.md b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md new file mode 100644 index 0000000..a87fd58 --- /dev/null +++ b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md @@ -0,0 +1,117 @@ +# Version de PrestaShop portée par chaque objet : design + +Date : 2026-10-02 +Statut : validé en conversation, en attente de relecture + +## Problème + +La version de PrestaShop (majeure, mineure, patch) vit dans un attribut +**statique** du trait `Traits\Version` (`public static $versions`). + +- **Partage selon la version de PHP.** Un statique déclaré dans un trait + appartient à chaque classe qui fait `use` du trait. + - `CommonPage` l'obtient via `Translations` ; `BackOfficePage` et les pages + v7/v8/v9 partagent donc le statique de `CommonPage`. + - `FrontOfficePage` refait `use Translations` : avant PHP 8.3 il partage le + statique de `CommonPage`, à partir de 8.3 il a le sien. + - La lib déclare `php ^8.1` : le comportement diffère entre la CI (8.1 à 8.5) + et un poste en 8.4. +- **Argument ignoré.** Le constructeur d'une page reçoit `patchVersion` mais + ne l'utilise jamais. `BackOfficePage` et `FrontOfficePage` calculent leurs + sélecteurs et messages (`getSelectors()`, `getMessages()` → + `getPageName()` → `getMajorVersion(namespace: true)`) **avant** + `parent::__construct`, avec la version en cache, sinon `PS_VERSION` des + globals, sinon `'8'`. +- **Repli silencieux mis en cache.** Ce `'8'` est mémorisé dans le statique et + l'emporte ensuite sur la vraie version des pages suivantes. Constaté le + 2026-10-02 : un test construisant une page sans version faisait lire des + sélecteurs v8 à une page v9 suivante, en PHP 8.1 et 8.2 seulement. +- **Bugs voisins.** + - `exctractVersions()` lève `InvalidVersionException` sans l'importer : + on obtient « classe introuvable » au lieu du message prévu. + - `Scenario` fait `setVersions($globals['PATCH_VERSION'] ?? …)` : une chaîne + peut remplacer le tableau des versions. + +## Décisions + +| Sujet | Décision | +|---|---| +| Où vit la version | Sur chaque objet (page, suite, scénario). Plus de statique. | +| Page sans version | `InvalidVersionException` explicite, plus de repli sur `'8'`. | +| Statique `$versions` | Supprimé. | +| Formats acceptés | Inchangés (le contrat avec l'app, qui refuse `8.1.10`, ne bouge pas). | +| API publique | Noms et signatures des getters/setters conservés (l'app les liste par leur nom). | + +## 1. Trait `Version` + +- `public static $versions` est remplacé par une propriété d'instance + `protected array $versions = ['patchVersion' => null, 'minorVersion' => null, 'majorVersion' => null]`. + Toutes les lectures et écritures passent par `$this->versions`. +- `getVersions()` / `setVersions(array $versions)` : `setVersions` est typé + `array` et ne garde que les trois clés connues. +- `getMajorVersion()` : + - renvoie la majeure de l'objet si elle est connue ; + - sinon, si `globals['PS_VERSION']` est présent, l'analyse + (`exctractVersions`) et renvoie la majeure ; + - sinon lève `InvalidVersionException` (« version PrestaShop inconnue »). + - Le repli sur `'8'` disparaît. +- `exctractVersions()` importe `PrestaFlow\Library\Exceptions\InvalidVersionException`. + Formats acceptés inchangés. +- `resolveVersion()` (suites) garde son ordre de priorité et son défaut + `'8.1.0'` : c'est le seul endroit avec un défaut. + +## 2. Pages + +- Nouvelle méthode `CommonPage::initVersion(string $patchVersion, array $globals): void` : + - `$patchVersion` non vide → `exctractVersions($patchVersion)` ; + - sinon `globals['PS_VERSION']` non vide → `exctractVersions(...)` ; + - sinon `InvalidVersionException`. +- Appelée **en premier** dans `CommonPage::__construct`, `BackOfficePage::__construct` + et `FrontOfficePage::__construct`, avant `getSelectors()` / `getMessages()`. +- `ImportPage::importPage()` continue de passer `patchVersion` et les globals ; + la copie des versions après construction devient + `$page->setVersions($this->getVersions())` (même effet, sans statique). + +## 3. Scénarios + +- `Scenario` reprend `$testSuite->getVersions()` (un tableau). +- Si `globals['PATCH_VERSION']` est présent, il est analysé par + `exctractVersions()` au lieu d'être passé tel quel à `setVersions()`. + +## 4. Nettoyage + +- `BackOfficeGoToMenuTest` : suppression de la sauvegarde / restauration de + `CommonPage::$versions` (setUp / tearDown). +- `CommonPage::visualCheckpoint()` : la majeure est lue par + `getMajorVersion()`, sans le contournement « globals d'abord ». +- Tests qui lisent ou écrivent le statique (`VersionOverrideTest`, + `TranslationsCatalogTest`, `BackOfficeGoToMenuTest`) : adaptés aux + accesseurs d'instance. +- Pages factices de tests construites sans version (ex. `makePage` des + tests `VisualCheckpoint*`) : elles reçoivent un `PS_VERSION` ou un + `patchVersion`. + +## Tests + +- Une page `BackOfficePage` 8.1 puis une page `FrontOfficePage` 9.0 + construites l'une après l'autre : chacune garde sa majeure et ses + sélecteurs (et l'inverse). +- Une page construite avec `patchVersion: '1.7.8.11'` et sans `PS_VERSION` + a la majeure `1.7`. +- Une page sans aucune version lève `InvalidVersionException`, avec son + message (preuve que la classe est bien importée). +- `Scenario` avec `PATCH_VERSION` garde un tableau de versions cohérent. +- `resolveVersion()` des suites : comportement inchangé (tests existants). +- Suite unitaire complète verte sous **PHP 8.1, 8.2, 8.3 et 8.4**. +- CI de la PR : 13 checks verts (PHPUnit, Smoke, Visual). + +## Livraison + +- PR lib vers `dev`, puis release vers `main`. +- Puis mise à jour de la lib dans l'app (`composer.lock` seul) ; tests de + l'app verts. + +## Hors périmètre + +- Élargir les formats de version acceptés (ex. `8.1.10`). +- Revoir `resolveVersion()` et son défaut `'8.1.0'`. From c511ee985af9120f7a4f3efd1978fac426cc5388 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:31:06 +0200 Subject: [PATCH 02/11] docs(plan): version PrestaShop par instance Co-Authored-By: Claude Opus 5.5 --- .../plans/2026-10-02-version-per-instance.md | 952 ++++++++++++++++++ ...6-10-02-version-per-instance.md.tasks.json | 40 + .../2026-10-02-version-per-instance-design.md | 5 +- 3 files changed, 995 insertions(+), 2 deletions(-) create mode 100644 docs/superpowers/plans/2026-10-02-version-per-instance.md create mode 100644 docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json diff --git a/docs/superpowers/plans/2026-10-02-version-per-instance.md b/docs/superpowers/plans/2026-10-02-version-per-instance.md new file mode 100644 index 0000000..230bf3c --- /dev/null +++ b/docs/superpowers/plans/2026-10-02-version-per-instance.md @@ -0,0 +1,952 @@ +# Version PrestaShop portée par chaque objet : plan d'implémentation + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development (recommended) or superpowers-extended-cc:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** chaque page, suite et scénario porte sa propre version de PrestaShop. Il n'y a plus de cache statique partagé, et une page sans version lève `InvalidVersionException`. + +**Architecture :** +- Le trait `Traits\Version` passe d'un `public static $versions` à une propriété d'instance `protected array $versions`. +- Les pages prennent leur version dès le début de leur constructeur (`initVersion()`), avant de calculer sélecteurs et messages. +- `Scenario` et `ImportPage` se transmettent la version explicitement. + +**Tech Stack :** PHP ^8.1, PHPUnit 10.5. Pas de navigateur dans ces tests. + +**Spec :** `docs/superpowers/specs/2026-10-02-version-per-instance-design.md` + +--- + +## Contexte pour l'implémenteur + +- **Dépôt :** worktree `/Users/jonathan/Documents/GitHub/PrestaFlow/php-library-visual`, branche `fix/version-per-instance`. Toutes les commandes se lancent depuis ce dossier. +- **Binaires PHP :** + - PHP 8.4 : `php` ; + - PHP 8.1 : `"$HOME/Library/Application Support/Herd/bin/php81"` ; + - PHP 8.2 : `"$HOME/Library/Application Support/Herd/bin/php82"` ; + - PHP 8.3 : `"$HOME/Library/Application Support/Herd/bin/php83"`. +- **Suite complète :** `php vendor/bin/phpunit` (523 tests verts au départ). +- **Pourquoi le statique pose problème :** un statique déclaré dans un trait appartient à chaque classe qui fait `use` du trait. + - `CommonPage` l'obtient via `Resolvers\Translations`, qui fait `use Version`. + - `FrontOfficePage` refait `use Translations` : avant PHP 8.3 il partage le statique de `CommonPage`, à partir de 8.3 il a le sien. + - Toute sous-classe qui ne refait pas `use` (BackOfficePage, pages v7/v8/v9, classes anonymes) partage le statique de `CommonPage` sur toutes les versions de PHP. +- **Commits :** + - chemins explicites, jamais `git add -A`, `git add .` ni `git commit -a` ; + - messages en français ; + - chaque message se termine par la ligne `Co-Authored-By: Claude Opus 5.5 ` (précédée d'une ligne vide). +- **Ne pas toucher** `/Users/jonathan/Documents/GitHub/PrestaFlow/php-library` (checkout de l'utilisateur). + +## Fichiers + +| Fichier | Rôle | +|---|---| +| `src/Traits/Version.php` | Version par instance, import de l'exception, fin du repli `'8'` | +| `src/Pages/CommonPage.php` | `initVersion()`, appel dans le constructeur, retrait du contournement de `visualCheckpoint()` | +| `src/Pages/BackOfficePage.php`, `src/Pages/FrontOfficePage.php` | Appel de `initVersion()` en tête de constructeur | +| `src/Scenarios/Scenario.php` | Reprise du tableau de versions de la suite, analyse de `PATCH_VERSION` | +| `src/Traits/ImportPage.php` | Suppression de la recopie des versions (la page la prend à sa construction) | +| `tests/Unit/Pages/PageVersionInitTest.php` (nouveau) | Version prise à la construction, erreurs | +| `tests/Unit/Pages/PageVersionIsolationTest.php` (nouveau) | Deux pages de versions différentes ne se contaminent pas | +| `tests/Unit/Scenarios/ScenarioVersionTest.php` (nouveau) | Versions du scénario | +| `tests/Unit/Tests/SuiteVersionWiringTest.php` | + import de pages par deux suites de versions différentes | +| `tests/Unit/Traits/VersionOverrideTest.php`, `tests/Unit/Resolvers/TranslationsCatalogTest.php`, `tests/Unit/Pages/BackOfficeGoToMenuTest.php` | Ne touchent plus le statique | +| `tests/Unit/Visual/VisualCheckpointTagTest.php` | Le test « globals d'abord » devient « version de la page » | + +--- + +### Task 1 : les pages prennent leur version à la construction + +**Goal :** une page analyse son `patchVersion`, sinon le `PS_VERSION` des globals, avant ses sélecteurs. Sans aucune version, elle lève `InvalidVersionException`. L'exception est importée dans le trait. + +**Files :** +- Create : `tests/Unit/Pages/PageVersionInitTest.php` +- Modify : `src/Traits/Version.php` (ajout d'un `use`) +- Modify : `src/Pages/CommonPage.php:47-62` (constructeur) ; nouvelle méthode `initVersion()` juste après le constructeur +- Modify : `src/Pages/BackOfficePage.php:18-36`, `src/Pages/FrontOfficePage.php:22-41` + +**Acceptance Criteria :** +- [ ] `new CommonPage('en', '1.7.8.11', [])` a la majeure `1.7` (`7` en namespace) et le patch `1.7.8.11`. +- [ ] `patchVersion` vide avec `PS_VERSION` `9.2.0` donne la majeure `9`. +- [ ] Sans `patchVersion` ni `PS_VERSION`, la construction lève `InvalidVersionException` avec le message « version PrestaShop inconnue ». +- [ ] `patchVersion` `8.1.10` lève `InvalidVersionException` (« Error with version 8.1.10 »), et non une erreur « classe introuvable ». +- [ ] Suite complète verte en PHP 8.4. + +**Verify :** `php vendor/bin/phpunit tests/Unit/Pages/PageVersionInitTest.php` → OK (5 tests) + +**Steps :** + +- [ ] **Step 1 : écrire le test qui échoue** + +`tests/Unit/Pages/PageVersionInitTest.php` : + +```php +assertSame('1.7', $page->getMajorVersion()); + $this->assertSame('7', $page->getMajorVersion(namespace: true)); + $this->assertSame('1.7.8.11', $page->getPatchVersion()); + } + + public function test_patch_version_argument_wins_over_the_globals(): void + { + $page = new CommonPage('en', '8.1.0', ['PS_VERSION' => '9.2.0']); + + $this->assertSame('8', $page->getMajorVersion()); + } + + public function test_globals_ps_version_is_used_when_patch_version_is_empty(): void + { + $page = new CommonPage('en', '', ['PS_VERSION' => '9.2.0']); + + $this->assertSame('9', $page->getMajorVersion()); + $this->assertSame('9.2.0', $page->getPatchVersion()); + } + + public function test_a_page_without_any_version_throws(): void + { + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('version PrestaShop inconnue'); + + new CommonPage('en', '', []); + } + + public function test_an_unsupported_version_format_throws_the_library_exception(): void + { + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('Error with version 8.1.10'); + + new CommonPage('en', '8.1.10', []); + } +} +``` + +- [ ] **Step 2 : vérifier l'échec** + +Run : `php vendor/bin/phpunit tests/Unit/Pages/PageVersionInitTest.php` + +Expected : FAIL. +- `test_a_page_without_any_version_throws` : aucune exception. +- `test_an_unsupported_version_format_throws_the_library_exception` : aucune exception, car le constructeur n'analyse pas encore la version. +- Les autres tests peuvent passer ou échouer selon le cache statique. + +- [ ] **Step 3 : importer l'exception dans le trait** + +Dans `src/Traits/Version.php`, remplacer : + +```php +use PrestaFlow\Library\Utils\Env; +``` + +par : + +```php +use PrestaFlow\Library\Exceptions\InvalidVersionException; +use PrestaFlow\Library\Utils\Env; +``` + +- [ ] **Step 4 : ajouter `initVersion()` et l'appeler dans les trois constructeurs** + +Dans `src/Pages/CommonPage.php`, ajouter l'import en tête (avec les autres `use`) : + +```php +use PrestaFlow\Library\Exceptions\InvalidVersionException; +``` + +Remplacer le début du constructeur : + +```php + public function __construct(string $locale, string $patchVersion, array $globals, array $customs = []) + { + $this->globals = $globals; +``` + +par : + +```php + public function __construct(string $locale, string $patchVersion, array $globals, array $customs = []) + { + $this->initVersion(patchVersion: $patchVersion, globals: $globals); + $this->globals = $globals; +``` + +Ajouter juste après la méthode `__construct` (avant `setCustoms`) : + +```php + /** + * Version PrestaShop de cette page : son patchVersion, sinon le + * PS_VERSION des globals. Appelée en tête des constructeurs, avant que + * getSelectors()/getMessages() ne choisissent les fichiers par majeure. + */ + public function initVersion(string $patchVersion, array $globals): void + { + $version = $patchVersion !== '' ? $patchVersion : ($globals['PS_VERSION'] ?? null); + + if (!is_string($version) || $version === '') { + throw new InvalidVersionException( + static::class . ' : version PrestaShop inconnue (ni patchVersion ni PS_VERSION dans les globals).' + ); + } + + $this->exctractVersions($version); + } +``` + +Dans `src/Pages/BackOfficePage.php` et `src/Pages/FrontOfficePage.php`, remplacer dans `__construct` : + +```php + { + $this->globals = $globals; + $this->customs = array_merge($this->customs, $customs); + $this->initLocale(locale: $locale); +``` + +par : + +```php + { + $this->initVersion(patchVersion: $patchVersion, globals: $globals); + $this->globals = $globals; + $this->customs = array_merge($this->customs, $customs); + $this->initLocale(locale: $locale); +``` + +- [ ] **Step 5 : vérifier le succès, puis la suite complète** + +Run : `php vendor/bin/phpunit tests/Unit/Pages/PageVersionInitTest.php` + +Expected : OK (5 tests). + +Run : `php vendor/bin/phpunit` + +Expected : OK. Si un test construit une page sans version, il échoue désormais sur « version PrestaShop inconnue ». Corriger alors **le test**, jamais `src/` : +- passer un `patchVersion` au constructeur (`'9.0.0'` si le test ne dépend pas de la version) ; +- ou ajouter `'PS_VERSION' => '9.0.0'` à ses globals. + +- [ ] **Step 6 : commit** + +```bash +git add src/Traits/Version.php src/Pages/CommonPage.php src/Pages/BackOfficePage.php src/Pages/FrontOfficePage.php tests/Unit/Pages/PageVersionInitTest.php +git commit -m "fix(version): une page prend sa version à la construction + +Co-Authored-By: Claude Opus 5.5 " +``` + +(Ajouter au `git add` tout test corrigé à l'étape 5, chemin par chemin.) + +--- + +### Task 2 : la version devient une propriété d'instance + +**Goal :** supprimer `public static $versions`. Chaque objet garde ses versions, `getMajorVersion()` ne retombe plus sur `'8'`, et les trois tests qui touchaient le statique sont adaptés. + +**Files :** +- Create : `tests/Unit/Pages/PageVersionIsolationTest.php` +- Modify : `src/Traits/Version.php` (réécriture complète ci-dessous) +- Modify : `tests/Unit/Traits/VersionOverrideTest.php:16-32` +- Modify : `tests/Unit/Resolvers/TranslationsCatalogTest.php:14-28` +- Modify : `tests/Unit/Pages/BackOfficeGoToMenuTest.php:13-30` + +**Acceptance Criteria :** +- [ ] Deux `BackOfficePage` (8.1.0 puis 9.0.0) gardent chacune leur majeure, sur toutes les versions de PHP. +- [ ] Une `BackOfficePage` 8.1.0 puis une `FrontOfficePage` 9.0.0 gardent chacune leur majeure, en PHP 8.1 comme en 8.4. +- [ ] `grep -rn '::\$versions' src tests` ne renvoie rien. +- [ ] Un objet sans version ni `PS_VERSION` lève `InvalidVersionException` dans `getMajorVersion()`. +- [ ] `setVersions()` n'accepte qu'un tableau et ne garde que les trois clés. +- [ ] Suite complète verte en PHP 8.4 et 8.1. + +**Verify :** `"$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit tests/Unit/Pages/PageVersionIsolationTest.php` → OK (4 tests) + +**Steps :** + +- [ ] **Step 1 : écrire le test qui échoue** + +`tests/Unit/Pages/PageVersionIsolationTest.php` : + +```php +assertSame('8', $first->getMajorVersion()); + $this->assertSame('9', $second->getMajorVersion()); + } + + public function test_a_back_office_page_does_not_leak_into_a_front_office_page(): void + { + $bo = new class ('en', '8.1.0', []) extends BackOfficePage {}; + $fo = new class ('en', '9.0.0', []) extends FrontOfficePage {}; + + $this->assertSame('8', $bo->getMajorVersion()); + $this->assertSame('9', $fo->getMajorVersion()); + } + + public function test_an_object_without_any_version_throws_instead_of_assuming_8(): void + { + $object = new class { + use Version; + public array $globals = []; + }; + + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('version PrestaShop inconnue'); + + $object->getMajorVersion(); + } + + public function test_set_versions_keeps_only_the_three_known_keys(): void + { + $object = new class { + use Version; + public array $globals = []; + }; + + $object->setVersions(['majorVersion' => '9', 'minorVersion' => '9.2', 'patchVersion' => '9.2.0', 'other' => 'x']); + + $this->assertSame( + ['patchVersion' => '9.2.0', 'minorVersion' => '9.2', 'majorVersion' => '9'], + $object->getVersions() + ); + } +} +``` + +- [ ] **Step 2 : vérifier l'échec** + +Run : `"$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit tests/Unit/Pages/PageVersionIsolationTest.php` + +Expected : FAIL. +- `test_two_back_office_pages_keep_their_own_version` : `'9'` au lieu de `'8'` (statique partagé). +- `test_a_back_office_page_does_not_leak_into_a_front_office_page` : `'9'` au lieu de `'8'` en PHP 8.1. +- `test_an_object_without_any_version_throws_instead_of_assuming_8` : pas d'exception (repli `'8'`). +- `test_set_versions_keeps_only_the_three_known_keys` : la clé `other` est gardée. + +- [ ] **Step 3 : réécrire `src/Traits/Version.php`** + +Contenu complet du fichier : + +```php + null, + 'minorVersion' => null, + 'majorVersion' => null, + ]; + + protected ?string $psVersionOverride = null; + + public function onVersion(string $version): self + { + if (!preg_match('/^\d+\.\d+(\.\d+){0,2}$/', $version)) { + throw new \InvalidArgumentException( + "Invalid PS version: '" . $version . "'. Expected format like '1.7.8.11' or '9.0.1'." + ); + } + + $this->psVersionOverride = $version; + + if (!empty($this->globals)) { + $this->resolveVersion(); + } + + return $this; + } + + public function resolveVersion(): void + { + $propertyVersion = property_exists($this, 'psVersion') ? ($this->psVersion ?? null) : null; + + $version = $this->psVersionOverride + ?? $propertyVersion + ?? Env::get('PRESTAFLOW_PS_VERSION') + ?? ($this->globals['PS_VERSION'] ?? null) + ?? '8.1.0'; + + if (!is_array($this->globals ?? null)) { + $this->globals = []; + } + $this->globals['PS_VERSION'] = $version; + + $this->setVersions([]); + + $this->exctractVersions($version); + } + + public function isVersionSupported() + { + if (in_array($this->getMajorVersion(), self::$supportedVersions)) { + return true; + } + + return false; + } + + public function setVersions(array $versions = []): array + { + return $this->versions = [ + 'patchVersion' => $versions['patchVersion'] ?? null, + 'minorVersion' => $versions['minorVersion'] ?? null, + 'majorVersion' => $versions['majorVersion'] ?? null, + ]; + } + + public function getVersions(): array + { + return $this->versions; + } + + public function setPatchVersion(string $patchVersion) + { + $this->versions['patchVersion'] = $patchVersion; + } + + public function getPatchVersion() + { + return $this->versions['patchVersion']; + } + + public function setMinorVersion(string $minorVersion) + { + $this->versions['minorVersion'] = $minorVersion; + } + + public function getMinorVersion() + { + return $this->versions['minorVersion']; + } + + public function setMajorVersion(string $majorVersion) + { + $this->versions['majorVersion'] = $majorVersion; + } + + /** + * Majeure de cet objet ('1.7', '8', '9'). Si elle n'a jamais été posée, + * elle se déduit du PS_VERSION des globals ; sans lui, erreur explicite + * (il n'y a plus de repli sur '8'). + */ + public function getMajorVersion(bool $namespace = false) + { + if (empty($this->versions['majorVersion'])) { + $psVersion = $this->globals['PS_VERSION'] ?? null; + + if (!is_string($psVersion) || $psVersion === '') { + throw new InvalidVersionException( + static::class . ' : version PrestaShop inconnue (aucune version reçue, ni PS_VERSION dans les globals).' + ); + } + + $this->exctractVersions($psVersion); + } + + $majorVersion = $this->versions['majorVersion']; + + if ($namespace && str_starts_with($majorVersion, '1.')) { + return substr($majorVersion, strlen('1.')); + } + + return $majorVersion; + } + + public function exctractVersions(string $patchVersion) + { + $this->versions['patchVersion'] = $patchVersion; + + if (strlen($patchVersion) === 7 || strlen($patchVersion) === 8) { + $this->versions['minorVersion'] = substr($patchVersion, 0, 5); + } else if (strlen($patchVersion) === 5) { + $this->versions['minorVersion'] = substr($patchVersion, 0, 3); + } else { + throw new InvalidVersionException('Error with version ' . $patchVersion); + } + + $minorVersion = $this->versions['minorVersion']; + if (str_starts_with($minorVersion, '1.7')) { + $this->versions['majorVersion'] = '1.7'; + } else if (str_starts_with($minorVersion, '1.6')) { + $this->versions['majorVersion'] = '1.6'; + } else { + $this->versions['majorVersion'] = substr($minorVersion, 0, 1); + } + } +} +``` + +Notes : +- les formats acceptés par `exctractVersions()` sont inchangés (longueurs 5, 7 et 8) ; +- `resolveVersion()` garde son ordre de priorité et son défaut `'8.1.0'` ; +- en namespace, `1.6` donne désormais `6`, comme le faisait déjà la branche « globals » d'origine. + +- [ ] **Step 4 : adapter les trois tests qui touchaient le statique** + +`tests/Unit/Traits/VersionOverrideTest.php` : dans `makeSuite()`, supprimer le bloc suivant (chaque instance part déjà de versions vides) : + +```php + // Reset the static state via the concrete class using the trait so no deprecation is raised. + $suite::$versions = [ + 'patchVersion' => null, + 'minorVersion' => null, + 'majorVersion' => null, + ]; +``` + +`tests/Unit/Resolvers/TranslationsCatalogTest.php` : dans `makePage()`, supprimer : + +```php + $page::$versions = [ + 'patchVersion' => null, + 'minorVersion' => null, + 'majorVersion' => null, + ]; +``` + +`tests/Unit/Pages/BackOfficeGoToMenuTest.php` : supprimer le docblock, la propriété `$versions`, `setUp()` et `tearDown()` (lignes 13 à 30) : + +```php + /** + * The page versions live in a static (Traits\Version) that getPageName() + * fills on first use and never re-derives. Before PHP 8.3 a class + * re-using that trait (FrontOfficePage, via Translations) shares the + * parent's storage, so whatever a BackOfficePage caches here is what the + * next front-office page in the process sees. Leave it as found. + */ + private array $versions = []; + + protected function setUp(): void + { + $this->versions = CommonPage::$versions; + } + + protected function tearDown(): void + { + CommonPage::$versions = $this->versions; + } +``` + +Retirer aussi `use PrestaFlow\Library\Pages\CommonPage;` de ce fichier s'il n'est plus utilisé ailleurs. Vérifier avec `grep -n CommonPage tests/Unit/Pages/BackOfficeGoToMenuTest.php`. + +- [ ] **Step 5 : vérifier** + +Run : + +```bash +"$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit tests/Unit/Pages/PageVersionIsolationTest.php +grep -rn '::\$versions' src tests +php vendor/bin/phpunit +"$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit +``` + +Expected : +- isolation : OK (4 tests) ; +- `grep` : aucune ligne ; +- les deux suites complètes : OK. + +Si un test échoue sur « version PrestaShop inconnue », appliquer la règle de la Task 1 (Step 5) : corriger le test en lui donnant une version, jamais `src/`. Cas probables, dont le constructeur n'appelle pas `parent::__construct` : +- `FakeThemePage` dans `tests/Unit/Pages/ThemeSelectorsTest.php` : ajouter `$this->setMajorVersion('9');` dans son constructeur, s'il échoue ; +- les classes anonymes de `ClickFallbackTest` et `VisibilityOnLostBrowserTest`. + +- [ ] **Step 6 : commit** + +```bash +git add src/Traits/Version.php tests/Unit/Pages/PageVersionIsolationTest.php tests/Unit/Traits/VersionOverrideTest.php tests/Unit/Resolvers/TranslationsCatalogTest.php tests/Unit/Pages/BackOfficeGoToMenuTest.php +git commit -m "fix(version): version par instance, plus de cache statique ni de repli sur 8 + +Co-Authored-By: Claude Opus 5.5 " +``` + +(Ajouter au `git add` tout test corrigé à l'étape 5, chemin par chemin.) + +--- + +### Task 3 : transmission explicite (scénario, import de page) + +**Goal :** +- `Scenario` reprend le **tableau** de versions de sa suite, et analyse `PATCH_VERSION` s'il est présent au lieu de le passer tel quel à `setVersions()`. +- `importPage()` ne recopie plus les versions de la suite dans la page : la page les prend à sa construction, et `initTranslations()` les recalcule à partir du même patch. + +**Files :** +- Create : `tests/Unit/Scenarios/ScenarioVersionTest.php` +- Modify : `src/Scenarios/Scenario.php:32-36` +- Modify : `src/Traits/ImportPage.php:37-43` +- Modify : `tests/Unit/Tests/SuiteVersionWiringTest.php` (nouveau test en fin de classe) + +**Acceptance Criteria :** +- [ ] Un scénario créé sur une suite 1.7.8.11 a la majeure `1.7` et le patch `1.7.8.11`. +- [ ] Avec `PATCH_VERSION` `9.2.0` dans les globals (suite en 8.1.0), le scénario a un tableau de versions dont la majeure vaut `9`. +- [ ] Changer la version du scénario ne change pas celle de la suite. +- [ ] Deux suites (1.7.8.11 puis 9.0.0) qui importent `FrontOffice\Home` obtiennent une page v7 puis une page v9, et la première page reste en `1.7`. +- [ ] Suite complète verte en PHP 8.4. + +**Verify :** `php vendor/bin/phpunit tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php` → OK + +**Steps :** + +- [ ] **Step 1 : écrire les tests qui échouent** + +`tests/Unit/Scenarios/ScenarioVersionTest.php` : + +```php +onVersion($version); + $suite->setGlobals(['LOCALE' => 'en', ...$globals]); + + return $suite; + } + + public function test_the_scenario_takes_the_suite_versions(): void + { + $scenario = new Scenario($this->suite('1.7.8.11')); + + $this->assertSame('1.7', $scenario->getMajorVersion()); + $this->assertSame('1.7.8.11', $scenario->getPatchVersion()); + } + + public function test_a_patch_version_global_is_parsed_not_stored_as_a_string(): void + { + $scenario = new Scenario($this->suite('8.1.0', ['PATCH_VERSION' => '9.2.0'])); + + $this->assertSame( + ['patchVersion' => '9.2.0', 'minorVersion' => '9.2', 'majorVersion' => '9'], + $scenario->getVersions() + ); + } + + public function test_changing_the_scenario_version_leaves_the_suite_alone(): void + { + $suite = $this->suite('8.1.0'); + $scenario = new Scenario($suite); + + $scenario->setMajorVersion('9'); + + $this->assertSame('8', $suite->getMajorVersion()); + } +} +``` + +Ajouter en fin de classe dans `tests/Unit/Tests/SuiteVersionWiringTest.php` : + +```php + public function test_two_suites_import_pages_of_their_own_version(): void + { + $old = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '1.7.8.11'; + }; + $old->importPage('FrontOffice\Home'); + + $new = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '9.0.0'; + }; + $new->importPage('FrontOffice\Home'); + + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v7\FrontOffice\Home\Page::class, $old->pages['frontOfficeHomePage']); + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v9\FrontOffice\Home\Page::class, $new->pages['frontOfficeHomePage']); + $this->assertSame('1.7', $old->pages['frontOfficeHomePage']->getMajorVersion()); + $this->assertSame('9', $new->pages['frontOfficeHomePage']->getMajorVersion()); + } +``` + +- [ ] **Step 2 : vérifier l'échec** + +Run : `php vendor/bin/phpunit tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php` + +Expected : FAIL sur `test_a_patch_version_global_is_parsed_not_stored_as_a_string` (TypeError : `setVersions()` attend un tableau et reçoit `'9.2.0'`). Les autres tests peuvent déjà passer après la Task 2. Ils servent de garde-fous. + +- [ ] **Step 3 : implémenter** + +Dans `src/Scenarios/Scenario.php`, remplacer : + +```php + $locale = $this->globals['LOCALE'] ?? $testSuite->getLocale(); + $versions = $this->globals['PATCH_VERSION'] ?? $testSuite->getVersions(); + + $this->setVersions(versions: $versions); + $this->setLocale(locale: $locale); +``` + +par : + +```php + $locale = $this->globals['LOCALE'] ?? $testSuite->getLocale(); + + $this->setVersions(versions: $testSuite->getVersions()); + if (is_string($this->globals['PATCH_VERSION'] ?? null) && $this->globals['PATCH_VERSION'] !== '') { + $this->exctractVersions($this->globals['PATCH_VERSION']); + } + $this->setLocale(locale: $locale); +``` + +Dans `src/Traits/ImportPage.php`, remplacer : + +```php + $pageInstance->setLocale(locale: $locale); + $pageInstance->setPatchVersion($patchVersion); + $pageInstance->setMinorVersion($this->getMinorVersion()); + $pageInstance->setMajorVersion($this->getMajorVersion()); + + $pageInstance->initTranslations( +``` + +par : + +```php + $pageInstance->setLocale(locale: $locale); + + // La page a pris sa version à sa construction (patchVersion) ; + // initTranslations() la recalcule à partir du même patch. + $pageInstance->initTranslations( +``` + +- [ ] **Step 4 : vérifier** + +Run : `php vendor/bin/phpunit tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php` + +Expected : OK. + +Run : `php vendor/bin/phpunit` + +Expected : OK. + +- [ ] **Step 5 : commit** + +```bash +git add src/Scenarios/Scenario.php src/Traits/ImportPage.php tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php +git commit -m "fix(version): scénario et import de page se transmettent la version + +Co-Authored-By: Claude Opus 5.5 " +``` + +--- + +### Task 4 : la capture visuelle lit la version de sa page + +**Goal :** retirer de `visualCheckpoint()` le contournement « globals `PS_VERSION` d'abord ». Il n'existait qu'à cause du cache statique. La majeure du tag vient désormais de `getMajorVersion()`. + +**Files :** +- Modify : `src/Pages/CommonPage.php` (dans `visualCheckpoint()`, ~l.700-708) +- Modify : `tests/Unit/Visual/VisualCheckpointTagTest.php` (remplacer `testMajorVersionDerivesFromGlobalsPsVersionFirst`) + +**Acceptance Criteria :** +- [ ] Le tag d'une page vient de sa propre version, même si une autre page a changé la sienne entre-temps. +- [ ] Le cas 1.7 donne toujours `auto-v1.7-…`. +- [ ] Le commentaire « cache statique » a disparu de `CommonPage.php`. +- [ ] Suite complète verte en PHP 8.4. + +**Verify :** `php vendor/bin/phpunit tests/Unit/Visual/` → OK + +**Steps :** + +- [ ] **Step 1 : remplacer le test** + +Dans `tests/Unit/Visual/VisualCheckpointTagTest.php`, remplacer toute la méthode : + +```php + public function testMajorVersionDerivesFromGlobalsPsVersionFirst(): void + { + // Le cache statique de version peut être périmé (suite précédente du worker). + $page = $this->makePage(['PS_VERSION' => '8.1.0'], '1.7'); + + $page->visualCheckpoint('home'); + + $this->assertSame('auto-v8-1280x720-fr', TestsSuite::$visualResults[0]['tag']); + } +``` + +par : + +```php + public function testMajorVersionComesFromThisPageOnly(): void + { + // makePage() construit la page avec patchVersion '8.1.0'. Une autre + // page passée en 1.7 ensuite ne doit pas changer son tag. + $page = $this->makePage([], null); + $other = $this->makePage([], null); + $other->setMajorVersion('1.7'); + + $page->visualCheckpoint('home'); + + $this->assertSame('auto-v8-1280x720-fr', TestsSuite::$visualResults[0]['tag']); + } +``` + +- [ ] **Step 2 : vérifier que le test passe déjà** + +Run : `php vendor/bin/phpunit tests/Unit/Visual/VisualCheckpointTagTest.php` + +Expected : OK. La version est par instance depuis la Task 2 ; ce test garde la régression. + +- [ ] **Step 3 : simplifier `visualCheckpoint()`** + +Dans `src/Pages/CommonPage.php`, remplacer : + +```php + // globals PS_VERSION d'abord (vérité de la suite courante ; le cache statique + // de Version peut venir d'une suite précédente du worker), puis le cache. + // '1.7' / '1.6' sont des majeures valides (auparavant rejetées → « v? »). + $rawMajorVersion = $this->getMajorVersion(); + $majorVersion = \PrestaFlow\Library\Visual\VisualTag::majorFromVersion( + is_string($this->globals['PS_VERSION'] ?? null) ? $this->globals['PS_VERSION'] : null + ) ?? \PrestaFlow\Library\Visual\VisualTag::majorFromVersion( + is_scalar($rawMajorVersion) ? (string) $rawMajorVersion : null + ); +``` + +par : + +```php + // Majeure de cette page ; '1.7' / '1.6' sont des majeures valides + // (auparavant rejetées → « v? »). + $rawMajorVersion = $this->getMajorVersion(); + $majorVersion = \PrestaFlow\Library\Visual\VisualTag::majorFromVersion( + is_scalar($rawMajorVersion) ? (string) $rawMajorVersion : null + ); +``` + +- [ ] **Step 4 : vérifier** + +Run : + +```bash +php vendor/bin/phpunit tests/Unit/Visual/ +grep -n "cache statique" src/Pages/CommonPage.php +php vendor/bin/phpunit +``` + +Expected : +- tests visuels : OK ; +- `grep` : aucune ligne ; +- suite complète : OK. + +- [ ] **Step 5 : commit** + +```bash +git add src/Pages/CommonPage.php tests/Unit/Visual/VisualCheckpointTagTest.php +git commit -m "refactor(visual): le tag lit la version de la page, sans contournement + +Co-Authored-By: Claude Opus 5.5 " +``` + +--- + +### Task 5 : vérification sur PHP 8.1 à 8.4 + +**Goal :** prouver que le comportement ne dépend plus de la version de PHP, et corriger les derniers tests qui construiraient une page sans version. + +**Files :** +- Modify : uniquement des tests qui échouent ici (aucun fichier `src/` attendu). + +**Acceptance Criteria :** +- [ ] `phpunit` complet vert sous PHP 8.1, 8.2, 8.3 et 8.4. +- [ ] Les nouveaux tests d'isolation passent aussi avec `--order-by=random` (3 graines) sous PHP 8.1. +- [ ] `grep -rn "self::\$versions\|::\$versions" src tests` ne renvoie rien. + +**Verify :** les quatre commandes du Step 1 → OK + +**Steps :** + +- [ ] **Step 1 : lancer la matrice** + +```bash +"$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit +"$HOME/Library/Application Support/Herd/bin/php82" vendor/bin/phpunit +"$HOME/Library/Application Support/Herd/bin/php83" vendor/bin/phpunit +php vendor/bin/phpunit +``` + +Expected : OK pour chacune. + +- [ ] **Step 2 : ordre aléatoire** + +```bash +for seed in 1 2 3; do "$HOME/Library/Application Support/Herd/bin/php81" vendor/bin/phpunit --order-by=random --random-order-seed=$seed tests/Unit/Pages tests/Unit/Visual tests/Unit/Scenarios tests/Unit/Tests tests/Unit/Traits tests/Unit/Resolvers || echo "FAIL seed $seed"; done +``` + +Expected : aucune ligne « FAIL seed ». + +- [ ] **Step 3 : corriger si besoin** + +Un échec sur « version PrestaShop inconnue » vient d'un test qui construit une page sans version. Lui donner une version (`patchVersion` `'9.0.0'`, `'PS_VERSION' => '9.0.0'` dans ses globals, ou `setMajorVersion('9')` dans un constructeur factice). Ne jamais remettre un défaut dans `src/`. Relancer le Step 1. + +Un autre type d'échec doit être remonté avec la sortie complète, sans contournement. + +- [ ] **Step 4 : commit (seulement si des tests ont changé)** + +```bash +git add +git commit -m "test(version): versions explicites dans les pages factices + +Co-Authored-By: Claude Opus 5.5 " +``` + +--- + +## Après le plan (hors tâches, sur demande de l'utilisateur) + +1. Pousser la branche, ouvrir la PR vers `dev` et attendre la CI (13 checks : PHPUnit 8.1 à 8.5, Smoke, Visual). +2. Release `dev` → `main` (« Release: … (#N) »). +3. Mettre à jour la lib dans l'app : seul `composer.lock` change. Lancer ensuite les tests de l'app. + - `app/Services/Suite/SuiteMetadata.php` cite `setMajorVersion` / `getMajorVersion` / `exctractVersions` : leurs noms ne changent pas. + - Point d'attention : une suite chargée sans `loadGlobals` qui importerait une page lève maintenant une erreur au lieu de supposer la v8. diff --git a/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json b/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json new file mode 100644 index 0000000..31670a2 --- /dev/null +++ b/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json @@ -0,0 +1,40 @@ +{ + "planPath": "docs/superpowers/plans/2026-10-02-version-per-instance.md", + "tasks": [ + { + "id": 1, + "subject": "Task 1: les pages prennent leur version à la construction", + "status": "pending", + "description": "**Goal:** initVersion() en tête des constructeurs CommonPage/BackOfficePage/FrontOfficePage ; InvalidVersionException importée dans Traits\\Version.\n\n```json:metadata\n{\"files\": [\"src/Traits/Version.php\", \"src/Pages/CommonPage.php\", \"src/Pages/BackOfficePage.php\", \"src/Pages/FrontOfficePage.php\", \"tests/Unit/Pages/PageVersionInitTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Pages/PageVersionInitTest.php\", \"acceptanceCriteria\": [\"patchVersion 1.7.8.11 -> majeure 1.7\", \"PS_VERSION des globals si patchVersion vide\", \"sans version -> InvalidVersionException\", \"8.1.10 -> InvalidVersionException, pas classe introuvable\", \"suite complète verte en 8.4\"]}\n```" + }, + { + "id": 2, + "subject": "Task 2: la version devient une propriété d'instance", + "status": "pending", + "blockedBy": [1], + "description": "**Goal:** suppression de public static $versions, plus de repli sur '8', tests adaptés.\n\n```json:metadata\n{\"files\": [\"src/Traits/Version.php\", \"tests/Unit/Pages/PageVersionIsolationTest.php\", \"tests/Unit/Traits/VersionOverrideTest.php\", \"tests/Unit/Resolvers/TranslationsCatalogTest.php\", \"tests/Unit/Pages/BackOfficeGoToMenuTest.php\"], \"verifyCommand\": \"\\\"$HOME/Library/Application Support/Herd/bin/php81\\\" vendor/bin/phpunit tests/Unit/Pages/PageVersionIsolationTest.php\", \"acceptanceCriteria\": [\"deux pages BO gardent leur majeure\", \"BO 8.1 puis FO 9.0 isolées en PHP 8.1\", \"aucun ::$versions\", \"getMajorVersion sans version lève\", \"setVersions ne garde que 3 clés\", \"suite verte 8.4 et 8.1\"]}\n```" + }, + { + "id": 3, + "subject": "Task 3: transmission explicite (scénario, import de page)", + "status": "pending", + "blockedBy": [2], + "description": "**Goal:** Scenario reprend le tableau de versions de la suite et analyse PATCH_VERSION ; importPage ne recopie plus les versions.\n\n```json:metadata\n{\"files\": [\"src/Scenarios/Scenario.php\", \"src/Traits/ImportPage.php\", \"tests/Unit/Scenarios/ScenarioVersionTest.php\", \"tests/Unit/Tests/SuiteVersionWiringTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php\", \"acceptanceCriteria\": [\"scénario sur suite 1.7.8.11 -> 1.7\", \"PATCH_VERSION analysé en tableau\", \"scénario isolé de la suite\", \"deux suites importent v7 puis v9\"]}\n```" + }, + { + "id": 4, + "subject": "Task 4: la capture visuelle lit la version de sa page", + "status": "pending", + "blockedBy": [2], + "description": "**Goal:** retrait du contournement globals d'abord dans visualCheckpoint(), test Tag remplacé.\n\n```json:metadata\n{\"files\": [\"src/Pages/CommonPage.php\", \"tests/Unit/Visual/VisualCheckpointTagTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Visual/\", \"acceptanceCriteria\": [\"tag issu de la version de la page\", \"1.7 -> auto-v1.7\", \"plus de commentaire cache statique\"]}\n```" + }, + { + "id": 5, + "subject": "Task 5: vérification sur PHP 8.1 à 8.4", + "status": "pending", + "blockedBy": [3, 4], + "description": "**Goal:** suite verte sous PHP 8.1/8.2/8.3/8.4 et en ordre aléatoire ; corrections de tests factices seulement.\n\n```json:metadata\n{\"files\": [], \"verifyCommand\": \"php vendor/bin/phpunit (x 4 binaires)\", \"acceptanceCriteria\": [\"vert 8.1-8.4\", \"ordre aléatoire 3 graines vert en 8.1\", \"aucun ::$versions\"]}\n```" + } + ], + "lastUpdated": "2026-10-02T15:00:00+02:00" +} diff --git a/docs/superpowers/specs/2026-10-02-version-per-instance-design.md b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md index a87fd58..93b6ab8 100644 --- a/docs/superpowers/specs/2026-10-02-version-per-instance-design.md +++ b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md @@ -69,8 +69,9 @@ La version de PrestaShop (majeure, mineure, patch) vit dans un attribut - Appelée **en premier** dans `CommonPage::__construct`, `BackOfficePage::__construct` et `FrontOfficePage::__construct`, avant `getSelectors()` / `getMessages()`. - `ImportPage::importPage()` continue de passer `patchVersion` et les globals ; - la copie des versions après construction devient - `$page->setVersions($this->getVersions())` (même effet, sans statique). + la recopie des versions après construction est supprimée : la page a pris + sa version dans son constructeur, et `initTranslations()` la recalcule déjà + à partir du même patch. ## 3. Scénarios From bc74fce8d1d5f651daa71f6d74f82c14ef83811d Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:32:58 +0200 Subject: [PATCH 03/11] =?UTF-8?q?fix(version):=20une=20page=20prend=20sa?= =?UTF-8?q?=20version=20=C3=A0=20la=20construction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/Pages/BackOfficePage.php | 1 + src/Pages/CommonPage.php | 20 +++++++++ src/Pages/FrontOfficePage.php | 1 + src/Traits/Version.php | 1 + tests/Unit/Pages/PageVersionInitTest.php | 55 ++++++++++++++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 tests/Unit/Pages/PageVersionInitTest.php diff --git a/src/Pages/BackOfficePage.php b/src/Pages/BackOfficePage.php index c571921..d3be4aa 100644 --- a/src/Pages/BackOfficePage.php +++ b/src/Pages/BackOfficePage.php @@ -17,6 +17,7 @@ class BackOfficePage extends CommonPage public function __construct(string $locale, string $patchVersion, array $globals, array $customs = []) { + $this->initVersion(patchVersion: $patchVersion, globals: $globals); $this->globals = $globals; $this->customs = array_merge($this->customs, $customs); $this->initLocale(locale: $locale); diff --git a/src/Pages/CommonPage.php b/src/Pages/CommonPage.php index 0f5038a..12c833a 100644 --- a/src/Pages/CommonPage.php +++ b/src/Pages/CommonPage.php @@ -7,6 +7,7 @@ use HeadlessChromium\Exception\OperationTimedOut; use HeadlessChromium\Exception\TargetDestroyed; use HeadlessChromium\Page as DomPage; +use PrestaFlow\Library\Exceptions\InvalidVersionException; use PrestaFlow\Library\Exceptions\TimeoutException; use PrestaFlow\Library\Expects\Expect; use PrestaFlow\Library\Resolvers\Translations; @@ -46,6 +47,7 @@ class CommonPage public function __construct(string $locale, string $patchVersion, array $globals, array $customs = []) { + $this->initVersion(patchVersion: $patchVersion, globals: $globals); $this->globals = $globals; $this->customs = array_merge($this->customs, $customs); $this->patchVersion = $patchVersion; @@ -61,6 +63,24 @@ public function __construct(string $locale, string $patchVersion, array $globals return $this; } + /** + * Version PrestaShop de cette page : son patchVersion, sinon le + * PS_VERSION des globals. Appelée en tête des constructeurs, avant que + * getSelectors()/getMessages() ne choisissent les fichiers par majeure. + */ + public function initVersion(string $patchVersion, array $globals): void + { + $version = $patchVersion !== '' ? $patchVersion : ($globals['PS_VERSION'] ?? null); + + if (!is_string($version) || $version === '') { + throw new InvalidVersionException( + static::class . ' : version PrestaShop inconnue (ni patchVersion ni PS_VERSION dans les globals).' + ); + } + + $this->exctractVersions($version); + } + public function setCustoms(string $type, array $data = []) { if (isset($this->customs[$type]) && is_array($this->customs[$type])) { diff --git a/src/Pages/FrontOfficePage.php b/src/Pages/FrontOfficePage.php index 8faf34b..417a129 100644 --- a/src/Pages/FrontOfficePage.php +++ b/src/Pages/FrontOfficePage.php @@ -21,6 +21,7 @@ class FrontOfficePage extends CommonPage public function __construct(string $locale, string $patchVersion, array $globals, array $customs = []) { + $this->initVersion(patchVersion: $patchVersion, globals: $globals); $this->globals = $globals; $this->customs = array_merge($this->customs, $customs); $this->initLocale(locale: $locale); diff --git a/src/Traits/Version.php b/src/Traits/Version.php index cfc5ba5..c5c6bbf 100644 --- a/src/Traits/Version.php +++ b/src/Traits/Version.php @@ -2,6 +2,7 @@ namespace PrestaFlow\Library\Traits; +use PrestaFlow\Library\Exceptions\InvalidVersionException; use PrestaFlow\Library\Utils\Env; trait Version diff --git a/tests/Unit/Pages/PageVersionInitTest.php b/tests/Unit/Pages/PageVersionInitTest.php new file mode 100644 index 0000000..0640204 --- /dev/null +++ b/tests/Unit/Pages/PageVersionInitTest.php @@ -0,0 +1,55 @@ +assertSame('1.7', $page->getMajorVersion()); + $this->assertSame('7', $page->getMajorVersion(namespace: true)); + $this->assertSame('1.7.8.11', $page->getPatchVersion()); + } + + public function test_patch_version_argument_wins_over_the_globals(): void + { + $page = new CommonPage('en', '8.1.0', ['PS_VERSION' => '9.2.0']); + + $this->assertSame('8', $page->getMajorVersion()); + } + + public function test_globals_ps_version_is_used_when_patch_version_is_empty(): void + { + $page = new CommonPage('en', '', ['PS_VERSION' => '9.2.0']); + + $this->assertSame('9', $page->getMajorVersion()); + $this->assertSame('9.2.0', $page->getPatchVersion()); + } + + public function test_a_page_without_any_version_throws(): void + { + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('version PrestaShop inconnue'); + + new CommonPage('en', '', []); + } + + public function test_an_unsupported_version_format_throws_the_library_exception(): void + { + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('Error with version 8.1.10'); + + new CommonPage('en', '8.1.10', []); + } +} From da8aef5a28f2ed333857c4e9a799793cb7afb1a5 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:54:08 +0200 Subject: [PATCH 04/11] fix(version): version par instance, plus de cache statique ni de repli sur 8 Co-Authored-By: Claude Opus 5.5 --- src/Tests/TestsSuite.php | 5 + src/Traits/Version.php | 133 +++++++----------- tests/Unit/Pages/BackOfficeGoToMenuTest.php | 20 --- tests/Unit/Pages/PageVersionIsolationTest.php | 63 +++++++++ .../Resolvers/TranslationsCatalogTest.php | 5 - tests/Unit/Tests/SuiteVersionWiringTest.php | 9 ++ tests/Unit/Traits/VersionOverrideTest.php | 6 - 7 files changed, 131 insertions(+), 110 deletions(-) create mode 100644 tests/Unit/Pages/PageVersionIsolationTest.php diff --git a/src/Tests/TestsSuite.php b/src/Tests/TestsSuite.php index f0f0f0b..077c0d1 100644 --- a/src/Tests/TestsSuite.php +++ b/src/Tests/TestsSuite.php @@ -268,6 +268,11 @@ public function __construct(bool $loadGlobals = true, bool $getBrowser = true) { if ($loadGlobals) { $this->loadGlobals(); + } else { + // Une suite a toujours une version ($psVersion, onVersion, env, + // sinon 8.1.0) : before() la vérifie, et il n'y a plus de repli + // implicite dans Version::getMajorVersion(). + $this->resolveVersion(); } $this->before(getBrowser: $getBrowser); diff --git a/src/Traits/Version.php b/src/Traits/Version.php index c5c6bbf..40c7aef 100644 --- a/src/Traits/Version.php +++ b/src/Traits/Version.php @@ -13,21 +13,20 @@ trait Version '9' ]; - public static $versions = [ + /** + * Version PrestaShop de CET objet (page, suite, scénario). Propriété + * d'instance : un statique de trait est partagé par les sous-classes (et, + * avant PHP 8.3, entre classes qui refont `use`), si bien qu'une page + * imposait sa version aux suivantes. + */ + protected array $versions = [ 'patchVersion' => null, 'minorVersion' => null, 'majorVersion' => null, ]; - /** - * Fluent override set via onVersion(); wins over the $psVersion property and env. - */ protected ?string $psVersionOverride = null; - /** - * Pin a specific PrestaShop version for this suite. Fluent, chainable. - * Overrides the $psVersion property and the PRESTAFLOW_PS_VERSION env variable. - */ public function onVersion(string $version): self { if (!preg_match('/^\d+\.\d+(\.\d+){0,2}$/', $version)) { @@ -38,8 +37,6 @@ public function onVersion(string $version): self $this->psVersionOverride = $version; - // Once the globals are loaded, the version has already been resolved: - // resolve it again so the pages imported next use this one. if (!empty($this->globals)) { $this->resolveVersion(); } @@ -47,10 +44,6 @@ public function onVersion(string $version): self return $this; } - /** - * Resolve the effective PS version and populate $this->globals['PS_VERSION'] + version parts. - * Priority: fluent onVersion() > $psVersion property > PRESTAFLOW_PS_VERSION env > '8.1.0'. - */ public function resolveVersion(): void { $propertyVersion = property_exists($this, 'psVersion') ? ($this->psVersion ?? null) : null; @@ -66,12 +59,7 @@ public function resolveVersion(): void } $this->globals['PS_VERSION'] = $version; - // Reset all cached version parts defensively so consumers (e.g. Translations, Scenario) - // that read the static state without calling resolveVersion() don't see stale values - // from a previous suite pinned to a different version. - self::$versions['patchVersion'] = null; - self::$versions['minorVersion'] = null; - self::$versions['majorVersion'] = null; + $this->setVersions([]); $this->exctractVersions($version); } @@ -85,105 +73,92 @@ public function isVersionSupported() return false; } - public function setVersions($versions = []): array + public function setVersions(array $versions = []): array { - return self::$versions = $versions; + return $this->versions = [ + 'patchVersion' => $versions['patchVersion'] ?? null, + 'minorVersion' => $versions['minorVersion'] ?? null, + 'majorVersion' => $versions['majorVersion'] ?? null, + ]; } public function getVersions(): array { - return self::$versions; + return $this->versions; } public function setPatchVersion(string $patchVersion) { - self::$versions['patchVersion'] = $patchVersion; + $this->versions['patchVersion'] = $patchVersion; } public function getPatchVersion() { - return self::$versions['patchVersion'] ; + return $this->versions['patchVersion']; } public function setMinorVersion(string $minorVersion) { - self::$versions['minorVersion'] = $minorVersion; + $this->versions['minorVersion'] = $minorVersion; } public function getMinorVersion() { - return self::$versions['minorVersion'] ; + return $this->versions['minorVersion']; } public function setMajorVersion(string $majorVersion) { - self::$versions['majorVersion'] = $majorVersion; + $this->versions['majorVersion'] = $majorVersion; } + /** + * Majeure de cet objet ('1.7', '8', '9'). Si elle n'a jamais été posée, + * elle se déduit du PS_VERSION des globals ; sans lui, erreur explicite + * (il n'y a plus de repli sur '8'). + */ public function getMajorVersion(bool $namespace = false) { - if (!empty(self::$versions['majorVersion'])) { - if ($namespace && str_starts_with(self::$versions['majorVersion'], 1.7)) { - return substr(self::$versions['majorVersion'], strlen('1.')); + if (empty($this->versions['majorVersion'])) { + $psVersion = $this->globals['PS_VERSION'] ?? null; + + if (!is_string($psVersion) || $psVersion === '') { + throw new InvalidVersionException( + static::class . ' : version PrestaShop inconnue (aucune version reçue, ni PS_VERSION dans les globals).' + ); } - return self::$versions['majorVersion']; + $this->exctractVersions($psVersion); } - if (isset($this->globals['PS_VERSION'])) { - if (version_compare($this->globals['PS_VERSION'], '9.0.0', '>=')) { - $this->setMajorVersion('9'); - return '9'; - } else if (version_compare($this->globals['PS_VERSION'], '8.0.0', '>=')) { - $this->setMajorVersion('8'); - return '8'; - } else if (version_compare($this->globals['PS_VERSION'], '1.7.0', '>=')) { - $this->setMajorVersion('1.7'); - - if ($namespace) { - return '7'; - } - - return '1.7'; - } else if (version_compare($this->globals['PS_VERSION'], '1.6.0', '>=')) { - $this->setMajorVersion('1.6'); - - if ($namespace) { - return '6'; - } - - return '1.6'; - } + $majorVersion = $this->versions['majorVersion']; + + if ($namespace && str_starts_with($majorVersion, '1.')) { + return substr($majorVersion, strlen('1.')); } - $this->setMajorVersion('8'); - return '8'; + return $majorVersion; } public function exctractVersions(string $patchVersion) { - self::$versions['patchVersion'] = $patchVersion; - - if (strlen(self::$versions['patchVersion']) === 7 || strlen(self::$versions['patchVersion']) === 8) { - self::$versions['minorVersion'] = substr(self::$versions['patchVersion'], 0, 5); - if (str_starts_with(self::$versions['minorVersion'], '1.7')) { - self::$versions['majorVersion'] = '1.7'; - } else if (str_starts_with(self::$versions['minorVersion'], '1.6')) { - self::$versions['majorVersion'] = '1.6'; - } else { - self::$versions['majorVersion'] = substr(self::$versions['minorVersion'], 0, 1); - } - } else if (strlen(self::$versions['patchVersion']) === 5) { - self::$versions['minorVersion'] = substr(self::$versions['patchVersion'], 0, 3); - if (str_starts_with(self::$versions['minorVersion'], '1.7')) { - self::$versions['majorVersion'] = '1.7'; - } else if (str_starts_with(self::$versions['minorVersion'], '1.6')) { - self::$versions['majorVersion'] = '1.6'; - } else { - self::$versions['majorVersion'] = substr(self::$versions['minorVersion'], 0, 1); - } + $this->versions['patchVersion'] = $patchVersion; + + if (strlen($patchVersion) === 7 || strlen($patchVersion) === 8) { + $this->versions['minorVersion'] = substr($patchVersion, 0, 5); + } else if (strlen($patchVersion) === 5) { + $this->versions['minorVersion'] = substr($patchVersion, 0, 3); + } else { + throw new InvalidVersionException('Error with version ' . $patchVersion); + } + + $minorVersion = $this->versions['minorVersion']; + if (str_starts_with($minorVersion, '1.7')) { + $this->versions['majorVersion'] = '1.7'; + } else if (str_starts_with($minorVersion, '1.6')) { + $this->versions['majorVersion'] = '1.6'; } else { - throw new InvalidVersionException('Error with version ' . self::$versions['patchVersion']); + $this->versions['majorVersion'] = substr($minorVersion, 0, 1); } } } diff --git a/tests/Unit/Pages/BackOfficeGoToMenuTest.php b/tests/Unit/Pages/BackOfficeGoToMenuTest.php index 3ea2a59..096e57c 100644 --- a/tests/Unit/Pages/BackOfficeGoToMenuTest.php +++ b/tests/Unit/Pages/BackOfficeGoToMenuTest.php @@ -4,31 +4,11 @@ use PHPUnit\Framework\TestCase; use PrestaFlow\Library\Pages\BackOfficePage; -use PrestaFlow\Library\Pages\CommonPage; final class BackOfficeGoToMenuTest extends TestCase { private const CURRENT = 'http://shop.test/admin-dev/index.php?controller=AdminDashboard&token=t'; - /** - * The page versions live in a static (Traits\Version) that getPageName() - * fills on first use and never re-derives. Before PHP 8.3 a class - * re-using that trait (FrontOfficePage, via Translations) shares the - * parent's storage, so whatever a BackOfficePage caches here is what the - * next front-office page in the process sees. Leave it as found. - */ - private array $versions = []; - - protected function setUp(): void - { - $this->versions = CommonPage::$versions; - } - - protected function tearDown(): void - { - CommonPage::$versions = $this->versions; - } - /** Page factice : $links associe un sélecteur à son href (ou null si absent). */ private function page(array $links): BackOfficePage { diff --git a/tests/Unit/Pages/PageVersionIsolationTest.php b/tests/Unit/Pages/PageVersionIsolationTest.php new file mode 100644 index 0000000..c769e70 --- /dev/null +++ b/tests/Unit/Pages/PageVersionIsolationTest.php @@ -0,0 +1,63 @@ +assertSame('8', $first->getMajorVersion()); + $this->assertSame('9', $second->getMajorVersion()); + } + + public function test_a_back_office_page_does_not_leak_into_a_front_office_page(): void + { + $bo = new class ('en', '8.1.0', []) extends BackOfficePage {}; + $fo = new class ('en', '9.0.0', []) extends FrontOfficePage {}; + + $this->assertSame('8', $bo->getMajorVersion()); + $this->assertSame('9', $fo->getMajorVersion()); + } + + public function test_an_object_without_any_version_throws_instead_of_assuming_8(): void + { + $object = new class { + use Version; + public array $globals = []; + }; + + $this->expectException(InvalidVersionException::class); + $this->expectExceptionMessage('version PrestaShop inconnue'); + + $object->getMajorVersion(); + } + + public function test_set_versions_keeps_only_the_three_known_keys(): void + { + $object = new class { + use Version; + public array $globals = []; + }; + + $object->setVersions(['majorVersion' => '9', 'minorVersion' => '9.2', 'patchVersion' => '9.2.0', 'other' => 'x']); + + $this->assertSame( + ['patchVersion' => '9.2.0', 'minorVersion' => '9.2', 'majorVersion' => '9'], + $object->getVersions() + ); + } +} diff --git a/tests/Unit/Resolvers/TranslationsCatalogTest.php b/tests/Unit/Resolvers/TranslationsCatalogTest.php index 11f81ec..9e8c142 100644 --- a/tests/Unit/Resolvers/TranslationsCatalogTest.php +++ b/tests/Unit/Resolvers/TranslationsCatalogTest.php @@ -17,11 +17,6 @@ private function makePage(string $patchVersion): object use Translations; public array $customs = ['messages' => null]; }; - $page::$versions = [ - 'patchVersion' => null, - 'minorVersion' => null, - 'majorVersion' => null, - ]; $page->initLocale('fr'); $page->initTranslations(locale: 'fr', patchVersion: $patchVersion); diff --git a/tests/Unit/Tests/SuiteVersionWiringTest.php b/tests/Unit/Tests/SuiteVersionWiringTest.php index ea74462..fa997dd 100644 --- a/tests/Unit/Tests/SuiteVersionWiringTest.php +++ b/tests/Unit/Tests/SuiteVersionWiringTest.php @@ -82,4 +82,13 @@ public function init() $suite->pages['frontOfficeHomePage'] ); } + + public function test_a_suite_without_globals_still_resolves_its_version(): void + { + $suite = new class (loadGlobals: false, getBrowser: false) extends TestsSuite { + protected $psVersion = '1.7.8.11'; + }; + + $this->assertSame('1.7', $suite->getMajorVersion()); + } } diff --git a/tests/Unit/Traits/VersionOverrideTest.php b/tests/Unit/Traits/VersionOverrideTest.php index 37171f5..ec80375 100644 --- a/tests/Unit/Traits/VersionOverrideTest.php +++ b/tests/Unit/Traits/VersionOverrideTest.php @@ -20,12 +20,6 @@ private function makeSuite(?string $psVersion = null): object public array $globals = []; public ?string $psVersion = null; }; - // Reset the static state via the concrete class using the trait so no deprecation is raised. - $suite::$versions = [ - 'patchVersion' => null, - 'minorVersion' => null, - 'majorVersion' => null, - ]; $suite->psVersion = $psVersion; return $suite; From 28b0004c841edba7d4d19cc6540aa9bdd7ada408 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:57:26 +0200 Subject: [PATCH 05/11] =?UTF-8?q?docs(version):=20r=C3=A9tablit=20les=20do?= =?UTF-8?q?cblocks=20de=20onVersion/resolveVersion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/Traits/Version.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Traits/Version.php b/src/Traits/Version.php index 40c7aef..ed751c2 100644 --- a/src/Traits/Version.php +++ b/src/Traits/Version.php @@ -25,8 +25,15 @@ trait Version 'majorVersion' => null, ]; + /** + * Fluent override set via onVersion(); wins over the $psVersion property and env. + */ protected ?string $psVersionOverride = null; + /** + * Pin a specific PrestaShop version for this suite. Fluent, chainable. + * Overrides the $psVersion property and the PRESTAFLOW_PS_VERSION env variable. + */ public function onVersion(string $version): self { if (!preg_match('/^\d+\.\d+(\.\d+){0,2}$/', $version)) { @@ -37,6 +44,8 @@ public function onVersion(string $version): self $this->psVersionOverride = $version; + // Once the globals are loaded, the version has already been resolved: + // resolve it again so the pages imported next use this one. if (!empty($this->globals)) { $this->resolveVersion(); } @@ -44,6 +53,10 @@ public function onVersion(string $version): self return $this; } + /** + * Resolve the effective PS version and populate $this->globals['PS_VERSION'] + version parts. + * Priority: fluent onVersion() > $psVersion property > PRESTAFLOW_PS_VERSION env > '8.1.0'. + */ public function resolveVersion(): void { $propertyVersion = property_exists($this, 'psVersion') ? ($this->psVersion ?? null) : null; @@ -59,6 +72,8 @@ public function resolveVersion(): void } $this->globals['PS_VERSION'] = $version; + // Reset all cached version parts defensively so a re-resolution (e.g. after + // onVersion()) never keeps stale values from a previous version. $this->setVersions([]); $this->exctractVersions($version); From 08986d6dbac31d3afb03a57c84910df2fd77dfee Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 14:58:50 +0200 Subject: [PATCH 06/11] =?UTF-8?q?fix(version):=20sc=C3=A9nario=20et=20impo?= =?UTF-8?q?rt=20de=20page=20se=20transmettent=20la=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/Scenarios/Scenario.php | 6 ++- src/Traits/ImportPage.php | 5 +-- tests/Unit/Scenarios/ScenarioVersionTest.php | 47 ++++++++++++++++++++ tests/Unit/Tests/SuiteVersionWiringTest.php | 18 ++++++++ 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 tests/Unit/Scenarios/ScenarioVersionTest.php diff --git a/src/Scenarios/Scenario.php b/src/Scenarios/Scenario.php index 179d225..d9bcf9f 100644 --- a/src/Scenarios/Scenario.php +++ b/src/Scenarios/Scenario.php @@ -30,9 +30,11 @@ public function __construct($testSuite, $params = []) } $locale = $this->globals['LOCALE'] ?? $testSuite->getLocale(); - $versions = $this->globals['PATCH_VERSION'] ?? $testSuite->getVersions(); - $this->setVersions(versions: $versions); + $this->setVersions(versions: $testSuite->getVersions()); + if (is_string($this->globals['PATCH_VERSION'] ?? null) && $this->globals['PATCH_VERSION'] !== '') { + $this->exctractVersions($this->globals['PATCH_VERSION']); + } $this->setLocale(locale: $locale); $this->steps($testSuite); } diff --git a/src/Traits/ImportPage.php b/src/Traits/ImportPage.php index 630ae35..de49aba 100644 --- a/src/Traits/ImportPage.php +++ b/src/Traits/ImportPage.php @@ -38,10 +38,9 @@ public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null $pageInstance->setGlobals($globals); $pageInstance->setUserAgent($userAgent); $pageInstance->setLocale(locale: $locale); - $pageInstance->setPatchVersion($patchVersion); - $pageInstance->setMinorVersion($this->getMinorVersion()); - $pageInstance->setMajorVersion($this->getMajorVersion()); + // La page a pris sa version à sa construction (patchVersion) ; + // initTranslations() la recalcule à partir du même patch. $pageInstance->initTranslations( locale: $locale, patchVersion: $pageInstance->getPatchVersion(), diff --git a/tests/Unit/Scenarios/ScenarioVersionTest.php b/tests/Unit/Scenarios/ScenarioVersionTest.php new file mode 100644 index 0000000..03fd451 --- /dev/null +++ b/tests/Unit/Scenarios/ScenarioVersionTest.php @@ -0,0 +1,47 @@ +onVersion($version); + $suite->setGlobals(['LOCALE' => 'en', ...$globals]); + + return $suite; + } + + public function test_the_scenario_takes_the_suite_versions(): void + { + $scenario = new Scenario($this->suite('1.7.8.11')); + + $this->assertSame('1.7', $scenario->getMajorVersion()); + $this->assertSame('1.7.8.11', $scenario->getPatchVersion()); + } + + public function test_a_patch_version_global_is_parsed_not_stored_as_a_string(): void + { + $scenario = new Scenario($this->suite('8.1.0', ['PATCH_VERSION' => '9.2.0'])); + + $this->assertSame( + ['patchVersion' => '9.2.0', 'minorVersion' => '9.2', 'majorVersion' => '9'], + $scenario->getVersions() + ); + } + + public function test_changing_the_scenario_version_leaves_the_suite_alone(): void + { + $suite = $this->suite('8.1.0'); + $scenario = new Scenario($suite); + + $scenario->setMajorVersion('9'); + + $this->assertSame('8', $suite->getMajorVersion()); + } +} diff --git a/tests/Unit/Tests/SuiteVersionWiringTest.php b/tests/Unit/Tests/SuiteVersionWiringTest.php index fa997dd..0396e5f 100644 --- a/tests/Unit/Tests/SuiteVersionWiringTest.php +++ b/tests/Unit/Tests/SuiteVersionWiringTest.php @@ -91,4 +91,22 @@ public function test_a_suite_without_globals_still_resolves_its_version(): void $this->assertSame('1.7', $suite->getMajorVersion()); } + + public function test_two_suites_import_pages_of_their_own_version(): void + { + $old = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '1.7.8.11'; + }; + $old->importPage('FrontOffice\Home'); + + $new = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '9.0.0'; + }; + $new->importPage('FrontOffice\Home'); + + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v7\FrontOffice\Home\Page::class, $old->pages['frontOfficeHomePage']); + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v9\FrontOffice\Home\Page::class, $new->pages['frontOfficeHomePage']); + $this->assertSame('1.7', $old->pages['frontOfficeHomePage']->getMajorVersion()); + $this->assertSame('9', $new->pages['frontOfficeHomePage']->getMajorVersion()); + } } From 1764c925572f0bef0f4956899cb856efae1b8bac Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 15:01:30 +0200 Subject: [PATCH 07/11] =?UTF-8?q?fix(version):=20importPage=20tire=20names?= =?UTF-8?q?pace=20et=20version=20de=20la=20page=20du=20m=C3=AAme=20patch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/Traits/ImportPage.php | 19 +++++++++++++--- tests/Unit/Tests/SuiteVersionWiringTest.php | 25 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Traits/ImportPage.php b/src/Traits/ImportPage.php index de49aba..01bb83d 100644 --- a/src/Traits/ImportPage.php +++ b/src/Traits/ImportPage.php @@ -6,8 +6,6 @@ trait ImportPage { public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null, $domain = '\\PrestaFlow\\Library') { - $pageClass = $domain.'\\Pages\\v'.$this->getMajorVersion(namespace: true).'\\'.$pageName.'\\Page'; - if ($globals === null || !is_array($globals)) { $globals = $this->globals; } else { @@ -27,7 +25,12 @@ public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null } $locale = $globals['LOCALE'] ?? $this->getLocale(); - $patchVersion = $globals['PATCH_VERSION'] ?? $this->getPatchVersion(); + $patchVersion = $globals['PATCH_VERSION'] ?? null; + if (!is_string($patchVersion) || $patchVersion === '') { + $patchVersion = (string) ($this->getPatchVersion() ?? ''); + } + + $pageClass = $domain.'\\Pages\\v'.$this->pageNamespaceVersion($patchVersion).'\\'.$pageName.'\\Page'; $customs = []; if (isset($this->customs) && is_array($this->customs)) { @@ -59,4 +62,14 @@ public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null fwrite(STDERR, $pageInstance->lastThemeWarning . PHP_EOL); } } + + /** Segment de namespace (7, 8, 9…) d'une version '1.7.8.11' / '9.2.0'. */ + private function pageNamespaceVersion(string $patchVersion): string + { + if ($patchVersion === '') { + return (string) $this->getMajorVersion(namespace: true); + } + + return preg_match('/^1\.(\d+)/', $patchVersion, $m) === 1 ? $m[1] : explode('.', $patchVersion)[0]; + } } diff --git a/tests/Unit/Tests/SuiteVersionWiringTest.php b/tests/Unit/Tests/SuiteVersionWiringTest.php index 0396e5f..2e16d6f 100644 --- a/tests/Unit/Tests/SuiteVersionWiringTest.php +++ b/tests/Unit/Tests/SuiteVersionWiringTest.php @@ -109,4 +109,29 @@ public function test_two_suites_import_pages_of_their_own_version(): void $this->assertSame('1.7', $old->pages['frontOfficeHomePage']->getMajorVersion()); $this->assertSame('9', $new->pages['frontOfficeHomePage']->getMajorVersion()); } + + public function test_a_patch_version_global_decides_both_the_namespace_and_the_page_version(): void + { + $suite = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '8.1.0'; + }; + $suite->importPage('FrontOffice\Home', globals: ['PATCH_VERSION' => '1.7.8.11']); + + $page = $suite->pages['frontOfficeHomePage']; + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v7\FrontOffice\Home\Page::class, $page); + $this->assertSame('1.7', $page->getMajorVersion()); + } + + public function test_a_scenario_imports_pages_of_its_own_version(): void + { + $suite = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '1.7.8.11'; + }; + $scenario = new \PrestaFlow\Library\Scenarios\Scenario($suite, ['locale' => 'en']); + $scenario->importPage('FrontOffice\Home'); + + $page = $scenario->pages['frontOfficeHomePage']; + $this->assertInstanceOf(\PrestaFlow\Library\Pages\v7\FrontOffice\Home\Page::class, $page); + $this->assertSame('1.7.8.11', $page->getPatchVersion()); + } } From afd7c0b8d8adcbb778792d7dc3710ff7e6b609a4 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 15:04:55 +0200 Subject: [PATCH 08/11] refactor(version): un seul analyseur de version, erreur claire pour une version sans pages Co-Authored-By: Claude Opus 5.5 --- src/Traits/ImportPage.php | 12 ++++++++- src/Traits/Version.php | 26 ++++++++++++++----- tests/Unit/Pages/PageVersionIsolationTest.php | 18 +++++++++++++ tests/Unit/Tests/SuiteVersionWiringTest.php | 21 +++++++++++++++ 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/Traits/ImportPage.php b/src/Traits/ImportPage.php index 01bb83d..ed5c649 100644 --- a/src/Traits/ImportPage.php +++ b/src/Traits/ImportPage.php @@ -2,6 +2,8 @@ namespace PrestaFlow\Library\Traits; +use PrestaFlow\Library\Exceptions\InvalidVersionException; + trait ImportPage { public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null, $domain = '\\PrestaFlow\\Library') @@ -32,6 +34,12 @@ public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null $pageClass = $domain.'\\Pages\\v'.$this->pageNamespaceVersion($patchVersion).'\\'.$pageName.'\\Page'; + if (!class_exists($pageClass)) { + throw new InvalidVersionException( + 'Version PrestaShop non prise en charge pour ' . $pageName . ' (' . $patchVersion . ') : ' . $pageClass . ' introuvable.' + ); + } + $customs = []; if (isset($this->customs) && is_array($this->customs)) { $customs = $this->customs; @@ -70,6 +78,8 @@ private function pageNamespaceVersion(string $patchVersion): string return (string) $this->getMajorVersion(namespace: true); } - return preg_match('/^1\.(\d+)/', $patchVersion, $m) === 1 ? $m[1] : explode('.', $patchVersion)[0]; + $major = self::parseVersions($patchVersion)['majorVersion']; + + return str_starts_with($major, '1.') ? substr($major, 2) : $major; } } diff --git a/src/Traits/Version.php b/src/Traits/Version.php index ed751c2..736c1cd 100644 --- a/src/Traits/Version.php +++ b/src/Traits/Version.php @@ -157,23 +157,35 @@ public function getMajorVersion(bool $namespace = false) public function exctractVersions(string $patchVersion) { - $this->versions['patchVersion'] = $patchVersion; + $this->setVersions(self::parseVersions($patchVersion)); + } + /** + * Analyseur unique d'une version ('1.7.8.11', '9.2.0'…) : patch, mineure et + * majeure. Lève InvalidVersionException pour un format refusé. + */ + public static function parseVersions(string $patchVersion): array + { if (strlen($patchVersion) === 7 || strlen($patchVersion) === 8) { - $this->versions['minorVersion'] = substr($patchVersion, 0, 5); + $minorVersion = substr($patchVersion, 0, 5); } else if (strlen($patchVersion) === 5) { - $this->versions['minorVersion'] = substr($patchVersion, 0, 3); + $minorVersion = substr($patchVersion, 0, 3); } else { throw new InvalidVersionException('Error with version ' . $patchVersion); } - $minorVersion = $this->versions['minorVersion']; if (str_starts_with($minorVersion, '1.7')) { - $this->versions['majorVersion'] = '1.7'; + $majorVersion = '1.7'; } else if (str_starts_with($minorVersion, '1.6')) { - $this->versions['majorVersion'] = '1.6'; + $majorVersion = '1.6'; } else { - $this->versions['majorVersion'] = substr($minorVersion, 0, 1); + $majorVersion = substr($minorVersion, 0, 1); } + + return [ + 'patchVersion' => $patchVersion, + 'minorVersion' => $minorVersion, + 'majorVersion' => $majorVersion, + ]; } } diff --git a/tests/Unit/Pages/PageVersionIsolationTest.php b/tests/Unit/Pages/PageVersionIsolationTest.php index c769e70..12ec362 100644 --- a/tests/Unit/Pages/PageVersionIsolationTest.php +++ b/tests/Unit/Pages/PageVersionIsolationTest.php @@ -60,4 +60,22 @@ public function test_set_versions_keeps_only_the_three_known_keys(): void $object->getVersions() ); } + public function test_parse_versions_splits_a_patch_version(): void + { + // Appelé via une classe qui utilise le trait : appeler un statique de + // trait directement est déprécié depuis PHP 8.1. + $parser = new class { + use Version; + public array $globals = []; + }; + + $this->assertSame( + ['patchVersion' => '1.7.8.11', 'minorVersion' => '1.7.8', 'majorVersion' => '1.7'], + $parser::parseVersions('1.7.8.11') + ); + $this->assertSame( + ['patchVersion' => '9.2.0', 'minorVersion' => '9.2', 'majorVersion' => '9'], + $parser::parseVersions('9.2.0') + ); + } } diff --git a/tests/Unit/Tests/SuiteVersionWiringTest.php b/tests/Unit/Tests/SuiteVersionWiringTest.php index 2e16d6f..067fc40 100644 --- a/tests/Unit/Tests/SuiteVersionWiringTest.php +++ b/tests/Unit/Tests/SuiteVersionWiringTest.php @@ -134,4 +134,25 @@ public function test_a_scenario_imports_pages_of_its_own_version(): void $this->assertInstanceOf(\PrestaFlow\Library\Pages\v7\FrontOffice\Home\Page::class, $page); $this->assertSame('1.7.8.11', $page->getPatchVersion()); } + + public function test_a_patch_version_without_pages_is_a_clear_error(): void + { + $suite = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '8.1.0'; + }; + + $this->expectException(\PrestaFlow\Library\Exceptions\InvalidVersionException::class); + $suite->importPage('FrontOffice\Home', globals: ['PATCH_VERSION' => '10.0.0']); + } + + public function test_an_unsupported_old_version_is_a_clear_error(): void + { + $suite = new class (loadGlobals: true, getBrowser: false) extends TestsSuite { + protected $psVersion = '8.1.0'; + }; + + $this->expectException(\PrestaFlow\Library\Exceptions\InvalidVersionException::class); + $this->expectExceptionMessage('non prise en charge'); + $suite->importPage('FrontOffice\Home', globals: ['PATCH_VERSION' => '1.6.1.24']); + } } From 301c129cf084596c1156796e51aa2eaa55647448 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 15:06:30 +0200 Subject: [PATCH 09/11] refactor(visual): le tag lit la version de la page, sans contournement Co-Authored-By: Claude Opus 5.5 --- src/Pages/CommonPage.php | 7 ++----- tests/Unit/Visual/VisualCheckpointTagTest.php | 9 ++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Pages/CommonPage.php b/src/Pages/CommonPage.php index 12c833a..13adb76 100644 --- a/src/Pages/CommonPage.php +++ b/src/Pages/CommonPage.php @@ -717,13 +717,10 @@ public function visualCheckpoint(string $name, ?string $selector = null, ?float $legacyRatio = $threshold !== null && $maxDiffPixels === null; $budget = $legacyRatio ? null : max(0, $maxDiffPixels ?? self::DEFAULT_MAX_DIFF_PIXELS); - // globals PS_VERSION d'abord (vérité de la suite courante ; le cache statique - // de Version peut venir d'une suite précédente du worker), puis le cache. - // '1.7' / '1.6' sont des majeures valides (auparavant rejetées → « v? »). + // Majeure de cette page ; '1.7' / '1.6' sont des majeures valides + // (auparavant rejetées → « v? »). $rawMajorVersion = $this->getMajorVersion(); $majorVersion = \PrestaFlow\Library\Visual\VisualTag::majorFromVersion( - is_string($this->globals['PS_VERSION'] ?? null) ? $this->globals['PS_VERSION'] : null - ) ?? \PrestaFlow\Library\Visual\VisualTag::majorFromVersion( is_scalar($rawMajorVersion) ? (string) $rawMajorVersion : null ); diff --git a/tests/Unit/Visual/VisualCheckpointTagTest.php b/tests/Unit/Visual/VisualCheckpointTagTest.php index 63469a5..1e5a80d 100644 --- a/tests/Unit/Visual/VisualCheckpointTagTest.php +++ b/tests/Unit/Visual/VisualCheckpointTagTest.php @@ -122,10 +122,13 @@ public function testPs17MajorVersionIsNotRenderedAsQuestionMark(): void $this->assertSame('auto-v1.7-1280x720-fr', TestsSuite::$visualResults[0]['tag']); } - public function testMajorVersionDerivesFromGlobalsPsVersionFirst(): void + public function testMajorVersionComesFromThisPageOnly(): void { - // Le cache statique de version peut être périmé (suite précédente du worker). - $page = $this->makePage(['PS_VERSION' => '8.1.0'], '1.7'); + // makePage() construit la page avec patchVersion '8.1.0'. Une autre + // page passée en 1.7 ensuite ne doit pas changer son tag. + $page = $this->makePage([], null); + $other = $this->makePage([], null); + $other->setMajorVersion('1.7'); $page->visualCheckpoint('home'); From 2c160612fd12dcdec20e064024c69775fa6d048f Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 15:08:41 +0200 Subject: [PATCH 10/11] =?UTF-8?q?docs(plan):=20t=C3=A2ches=20de=20la=20ver?= =?UTF-8?q?sion=20par=20instance=20termin=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...6-10-02-version-per-instance.md.tasks.json | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json b/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json index 31670a2..5f4f692 100644 --- a/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json +++ b/docs/superpowers/plans/2026-10-02-version-per-instance.md.tasks.json @@ -4,37 +4,46 @@ { "id": 1, "subject": "Task 1: les pages prennent leur version à la construction", - "status": "pending", + "status": "completed", "description": "**Goal:** initVersion() en tête des constructeurs CommonPage/BackOfficePage/FrontOfficePage ; InvalidVersionException importée dans Traits\\Version.\n\n```json:metadata\n{\"files\": [\"src/Traits/Version.php\", \"src/Pages/CommonPage.php\", \"src/Pages/BackOfficePage.php\", \"src/Pages/FrontOfficePage.php\", \"tests/Unit/Pages/PageVersionInitTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Pages/PageVersionInitTest.php\", \"acceptanceCriteria\": [\"patchVersion 1.7.8.11 -> majeure 1.7\", \"PS_VERSION des globals si patchVersion vide\", \"sans version -> InvalidVersionException\", \"8.1.10 -> InvalidVersionException, pas classe introuvable\", \"suite complète verte en 8.4\"]}\n```" }, { "id": 2, "subject": "Task 2: la version devient une propriété d'instance", - "status": "pending", - "blockedBy": [1], + "status": "completed", + "blockedBy": [ + 1 + ], "description": "**Goal:** suppression de public static $versions, plus de repli sur '8', tests adaptés.\n\n```json:metadata\n{\"files\": [\"src/Traits/Version.php\", \"tests/Unit/Pages/PageVersionIsolationTest.php\", \"tests/Unit/Traits/VersionOverrideTest.php\", \"tests/Unit/Resolvers/TranslationsCatalogTest.php\", \"tests/Unit/Pages/BackOfficeGoToMenuTest.php\"], \"verifyCommand\": \"\\\"$HOME/Library/Application Support/Herd/bin/php81\\\" vendor/bin/phpunit tests/Unit/Pages/PageVersionIsolationTest.php\", \"acceptanceCriteria\": [\"deux pages BO gardent leur majeure\", \"BO 8.1 puis FO 9.0 isolées en PHP 8.1\", \"aucun ::$versions\", \"getMajorVersion sans version lève\", \"setVersions ne garde que 3 clés\", \"suite verte 8.4 et 8.1\"]}\n```" }, { "id": 3, "subject": "Task 3: transmission explicite (scénario, import de page)", - "status": "pending", - "blockedBy": [2], + "status": "completed", + "blockedBy": [ + 2 + ], "description": "**Goal:** Scenario reprend le tableau de versions de la suite et analyse PATCH_VERSION ; importPage ne recopie plus les versions.\n\n```json:metadata\n{\"files\": [\"src/Scenarios/Scenario.php\", \"src/Traits/ImportPage.php\", \"tests/Unit/Scenarios/ScenarioVersionTest.php\", \"tests/Unit/Tests/SuiteVersionWiringTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Scenarios/ScenarioVersionTest.php tests/Unit/Tests/SuiteVersionWiringTest.php\", \"acceptanceCriteria\": [\"scénario sur suite 1.7.8.11 -> 1.7\", \"PATCH_VERSION analysé en tableau\", \"scénario isolé de la suite\", \"deux suites importent v7 puis v9\"]}\n```" }, { "id": 4, "subject": "Task 4: la capture visuelle lit la version de sa page", - "status": "pending", - "blockedBy": [2], + "status": "completed", + "blockedBy": [ + 2 + ], "description": "**Goal:** retrait du contournement globals d'abord dans visualCheckpoint(), test Tag remplacé.\n\n```json:metadata\n{\"files\": [\"src/Pages/CommonPage.php\", \"tests/Unit/Visual/VisualCheckpointTagTest.php\"], \"verifyCommand\": \"php vendor/bin/phpunit tests/Unit/Visual/\", \"acceptanceCriteria\": [\"tag issu de la version de la page\", \"1.7 -> auto-v1.7\", \"plus de commentaire cache statique\"]}\n```" }, { "id": 5, "subject": "Task 5: vérification sur PHP 8.1 à 8.4", - "status": "pending", - "blockedBy": [3, 4], + "status": "completed", + "blockedBy": [ + 3, + 4 + ], "description": "**Goal:** suite verte sous PHP 8.1/8.2/8.3/8.4 et en ordre aléatoire ; corrections de tests factices seulement.\n\n```json:metadata\n{\"files\": [], \"verifyCommand\": \"php vendor/bin/phpunit (x 4 binaires)\", \"acceptanceCriteria\": [\"vert 8.1-8.4\", \"ordre aléatoire 3 graines vert en 8.1\", \"aucun ::$versions\"]}\n```" } ], - "lastUpdated": "2026-10-02T15:00:00+02:00" -} + "lastUpdated": "2026-10-02T15:08:41+02:00" +} \ No newline at end of file From c5dbfb0627eea3e379142b431b11d793e8846e67 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 2 Oct 2026 15:13:32 +0200 Subject: [PATCH 11/11] =?UTF-8?q?fix(version):=20initVersion=20prot=C3=A9g?= =?UTF-8?q?=C3=A9e,=20message=20d'import=20plus=20juste,=20spec=20=C3=A0?= =?UTF-8?q?=20jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../2026-10-02-version-per-instance-design.md | 18 +++++++++++++++++- src/Pages/CommonPage.php | 2 +- src/Traits/ImportPage.php | 2 +- tests/Unit/Pages/PageVersionIsolationTest.php | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/specs/2026-10-02-version-per-instance-design.md b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md index 93b6ab8..ec1bb18 100644 --- a/docs/superpowers/specs/2026-10-02-version-per-instance-design.md +++ b/docs/superpowers/specs/2026-10-02-version-per-instance-design.md @@ -1,7 +1,7 @@ # Version de PrestaShop portée par chaque objet : design Date : 2026-10-02 -Statut : validé en conversation, en attente de relecture +Statut : implémenté (branche fix/version-per-instance) ## Problème @@ -116,3 +116,19 @@ La version de PrestaShop (majeure, mineure, patch) vit dans un attribut - Élargir les formats de version acceptés (ex. `8.1.10`). - Revoir `resolveVersion()` et son défaut `'8.1.0'`. + +## Notes d'implémentation + +Écarts avec ce qui précède, décidés pendant l'implémentation : + +- **Suites sans globals** : `TestsSuite::__construct(loadGlobals: false)` appelle + `resolveVersion()`. Sans cela, `before()` → `isVersionSupported()` dépendait + du repli sur `'8'`. +- **Analyseur unique** : `Version::parseVersions()` (statique) découpe une version ; + `exctractVersions()` et `importPage()` s'en servent. Un échec ne modifie plus + l'état de l'objet. +- **`importPage()`** : le namespace `vX` et la version de la page viennent du même + patch (`globals['PATCH_VERSION']`, sinon celui de l'appelant). Une classe de page + introuvable lève `InvalidVersionException`. +- **`initVersion()`** est `protected` : l'éditeur de l'app liste les méthodes + publiques des pages comme actions. diff --git a/src/Pages/CommonPage.php b/src/Pages/CommonPage.php index 13adb76..4fed47b 100644 --- a/src/Pages/CommonPage.php +++ b/src/Pages/CommonPage.php @@ -68,7 +68,7 @@ public function __construct(string $locale, string $patchVersion, array $globals * PS_VERSION des globals. Appelée en tête des constructeurs, avant que * getSelectors()/getMessages() ne choisissent les fichiers par majeure. */ - public function initVersion(string $patchVersion, array $globals): void + protected function initVersion(string $patchVersion, array $globals): void { $version = $patchVersion !== '' ? $patchVersion : ($globals['PS_VERSION'] ?? null); diff --git a/src/Traits/ImportPage.php b/src/Traits/ImportPage.php index ed5c649..72a0f4f 100644 --- a/src/Traits/ImportPage.php +++ b/src/Traits/ImportPage.php @@ -36,7 +36,7 @@ public function importPage($pageName, $userAgent = 'PrestaFlow', $globals = null if (!class_exists($pageClass)) { throw new InvalidVersionException( - 'Version PrestaShop non prise en charge pour ' . $pageName . ' (' . $patchVersion . ') : ' . $pageClass . ' introuvable.' + 'Page ' . $pageName . ' introuvable pour PrestaShop ' . $patchVersion . ' (' . $pageClass . ') : version non prise en charge ou nom de page erroné.' ); } diff --git a/tests/Unit/Pages/PageVersionIsolationTest.php b/tests/Unit/Pages/PageVersionIsolationTest.php index 12ec362..bb0f5fa 100644 --- a/tests/Unit/Pages/PageVersionIsolationTest.php +++ b/tests/Unit/Pages/PageVersionIsolationTest.php @@ -60,6 +60,7 @@ public function test_set_versions_keeps_only_the_three_known_keys(): void $object->getVersions() ); } + public function test_parse_versions_splits_a_patch_version(): void { // Appelé via une classe qui utilise le trait : appeler un statique de