Changer le thème

Tests de composants Vitest : Browser Mode et intégration Playwright

Easton editorial illustration: 组件测试样品, jsdom 模拟舱, Browser 真实渲染舱, CI 覆盖率闸门

La première fois que j’ai testé un composant Canvas, j’ai pris une sacrée claque.

Le rapport affichait un « PASS » vert ; j’ai soumis le code en toute confiance. Le lendemain, un collègue ouvre la page dans un vrai navigateur — le Canvas ne s’affiche tout simplement pas. Pourtant les tests passaient !

J’ai compris ensuite : jsdom n’est qu’un « faux » navigateur. Il simule les API DOM, mais le rendu réel du Canvas, les styles CSS calculés, le cycle de vie des Web Components — tout ça, il ne peut pas le tester. Ma config de tests unitaires depuis six mois ne couvrait qu’une partie de la surface.

C’est pour ça que Vitest 3.0 a introduit le Browser Mode — exécuter les tests directement dans un vrai navigateur. Contrairement à jsdom qui simule le DOM dans Node.js, Browser Mode lance Chromium/Firefox/Safari, rend réellement les composants, puis interagit via l’API Playwright. Test vert ? Alors c’est vraiment bon.

Cet article vous guide de la configuration Browser Mode à zéro jusqu’aux tests React/Vue en conditions réelles, puis aux seuils de couverture en CI. Troisième volet de la série Vitest : les deux premiers couvraient la config unitaire et le TDD ; ici, on complète avec les tests de composants.

Pourquoi Browser Mode ?

Vous vous demandez peut-être : jsdom ne suffit pas ?

Pour des composants purement logiques — une calculatrice, un validateur de formulaire — jsdom suffit largement. C’est un simulateur DOM dans Node.js, rapide et simple. Mes deux premiers articles sur les tests unitaires tournaient entièrement en jsdom : données réactives, déclenchement d’événements, aucun souci.

Mais dans ces scénarios, jsdom faiblit :

  • Dessin Canvas : jsdom expose l’API Canvas, mais ne dessine pas vraiment. Compter les appels à ctx.fillRect() ? OK. Vérifier que le rendu est correct ? Impossible.
  • Styles CSS calculés : getComputedStyle() renvoie un objet vide en jsdom. Dans un vrai navigateur, la largeur dépend du conteneur parent, du padding, de la bordure — jsdom ne calcule rien de tout ça.
  • Web Components : connectedCallback, disconnectedCallback sont simulés, mais le timing du cycle de vie diffère du navigateur réel.
  • Rendu asynchrone : animation frames, requestIdleCallback, IntersectionObserver — jsdom ne les implémente pas ou de façon incomplète.

Exemple concret. L’an dernier, un composant utilisait une animation CSS pour ouvrir/fermer un bouton. En jsdom, l’événement transitionend ne se déclenche jamais — pas de transition réelle. J’ai mocké l’événement ; les tests passaient, mais après un changement de durée d’animation en prod, le composant était cassé.

Browser Mode résout ça. Il rend le composant dans un vrai navigateur : Chromium (ou Firefox/Safari), montage sur la page, clics, saisie, attentes via Playwright. Ce qui se passe dans le navigateur, le test le vérifie.

Un chiffre utile : le Browser Mode de Vitest 3.0 partage le contexte Chromium — le navigateur ne démarre qu’une fois, tous les tests réutilisent la même instance. Officiellement, ~30 % plus rapide qu’un E2E Playwright classique. Pour 50 composants, pas de redémarrage du navigateur à chaque test.

Côté pyramide de tests, le niveau « composants » fait débat. Pyramide classique : beaucoup d’unitaires, peu d’E2E. Le blog Vue et alexop.dev proposent l’inverse : 70 % d’intégration, 20 % d’unitaires, 10 % d’E2E — un composant combine template, styles et logique ; jsdom ne couvre que la logique. Browser Mode comble ce trou : plus fidèle que jsdom, plus léger que Playwright E2E.

Configuration Browser Mode en pratique

Configurer Browser Mode n’est pas si compliqué — mais j’ai déjà marché sur quelques pièges.

Installation des dépendances

Installez Vitest et le provider Browser Mode. Recommandation officielle : Playwright.

npm install -D vitest @vitest/browser-playwright

Playwright installe Chromium, Firefox et WebKit. Pour ne tester que Chromium (souvent suffisant) :

npx playwright install chromium

Comptez un peu de temps : le paquet Chromium fait ~170 Mo. Une fois téléchargé, vous êtes prêt.

Configuration vitest.config.ts

La config est simple ; un détail compte :

import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    browser: {
      provider: playwright(),
      enabled: true,
      instances: [{ browser: 'chromium' }],
    },
  },
})

instances choisit le navigateur. Pour la compatibilité multi-navigateurs :

instances: [
  { browser: 'chromium' },
  { browser: 'firefox' },
  { browser: 'webkit' },  // Safari
]

Je ne garde en général que Chromium — multi-navigateur ralentit, et la plupart des bugs frontend apparaissent déjà sous Chromium. Safari, je le couvre via Playwright E2E sur les parcours critiques.

Mode headless vs mode UI

Deux usages distincts :

  • Headless : pas de fenêtre visible, tests en arrière-plan. Idéal en CI, rapide, mais vous ne voyez pas le rendu.
  • UI : fenêtre ouverte, vous observez rendu, clics, saisie. Parfait en développement pour déboguer.

En dev, j’utilise le mode UI :

npx vitest --browser.ui

Vitest ouvre un workbench : liste des tests à gauche, navigateur à droite. Vous cliquez un fichier, le composant s’affiche, vous suivez l’exécution. Un bouton ne répond pas ? Déboguez directement dans le navigateur.

En CI, headless obligatoire :

browser: {
  provider: playwright(),
  enabled: true,
  headless: true,  // CI force headless
  instances: [{ browser: 'chromium' }],
}

Convention de nommage des fichiers de test

L’équipe Vitest suggère le suffixe .browser.test.ts pour distinguer du .test.ts classique. Avantages :

  • Lancer séparément tests jsdom et Browser Mode.
  • En cas d’échec CI, le nom indique tout de suite qu’il s’agit de tests navigateur.

Vitest n’impose pas ce nom ; .test.ts fonctionne aussi. L’essentiel : configurer les dossiers ou tout exécuter en Browser Mode. Ma pratique : jsdom pour l’unitaire, Browser Mode pour les composants.

Tests de composants React/Vue

C’est le cœur du Browser Mode. La syntaxe diffère un peu de Testing Library, mais on s’y habitue vite.

Tests de composants React

Installez l’adaptateur React :

npm install -D @vitest/browser-react

Exemple : composant Counter, le compteur s’incrémente au clic :

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter'

test('incrément au clic sur le bouton', async () => {
  // Monter le composant dans le navigateur
  await page.mount(<Counter />)

  // Trouver le bouton
  const button = page.getByRole('button', { name: 'Count: 0' })

  // Simuler le clic
  await userEvent.click(button)

  // Vérifier le texte mis à jour
  await expect.element(button).toHaveTextContent('Count: 1')
})

Comparaison Testing Library : render() devient page.mount(), screen.getByRole() devient page.getByRole(). Même esprit, page est le contexte Browser Mode.

Détail : await expect.element(button) — Web Testing API de Vitest, attente automatique du changement d’état. Pas besoin de await waitFor() manuel.

Tests de composants Vue

Même logique, avec l’adaptateur Vue :

npm install -D @vitest/browser-vue

Test du Counter Vue :

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter.vue'

test('incrément au clic sur le bouton', async () => {
  // Monter le composant Vue
  await page.mount(Counter)

  // Trouver, cliquer, vérifier
  const button = page.getByRole('button', { name: 'Count: 0' })
  await userEvent.click(button)
  await expect.element(button).toHaveTextContent('Count: 1')
})

Vue 2 : @vue/test-utils, pas de support Browser Mode direct. Vue 3 : @vitest/browser-vue suffit.

Cas concret

Dans un projet : liste triable par glisser-déposer (HTML5 Drag & Drop). En jsdom, impossible de simuler dragstart et drop proprement — mock = « événement déclenché », pas « ordre réellement changé ».

En Browser Mode :

test('tri par glisser-déposer', async () => {
  await page.mount(<SortableList items={['A', 'B', 'C']} />)

  const itemA = page.getByText('A')
  const itemC = page.getByText('C')

  // Glisser A après C
  await userEvent.dragTo(itemA, itemC)

  // Vérifier le nouvel ordre
  const items = page.getByRole('listitem')
  await expect.element(items.nth(2)).toHaveTextContent('A')
})

Dans le navigateur, le Drag & Drop se déclenche vraiment, la logique de tri s’exécute, l’ordre change. Test vert = comportement réel validé.

C’est la valeur du Browser Mode — comportement réel, pas simulation.

Playwright vs Browser Mode : guide de choix

Browser Mode et Playwright, tous deux dans le navigateur — quelle différence ?

En bref : Browser Mode pour les composants, Playwright pour les flux.

Différences clés

CaractéristiqueBrowser ModePlaywright
PérimètreComposant isoléFlux multi-pages
Vitesse~200 ms/test2-5 s/test
Coût de démarrageInstance partagéeDémarrage par test
Complexité configFaible, intégré à VitestÉlevée, projet séparé
Usage typiqueItération rapide en devValidation des parcours avant release

Browser Mode = test de composant monté seul. Playwright = E2E sur l’application complète : navigation, login, formulaires, parcours entier.

Analogie : Browser Mode, le microscope sur une cellule ; Playwright, l’examen de l’organe entier. Complémentaires, pas substituables.

Stratégie combinée

En projet réel :

  • Browser Mode : tous les composants UI — boutons, formulaires, cartes, modales. Écrits en dev, commités avec le code. Rapides, retour immédiat.
  • Playwright E2E : 3 à 5 parcours critiques — login → accueil, recherche → résultats, soumission → commande. Avant release ou build quotidien CI.

Avantages :

  1. Browser Mode attrape la majorité des bugs UI en développement.
  2. Playwright couvre l’intégration multi-pages avant release.
  3. Coût de maintenance maîtrisé — ~50 tests composants, ~5 E2E, CI reste fluide.

Quand choisir quoi ?

  • Browser Mode : comportement UI d’un composant — clic, saisie, rendu, styles. Besoin de feedback instantané après modification.
  • Playwright : flux multi-pages — login → navigation → action → vérification. Ou intégration front + API + base.

Exemple : sélecteur de dates (plages, dates désactivées, format) → Browser Mode. Réservation → paiement sur plusieurs pages → Playwright.

Browser Mode = front uniquement. Playwright peut couvrir full-stack (front + API backend). Browser Mode mock le backend côté composant.

Seuils de couverture en CI

La couverture clôt la boucle CI. Je l’ai sous-estimée jusqu’à une refacto qui a fait passer la couverture de 80 % à 60 % — bug en prod, leçon apprise.

Configuration de la couverture

Dans vitest.config.ts :

test: {
  coverage: {
    provider: 'v8',  // ou 'istanbul'
    reporter: ['text', 'json', 'html'],
    thresholds: {
      lines: 80,
      functions: 80,
      branches: 75,
      statements: 80
    }
  }
}

Seuils recommandés :

  • Nouveau projet : partir de 50 %, monter progressivement.
  • Projet mature : 80 % est raisonnable ; modules critiques 90-95 %.
  • Pas 100 % : branches limites, erreurs rares — forcer des tests coûte plus qu’ils n’apportent.

Lancer avec couverture :

npx vitest run --coverage

Sous le seuil ? Vitest échoue, le build CI bloque. Porte d’entrée : pas de merge sous le seuil.

Intégration GitHub Actions

Deux étapes : tests + rapport de couverture.

# .github/workflows/test.yml
name: Test

on: [pull_request]

jobs:
  test:
    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 chromium --with-deps

      - name: Run tests with coverage
        run: npx vitest run --coverage

      - name: Report coverage
        uses: davelosert/vitest-coverage-report-action@v2
        with:
          json-summary-path: './coverage/coverage-summary.json'

Cette action commente automatiquement la PR :

  • Variation de couverture (ex. 80 % → 79 %, affiché -1 %)
  • Fichiers en baisse
  • Nouveau code non couvert

L’auteur voit tout de suite : « Ma couverture baisse, il faut ajouter des tests. »

Points d’attention en CI

Pièges Browser Mode en CI :

  1. Installation Playwright : --with-deps, sinon Chromium ne démarre pas.
  2. Headless : headless: true obligatoire sans écran.
  3. Timeout : Browser Mode est plus lent que jsdom ; j’utilise 30 s.
  4. Parallélisme : instance partagée — maxWorkers: 4 max chez moi.

Extrait de workflow :

- name: Run browser tests
  run: npx vitest run --coverage --browser.headless
  env:
    CI: true

--browser.headless sans fenêtre ; CI: true ajuste le comportement Vitest (ex. couleurs désactivées).

Synthèse

L’essentiel : jsdom ne teste pas le comportement navigateur réel ; Browser Mode oui. Canvas, styles calculés, Web Components, drag & drop, animations — Browser Mode est souvent le seul choix crédible.

La config est simple : @vitest/browser-playwright, quelques lignes dans vitest.config.ts, c’est parti. API proche de Testing Library pour React et Vue.

Browser Mode n’est pas universel : composant vs flux. Combinez : Browser Mode en dev sur tous les composants UI, Playwright E2E sur les parcours critiques avant release. Couverture large, CI raisonnable.

Les seuils de couverture ferment la boucle. Seuil minimal, PR bloquée si non respecté. vitest-coverage-report-action dans GitHub Actions — la variation apparaît dans le commentaire PR.

Si vous n’avez pas encore testé Browser Mode, commencez par un bouton ou un champ texte. Validez la config, puis montez en complexité. Des pièges, oui — une fois passés, les tests deviennent fluides et l’efficacité en dev monte nettement.

Configurer Vitest Browser Mode pour les tests de composants

Configurer Browser Mode de zéro, exécuter des tests React/Vue, intégrer les seuils de couverture en CI

⏱️ Estimated time: 20 min

  1. 1

    Step 1: Installer le provider Playwright

    Exécutez npm install -D vitest @vitest/browser-playwright, puis npx playwright install chromium pour télécharger le navigateur.
  2. 2

    Step 2: Configurer vitest.config.ts

    Dans test.browser, définissez provider: playwright(), enabled: true, instances: [{ browser: 'chromium' }] ; en CI, ajoutez headless: true.
  3. 3

    Step 3: Écrire les tests de composants

    Utilisez page.mount() pour rendre le composant, page.getByRole() pour cibler les éléments, userEvent.click() pour simuler les interactions, expect.element() pour les assertions.
  4. 4

    Step 4: Configurer les seuils de couverture

    Définissez coverage.thresholds (lines: 80) dans vitest.config.ts ; intégrez vitest-coverage-report-action dans GitHub Actions pour afficher l'évolution sur les PR.

FAQ

Quelle différence entre Browser Mode et jsdom ?
jsdom simule le DOM dans Node.js : pas de rendu Canvas, pas de styles CSS calculés, cycle de vie Web Components incomplet. Browser Mode rend les composants dans un vrai navigateur et teste le comportement réel.
Browser Mode ou Playwright E2E — comment choisir ?
Browser Mode convient aux tests de composants isolés (~200 ms/test), avec retour immédiat en développement. Playwright convient aux flux multi-pages (2-5 s/test), pour valider les parcours critiques avant release. Combinez les deux.
Quel seuil de couverture adopter ?
Nouveau projet : partir de 50 %. Projet mature : 80 % est raisonnable. Modules critiques : 90 %. Ne visez pas 100 % — certains cas limites restent difficiles à tester.
Browser Mode prend-il en charge Vue 2 ?
Non. Pour Vue 2, utilisez @vue/test-utils. Les projets Vue 3 peuvent utiliser @vitest/browser-vue directement.
Points d'attention pour Browser Mode en CI ?
Installez Playwright avec --with-deps, configurez headless: true, augmentez le timeout (30 s), limitez le parallélisme (maxWorkers: 4).

10 min de lecture · Publié le: 17 mai 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog