Bootcode Hub — Tester, Builder, Conteneuriser, Déployer
Utilisez les flèches, cliquez ou glissez pour naviguer
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
Pyramide des tests
Unit → Integration → E2E — pourquoi les E2E sont essentiels
Playwright
Installation, premiers tests, sélecteurs, assertions
Build de production Vite
Code splitting, tree shaking, compression
Docker + Nginx
Conteneuriser le frontend, servir les fichiers statiques
Variables d'environnement & GitHub Actions
.env, VITE_*, pipeline CI/CD automatisé
Monitoring basique
Error reporting, analytics, Lighthouse CI
Unit → Integration → E2E
Pourquoi les tests E2E sont indispensables
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
❌ 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é
❌ 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
Le framework de tests E2E moderne
Installation, sélecteurs, assertions, premiers tests
🌐 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
// 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
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
✅ 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
// 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
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();
});
🔐 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('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
🎬 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
Optimiser, minifier, découper
Le build de prod est DIFFÉRENT du dev
🛠️ 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é
🌳 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)
// 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
Conteneuriser le frontend pour la production
Nginx est le standard pour servir du frontend — pas le dev server Vite
📦 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
# 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
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
// 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
.env, VITE_*, config par environnement
Les variables VITE_* sont injectées au BUILD TIME, pas au runtime
.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
✅ 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
❌ 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
Pipeline CI/CD automatisé
Merge → Tests → Build → Deploy. Zéro intervention manuelle
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é
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
# 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
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é
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
# 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
Détecter les erreurs AVANT les utilisateurs
Error reporting, analytics, Lighthouse CI
❌ 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
// 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
# 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
📊 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
🎯 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
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
Tests E2E + Déploiement = Confiance en production
Playwright pour tester, Vite pour builder, Docker + Nginx pour servir
GitHub Actions pour tout automatiser