diff --git a/docs/testing.md b/docs/testing.md index f2032cd8..980e5b86 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -406,6 +406,25 @@ comme s'il s'agissait du huitième, toujours le même.** C'était trop précis, un lecteur voyant rougir une autre spec en aurait conclu à une régression. Corrigé au run du 2026-08-27, qui a rendu un huitième différent. +⚠️ **Le 2026-10-03, l'un de ces « huitièmes » s'est révélé ne PAS être de la pollution.** +`product-revenue-account.spec.ts:133` relisait la liste des produits par l'API **sans attendre** +le POST de création qu'il venait de déclencher : sous la charge de la suite complète, la relecture +partait la première et rendait une liste vide (`Expected: 6, Received: undefined`). Prouvé en +injectant un délai de 1,5 s sur le POST : l'ancien test échoue avec ce message exact, le test +corrigé passe. Corrigé (`fillProductForm` attend la réponse du serveur). *Un test qui passe rejoué +seul n'est pas forcément pollué : il peut aussi être une course que la charge révèle.* + +`products.spec.ts:166` (« doublon », bouton *Créer* resté désactivé 30 s — **KF-054, [#498]**), lui, +**n'a pas de cause établie** : ni reproduit en enchaînant les specs voisines, ni sous délai injecté des lectures ou de +la création. D'où, depuis cette date, **`trace: 'retain-on-failure'`** dans `playwright.config.ts`. + +⛔ **Avant de rejouer seul un échec, mettre `frontend/test-results/` à l'abri** (`cp -r +frontend/test-results /quelque/part/`) : Playwright vide ce répertoire au lancement, et le rejeu +efface la trace et l'instantané du run qui a échoué — c'est exactement ce qui a privé +`products.spec.ts:166` de diagnostic. + +[#498]: https://github.com/guycorbaz/kesh/issues/498 + ### Comment lire un rouge, dans l'ordre 1. **Comparer les fichiers en échec à la liste ci-dessus.** Un échec hors liste est une diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index fb4e82f0..8253fb7e 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -57,6 +57,11 @@ const config: PlaywrightTestConfig = { baseURL: process.env.KESH_BACKEND_URL ?? 'http://127.0.0.1', locale: 'fr-CH', timezoneId: 'Europe/Zurich', + // Un échec intermittent ne se diagnostique que sur sa trace : rejoué seul, + // il passe, et le rejeu ÉCRASE `test-results/` — instantané compris. Sans + // trace conservée, la cause de `products.spec.ts:166` (KF-054, #498) n'a pas + // pu être établie. Coût : de l'espace disque, sur les seuls échecs. + trace: 'retain-on-failure', }, }; diff --git a/frontend/tests/e2e/product-revenue-account.spec.ts b/frontend/tests/e2e/product-revenue-account.spec.ts index f79ba97a..bba32003 100644 --- a/frontend/tests/e2e/product-revenue-account.spec.ts +++ b/frontend/tests/e2e/product-revenue-account.spec.ts @@ -110,7 +110,16 @@ async function pickAccount( await expect(field).toHaveValue(`${account.number} — ${account.name}`); } -/** Remplit le dialogue de la fiche produit et enregistre. */ +/** + * Remplit le dialogue de la fiche produit et enregistre — **et attend la réponse + * du serveur**. + * + * ⚠️ Sans cette attente, l'appelant qui relit la liste par l'API juste après + * court contre le POST : sous la charge de la suite complète, la relecture partait + * avant la création et rendait une liste vide (`defaultRevenueAccountId` + * `undefined`). Échec intermittent, vert rejoué seul — classé à tort en + * « pollution d'état » jusqu'au 2026-10-03. + */ async function fillProductForm( page: import('@playwright/test').Page, name: string, @@ -123,7 +132,13 @@ async function fillProductForm( } // ⚠️ Le libellé dépend du mode : « Créer » à la création, « Enregistrer » à // l'édition (`+page.svelte:714-717`). La regex couvre les deux. + const saved = page.waitForResponse( + (r) => + r.url().includes('/api/v1/products') && + ['POST', 'PUT'].includes(r.request().method()) + ); await page.getByRole('button', { name: /^(Créer|Enregistrer)$/ }).click(); + expect((await saved).ok(), 'la fiche produit doit être enregistrée').toBeTruthy(); } // ===========================================================================