Tests E2E Playwright & Déploiement en Production

Bootcode Hub — Tester, Builder, Conteneuriser, Déployer

Utilisez les flèches, cliquez ou glissez pour naviguer

Objectifs de la leçon

1. Tests E2E Playwright

Écrire au moins 5 tests couvrant les parcours critiques

2. Build de production Vite

Optimisé, minifié, code-splitté

3. Docker + Nginx

Conteneuriser et servir le frontend

4. Variables d'environnement

Configurer par environnement (dev, staging, prod)

5. Pipeline CI/CD

GitHub Actions : lint → test → build → deploy

6. Monitoring basique

Error reporting, analytics, Lighthouse CI

Plan du cours

1

Pyramide des tests

Unit → Integration → E2E — pourquoi les E2E sont essentiels

2

Playwright

Installation, premiers tests, sélecteurs, assertions

3

Build de production Vite

Code splitting, tree shaking, compression

4

Docker + Nginx

Conteneuriser le frontend, servir les fichiers statiques

5

Variables d'environnement & GitHub Actions

.env, VITE_*, pipeline CI/CD automatisé

6

Monitoring basique

Error reporting, analytics, Lighthouse CI

La Pyramide des Tests

Unit → Integration → E2E

Pourquoi les tests E2E sont indispensables

La pyramide des tests

E2E

Peu nombreux, lents, haute valeur

Intégration

Quantité moyenne, vitesse moyenne

Unitaires

Nombreux, rapides, ciblés

Unitaires

Une fonction isolée

Ex: formatDate() retourne le bon format

Intégration

Plusieurs modules ensemble

Ex: Le composant LoginForm appelle l'API

E2E

Le parcours complet utilisateur

Ex: Login → créer un projet → voir le dashboard

Pourquoi les tests E2E sont importants

❌ Sans E2E

Tous les tests unitaires passent…

mais le bouton "S'inscrire" ne fonctionne pas en prod

✅ Avec E2E

On teste ce que l'UTILISATEUR fait réellement

Le parcours complet est validé avant le déploiement

💡 La règle du 80/20

Tester les 3 à 5 parcours critiques couvre 80% de la valeur

Login, inscription, CRUD principal, checkout, navigation clé

⚠️ Piège : Trop de tests E2E

❌ Anti-pattern

50 tests E2E qui couvrent tout

→ Suite de tests lente (20+ minutes)

→ Tests fragiles qui cassent souvent

→ Maintenance coûteuse

✅ Bonne pratique

5-10 tests E2E sur les parcours critiques

→ Rapide (2-5 minutes)

→ Tests stables et fiables

→ Haute valeur ajoutée

💡 Complétez avec des tests unitaires et d'intégration pour le reste

Playwright

Le framework de tests E2E moderne

Installation, sélecteurs, assertions, premiers tests

Pourquoi Playwright ?

🌐 Multi-navigateurs

Chrome, Firefox, Safari — un seul framework

⚡ Auto-wait

Attend automatiquement que les éléments soient prêts

🔍 Trace Viewer

Debugger visuel avec screenshots et timeline

🎯 Codegen

Générer des tests en enregistrant vos actions

Installation de Playwright

// 1️⃣ Installer Playwright

npm init playwright@latest

// 2️⃣ Structure générée

📁 tests/

📄 example.spec.ts

📄 playwright.config.ts

// 3️⃣ Lancer les tests

npx playwright test

npx playwright test --ui

Configuration : playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({

testDir: './e2e',

baseURL: 'http://localhost:5173',

use: {

screenshot: 'only-on-failure',

trace: 'on-first-retry',

},

webServer: {

command: 'npm run dev',

port: 5173,

reuseExistingServer: true,

},

});

💡 webServer démarre automatiquement votre serveur dev avant les tests

Sélecteurs : Trouver les éléments

✅ Recommandé — Résistants aux changements

page.getByRole('button', { name: 'Se connecter' })

page.getByText('Bienvenue')

page.getByTestId('login-form')

page.getByLabel('Email')

page.getByPlaceholder('Entrez votre email')

❌ Éviter — Fragile, casse au moindre refactor CSS

page.locator('.btn-primary') // class CSS

page.locator('#submit-btn') // ID fragile

page.locator('div > form > button:nth-child(2)') // Xpath/CSS complexe

Assertions : Vérifier le résultat

// Vérifier la visibilité

await expect(page.getByText('Dashboard')).toBeVisible();

// Vérifier l'URL

await expect(page).toHaveURL('/dashboard');

// Vérifier le titre

await expect(page).toHaveTitle(/Bootcode Hub/);

// Vérifier le contenu d'un input

await expect(page.getByLabel('Email')).toHaveValue('[email protected]');

// Vérifier le nombre d'éléments

await expect(page.getByRole('listitem')).toHaveCount(5);

💡 Toutes les assertions Playwright font de l'auto-wait — elles réessayent automatiquement jusqu'au timeout

Premier test : Navigation

import { test, expect } from '@playwright/test';

test('la page d\'accueil se charge correctement', async ({ page }) => {

await page.goto('/');

// Vérifier le titre

await expect(page).toHaveTitle(/Bootcode Hub/);

// Vérifier qu'un élément clé est visible

await expect(

page.getByRole('heading', { name: 'Bienvenue' })

).toBeVisible();

});

Les 5 tests critiques du Bootcode Hub

🔐 Test 1 : Inscription

Remplir le formulaire → soumettre → redirection vers le dashboard

🔑 Test 2 : Connexion

Email + mot de passe → login → voir le dashboard

📝 Test 3 : Créer un projet

Connecté → cliquer "Nouveau" → remplir → voir dans la liste

🗑️ Test 4 : Supprimer un projet

Sélectionner → confirmer suppression → vérifier disparition

🚪 Test 5 : Déconnexion

Cliquer "Déconnexion" → retour login → plus d'accès protégé

Test E2E : Connexion complète

test('l\'utilisateur peut se connecter', async ({ page }) => {

await page.goto('/login');

// Remplir le formulaire

await page.getByLabel('Email').fill('[email protected]');

await page.getByLabel('Mot de passe').fill('password123');

// Soumettre

await page.getByRole('button', { name: 'Se connecter' }).click();

// Vérifier la redirection

await expect(page).toHaveURL('/dashboard');

await expect(page.getByText('Bienvenue')).toBeVisible();

});

✅ Ce test vérifie le PARCOURS complet : navigation → saisie → action → résultat

Outils de développement Playwright

🎬 Codegen

Enregistrer ses actions et générer le code

npx playwright codegen

localhost:5173

🐛 Debug mode

Exécuter pas à pas avec l'inspecteur

npx playwright test

--debug

📊 UI mode

Interface visuelle pour lancer et observer

npx playwright test --ui

🔍 Trace Viewer

Analyser une trace après un échec

npx playwright show-trace

trace.zip

Build de Production Vite

Optimiser, minifier, découper

Le build de prod est DIFFÉRENT du dev

Dev vs Production

🛠️ Développement

npm run dev

→ Hot Module Replacement (HMR)

→ Source maps détaillées

→ Pas de minification

→ Code non optimisé

🚀 Production

npm run build

→ Minification (code compact)

→ Tree shaking (code mort éliminé)

→ Code splitting (chargement à la demande)

→ Fichiers hachés pour le cache

⚠️ Piège : Ne JAMAIS utiliser npm run dev en production — c'est lent et non sécurisé

Les optimisations de Vite

🌳 Tree Shaking

Élimine le code mort — les fonctions importées mais jamais utilisées sont supprimées

import { format } from 'date-fns'; // Seul format est inclus

✂️ Code Splitting

Découpe l'app en chunks — chaque page est chargée à la demande

const Dashboard = lazy(() => import('./pages/Dashboard'));

📦 Minification + Compression

Variables raccourcies, espaces supprimés, gzip/brotli pour réduire la taille

// 500 KB → 120 KB → 35 KB (gzip)

Lancer le build de production

// Construire pour la production

npm run build

// Résultat dans dist/

📁 dist/

📄 index.html

📁 assets/

📄 index-a1b2c3.js ← hash pour le cache

📄 index-d4e5f6.css

📄 vendor-g7h8i9.js ← librairies séparées

// Prévisualiser le build localement

npm run preview

⚠️ Piège : Toujours tester le build localement avant de déployer — des erreurs n'apparaissent qu'en prod

Docker + Nginx

Conteneuriser le frontend pour la production

Nginx est le standard pour servir du frontend — pas le dev server Vite

Pourquoi Docker ?

📦 Isolation

Chaque app dans son propre conteneur, pas de conflits

🔄 Reproductibilité

"Ça marche sur ma machine" → ça marche PARTOUT

🚀 Déploiement simple

Un conteneur = un docker run et c'est en prod

📐 Scalabilité

Facile à dupliquer avec Kubernetes ou Docker Swarm

Dockerfile multi-stage

# 1️⃣ Étape Build

FROM node:20-alpine AS build

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

# 2️⃣ Étape Production

FROM nginx:alpine

COPY --from=build /app/dist /usr/share/nginx/html

COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

✅ Multi-stage = image finale légère (~25 MB) sans node_modules

Configuration Nginx pour SPA

server {

listen 80;

root /usr/share/nginx/html;

index index.html;

# Gzip compression

gzip on;

gzip_types text/css application/javascript;

# Cache des assets hachés (1 an)

location /assets/ {

expires 1y;

add_header Cache-Control "public, immutable";

}

# ⚠️ Fallback SPA — CRITIQUE

location / {

try_files $uri $uri/ /index.html;

}

}

⚠️ Piège : Sans try_files → index.html, un refresh sur /dashboard donne une erreur 404

Construire et lancer le conteneur

// 1️⃣ Construire l'image

docker build -t bootcode-hub .

// 2️⃣ Lancer le conteneur

docker run -d -p 80:80 bootcode-hub

// 3️⃣ Vérifier que ça tourne

docker ps

curl http://localhost

Variables d'Environnement

.env, VITE_*, config par environnement

Les variables VITE_* sont injectées au BUILD TIME, pas au runtime

Les fichiers .env

.env

Valeurs par défaut

VITE_API_URL=

http://localhost:3000

.env.staging

Environnement de test

VITE_API_URL=

https://api.staging.com

.env.production

Environnement de prod

VITE_API_URL=

https://api.bootcode.com

// Utiliser dans le code

const apiUrl = import.meta.env.VITE_API_URL;

// Builder pour un environnement spécifique

npx vite build --mode staging

VITE_* : Build Time vs Runtime

✅ Exposées au client

Préfixées par VITE_

VITE_API_URL=https://api.com

VITE_APP_TITLE=Bootcode Hub

🔒 Jamais exposées

Sans le préfixe VITE_

DATABASE_URL=postgres://...

JWT_SECRET=super_secret

⚠️ Pièges critiques

❌ Committer un .env avec des secrets → fuite de données

❌ Mettre un secret dans VITE_* → visible dans le bundle JS côté client

❌ Changer VITE_* après le build → aucun effet, il faut re-builder

Sécuriser les variables

❌ Ne JAMAIS committer

# .gitignore

.env

.env.local

.env.production

✅ Committer un template

# .env.example

VITE_API_URL=

VITE_APP_TITLE=

DATABASE_URL=

💡 L'équipe clone le repo → copie .env.example en .env → remplit les valeurs

GitHub Actions

Pipeline CI/CD automatisé

Merge → Tests → Build → Deploy. Zéro intervention manuelle

CI/CD : C'est quoi ?

CI — Continuous Integration

À chaque push :

1️⃣ Lint (vérifier le style)

2️⃣ Tests (unitaires + E2E)

3️⃣ Build (vérifier que ça compile)

CD — Continuous Deployment

Après CI réussi :

4️⃣ Build de l'image Docker

5️⃣ Push vers le registre

6️⃣ Déploiement en production

💡 CI/CD = confiance. Chaque merge est automatiquement testé et déployé

Pipeline CI : .github/workflows/ci.yml

name: CI Pipeline

on: [push, pull_request]

jobs:

ci:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with: { node-version: 20 }

- run: npm ci

- run: npm run lint

- run: npm run test

- run: npm run build

💡 npm ci au lieu de npm install — install propre et reproductible depuis le lockfile

Ajouter les tests E2E au pipeline

# Job E2E séparé dans le même workflow

e2e:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with: { node-version: 20 }

- run: npm ci

- run: npx playwright install --with-deps

- run: npx playwright test

- uses: actions/upload-artifact@v4

if: failure()

with:

name: playwright-report

path: playwright-report/

💡 upload-artifact sauvegarde le rapport Playwright en cas d'échec — indispensable pour débugger en CI

Pipeline CD : Build Docker & Deploy

deploy:

needs: [ci, e2e]

if: github.ref == 'refs/heads/main'

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- run: |

docker build -t bootcode-hub .

docker tag bootcode-hub registry/bootcode-hub:${{ github.sha }}

docker push registry/bootcode-hub:${{ github.sha }}

needs: [ci, e2e] — le deploy ne se lance que si CI + E2E passent

💡 github.sha — chaque image est taguée avec le commit pour la traçabilité

Le pipeline complet

Push

git push

Lint

eslint

Tests

vitest

E2E

playwright

Build

vite build

Deploy

docker

Si une étape échoue, le pipeline s'arrête

Le code défectueux n'arrive JAMAIS en production

Secrets dans GitHub Actions

# Dans le workflow YAML

env:

VITE_API_URL: ${{ secrets.VITE_API_URL }}

DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}

Configuration dans GitHub :

1️⃣ Repo → Settings → Secrets and variables → Actions

2️⃣ New repository secret

3️⃣ Ajouter chaque variable sensible

⚠️ Les secrets sont masqués dans les logs — jamais visibles en clair dans le pipeline

Monitoring Basique

Détecter les erreurs AVANT les utilisateurs

Error reporting, analytics, Lighthouse CI

Pourquoi monitorer ?

❌ Sans monitoring

→ Un utilisateur tweete "votre site est cassé"

→ Vous découvrez le bug 3 jours plus tard

→ Pas de contexte pour reproduire

✅ Avec monitoring

→ Alerte Slack en 30 secondes

→ Stack trace + navigateur + URL

→ Fix déployé avant que l'utilisateur le remarque

Error Reporting : Sentry

// 1️⃣ Installer Sentry

npm install @sentry/react

// 2️⃣ Initialiser dans main.tsx

import * as Sentry from '@sentry/react';

Sentry.init({

dsn: import.meta.env.VITE_SENTRY_DSN,

environment: import.meta.env.MODE,

tracesSampleRate: 0.1,

});

✅ Sentry capture automatiquement : erreurs JS, requêtes réseau échouées, performance

Lighthouse CI : Performance automatisée

# Ajouter au pipeline GitHub Actions

- name: Lighthouse CI

uses: treosh/lighthouse-ci-action@v12

with:

urls: |

http://localhost/

http://localhost/login

budgetPath: ./budget.json

90+

Performance

90+

Accessibilité

90+

Best Practices

90+

SEO

💡 Lighthouse CI bloque le déploiement si les scores descendent en dessous du seuil défini

Analytics et métriques

📊 Web Vitals

Mesurer les Core Web Vitals en production

import { onCLS, onLCP }

from 'web-vitals';

onCLS(console.log);

onLCP(console.log);

🏥 Health Check

Endpoint simple pour vérifier que l'app est en vie

# Vérifier toutes les minutes

curl -f http://prod/health

# Alerter si 3 échecs

💡 Le monitoring détecte les erreurs en prod AVANT que les utilisateurs ne signalent

À retenir !

🎯 Les tests E2E testent le PARCOURS complet, pas un composant isolé

📊 Tester les 3-5 parcours critiques couvre 80% de la valeur

🚀 Le build de prod est DIFFÉRENT du dev : minifié, code-splitté, tree-shaké

🐳 Nginx est le standard pour servir du frontend en prod — pas le dev server Vite

🔐 Les variables VITE_* sont injectées au BUILD TIME, pas au runtime

⚙️ Le pipeline CI/CD automatise : merge → tests → deploy. Zéro intervention

⚠️ Pièges courants

Trop de tests E2E = tests lents et fragiles

→ Focus sur les parcours critiques uniquement

Sélectionner par class CSS → tests fragiles

→ Utiliser data-testid ou getByRole

Utiliser le dev server Vite en production

→ Lent, pas sécurisé — toujours npm run build + Nginx

Oublier le fallback SPA dans Nginx

try_files $uri $uri/ /index.html sinon erreur 404 au refresh

Committer les .env avec des secrets

→ Utiliser .gitignore + .env.example + GitHub Secrets

Ne pas tester le build de prod localement

→ Toujours npm run build && npm run preview avant de déployer

Questions ?

Tests E2E + Déploiement = Confiance en production

Playwright pour tester, Vite pour builder, Docker + Nginx pour servir

GitHub Actions pour tout automatiser