rubis/docs/tech/e2e-tests.md
ordinarthur 59f81879d8
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 1m39s
Build & Deploy API / build-and-deploy (push) Successful in 2m30s
Build & Deploy Web / build-and-deploy (push) Successful in 1m21s
test(e2e): tests Playwright multi-stack — vrai navigateur, DB isolée, Stripe mocké
Ajoute une couche end-to-end où un Chromium drive la SPA + API ensemble
contre une DB Postgres séparée, avec Stripe entièrement mocké au niveau
API. 6 scénarios couverts (signup + onboarding + 4 sur le billing trial).

Architecture :
  - DB `rubis_test_e2e` séparée, TRUNCATE entre tests (~50 ms reset)
  - Routes test-only `/__test__/*` gated par NODE_ENV=test_e2e
    (reset, install Stripe mock, fire webhook, lire org state, last-org)
  - Stripe mocké via __setStripeForTests — pas d'appel réseau
  - Playwright spawn API + SPA automatiquement (webServer config)
  - CORS étendu à test_e2e pour le cross-origin localhost:5173 → :3333

Scénarios :
  - signup.spec.ts : signup → onboarding 3 étapes → dashboard (assert rubis hero)
  - billing-trial.spec.ts :
      • démarrer essai 14j → redirect Stripe Checkout (mock)
      • fallback Free 2 factures continue l'onboarding
      • webhook checkout.completed → org en trialing + trial_ends_at
      • retour ?trial=cancel après abandon
      • inspection DB : stripeCustomerId posé après start-trial

Scripts :
  - pnpm e2e          (headless)
  - pnpm e2e:headed   (Chromium visible)
  - pnpm e2e:ui       (mode interactif Playwright)
  - pnpm e2e:setup    (crée + migre rubis_test_e2e via docker exec)

Documentation : docs/tech/e2e-tests.md — architecture, scénarios,
extensions, CI, troubleshooting.

Limites assumées :
  - L'UI Stripe Checkout (3DS, formulaire CB) n'est pas testée — externe.
    Pour ça : playbook manuel docs/tech/stripe-trial-e2e-playbook.md.
  - Le rendu du banner "Essai Pro" n'est pas asserté en E2E à cause de
    TanStack Query staleTime — couvert par les tests vitest à la place.

État global du chantier billing : 127 tests japa + 6 Playwright + 11
vitest = couverture multi-niveaux.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:58:51 +02:00

6.5 KiB

Tests E2E Playwright

Version : 0.1 · Dernière maj : 2026-05-18 Stack : Playwright + Chromium · DB séparée rubis_test_e2e · Stripe mocké

Tests end-to-end qui simulent un vrai user interagissant avec la SPA + landing dans un Chromium headless (ou headed). Validation complète de la chaîne navigation, formulaires, redirections, hydratation Tanstack, fetch API, persistence DB.

Complémentaires aux tests :

  • Unit (apps/api/tests/unit/) : logique pure, fonctions isolées.
  • Functional HTTP (apps/api/tests/functional/) : routes API via japa ApiClient.
  • Vitest SPA (apps/web/src/**/*.test.tsx) : hooks + composants isolés.
  • E2E Playwright (e2e/tests/) ← vous êtes ici. Navigateur réel, full stack.

Pré-requis (une fois)

  1. PostgreSQL local accessible (généralement via docker-compose dev) :

    pnpm dev:up   # spin up Postgres + Redis + Mailpit + MinIO
    
  2. Setup de la DB de test :

    pnpm e2e:setup
    

    Crée rubis_test_e2e (si absente) et applique toutes les migrations Adonis.

  3. Chromium Playwright (déjà fait si tu as pnpm install après ce commit) :

    pnpm exec playwright install chromium
    

Lancement

# Headless (CI / dev rapide)
pnpm e2e

# Headed (regarder le navigateur en action — debug)
pnpm e2e:headed

# UI interactive (mode dev Playwright, idéal pour itérer)
pnpm e2e:ui

Playwright spawn lui-même l'API Adonis (port 3333) et la SPA Vite (port 5173) en mode test_e2e avant de lancer les tests. Pas besoin de pnpm dev à part. Si tu as déjà ton stack qui tourne, set E2E_SKIP_WEBSERVER=1.

Variables d'env

Variable Default Rôle
E2E_PG_DB_NAME rubis_test_e2e Nom de la DB de test
E2E_WEB_URL http://localhost:5173 Base URL SPA
E2E_API_URL http://localhost:3333 Base URL API (pour les helpers reset/mock)
E2E_SKIP_WEBSERVER unset Si défini, Playwright n'essaie pas de spawn API+SPA
CI unset Active retries + reporter github + traces

Architecture

Stripe mocké côté API

Le SDK Stripe est remplacé par un mock déterministe via __setStripeForTests (cf. apps/api/app/services/stripe.ts). En NODE_ENV=test_e2e, les endpoints suivants sont exposés (gated, 404 sinon) :

Endpoint Rôle
POST /__test__/reset TRUNCATE des tables applicatives + ré-install mock par défaut
POST /__test__/stripe/mock Switch de scenario (trial_happy, trial_decline)
POST /__test__/stripe/webhook Simule un event Stripe sans signer manuellement
GET /__test__/state/org/:id Inspection directe DB d'une org

Ces endpoints sont invisibles en prod : le contrôleur throw 404 si NODE_ENV !== 'test_e2e'. Aucun risque de fuite.

DB de test isolée

rubis_test_e2e est physiquement séparée de rubis (dev). Chaque test commence par await resetDb() qui TRUNCATE les tables. Aucune pollution croisée.

Le TRUNCATE CASCADE prend ~50 ms ; bien plus rapide qu'un drop/recreate. Tests séquentiels (workers: 1) pour ne pas avoir 2 truncates en parallèle.

Mocking Stripe au niveau navigateur ?

Non. On mocke côté API, pas côté navigateur. Pourquoi : la SPA appelle de toute façon notre API, et notre API appelle Stripe. Mocker côté navigateur (via Playwright route()) ne couvrirait que les appels directs SPA → Stripe, ce qui n'existe pas dans notre archi (tout passe par l'API).

L'avantage : on teste l'intégralité de notre code applicatif comme en prod, seul Stripe est rempl.

Limites — ce qui n'est PAS testé

  • L'UI Stripe Checkout (3DS challenge, formulaire CB) — externe à notre app, mocké. Pour valider : utiliser le playbook manuel stripe-trial-e2e-playbook.md avec Stripe Test Clocks.
  • Le prélèvement réel à J+14 — nécessite Stripe Test Clocks + advance, hors scope automation.
  • Les emails sortants — capturés par Mailpit en dev, mais les tests E2E ne vérifient pas le rendu HTML pixel-perfect (le visual regression viendrait en V2).

Scénarios couverts (V1)

Fichier Scénarios
tests/signup.spec.ts Signup + onboarding 3 étapes → dashboard
tests/billing-trial.spec.ts Démarrer essai 14j (mock Stripe), fallback Free, retour ?trial=cancel, inspection DB post-action

Chaque scénario tourne en ~3 s en headless. La suite complète : ~30 s.


Étendre

Ajouter un nouveau test : créer e2e/tests/<feature>.spec.ts qui suit le pattern :

import { test, expect } from '@playwright/test'
import { resetDb } from './helpers/api'

test.describe('Ma feature', () => {
  test.beforeEach(async () => { await resetDb() })

  test('mon scénario', async ({ page }) => {
    await page.goto('/signup')
    // ...
  })
})

Pour simuler un webhook Stripe au milieu du scénario :

import { fireStripeWebhook } from './helpers/api'

await fireStripeWebhook({
  type: 'customer.subscription.trial_will_end',
  data: { object: { customer: 'cus_e2e_mock', id: 'sub_xxx' } },
})

Pour inspecter l'état DB sans naviguer dans le SPA :

import { getOrgState } from './helpers/api'

const state = await getOrgState(orgId)
expect(state?.plan).toBe('pro')

CI

À ajouter dans le pipeline Gitea (suit le pattern de apps/api actuel) :

# .gitea/workflows/e2e.yml (à créer)
e2e:
  steps:
    - run: pnpm install
    - run: pnpm dev:up   # Postgres + Redis + Mailpit
    - run: pnpm exec playwright install --with-deps chromium
    - run: pnpm e2e:setup
    - run: pnpm e2e
    - if: failure()
      uses: actions/upload-artifact@v4
      with:
        name: playwright-report
        path: playwright-report/

Tant que la CI E2E n'est pas montée, les tests se lancent uniquement en local avant les déploiements sensibles (changement signup, billing, onboarding).


Troubleshooting

  • « could not connect to server: Connection refused »pnpm dev:up pour démarrer Postgres.
  • « Database "rubis_test_e2e" does not exist »pnpm e2e:setup.
  • « Timed out waiting for http://localhost:3333/api/v1/health » → l'API ne boot pas. Vérifier apps/api/.env (APP_KEY notamment). Lancer manuellement cd apps/api && NODE_ENV=test_e2e pnpm dev pour voir les erreurs.
  • « Stripe SDK not initialized » → l'endpoint /__test__/stripe/mock n'a pas été appelé. resetDb() le fait par défaut au début de chaque test.
  • Tests flaky → augmenter timeout dans playwright.config.ts. La SPA peut être lente sur certaines machines.