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

179 lines
6.5 KiB
Markdown

# 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) :
```bash
pnpm dev:up # spin up Postgres + Redis + Mailpit + MinIO
```
2. **Setup de la DB de test** :
```bash
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) :
```bash
pnpm exec playwright install chromium
```
---
## Lancement
```bash
# 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](./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 :
```ts
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 :
```ts
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 :
```ts
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) :
```yaml
# .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.