ITSkillsCenter
Développement Web

Tests visuels avec Playwright en 2026 : tutoriel pas-à-pas

12 min de lecture

Le bug visuel est sournois : le code est correct, les tests unitaires passent, les tests E2E passent, mais une mise a jour de Tailwind a decale un padding et le bouton de paiement chevauche maintenant le footer sur mobile. Aucun test fonctionnel ne le voit. Seul un humain qui regarde la page peut le détecter — sauf si on a mis en place des tests visuels. Playwright 1.59 inclut un module de comparaison de screenshots très mature, qui transforme cette tache en routine automatisable. Ce tutoriel construit pas-à-pas une suite de tests visuels stable, sans les faux positifs qui ont ruine la reputation de cette pratique dans le passe.

Prerequis

  • Node.js 20 ou plus, idéalement Node 24 (Krypton).
  • Playwright 1.59 déjà installé dans le projet.
  • Application qui démarre en local avec un rendu stable.
  • Niveau attendu : a l aise avec Playwright basique, lecture de fichiers de configuration TypeScript.
  • Temps total : 2h pour parcourir et stabiliser sur un projet réel.

Étape 1 — Comprendre toHaveScreenshot

Le matcher toHaveScreenshot de Playwright capture l élément ou la page complète, le compare a une image de référence stockée dans le dossier des snapshots, et échoue si la différence dépasse un seuil configuré. Le seuil par défaut est très strict (un seul pixel different fait échouer le test), ce qui est rarement réaliste sur des configurations multi-OS. La première chose a faire est de comprendre la mécanique du matcher.

// tests/visual/home.spec.ts
import { test, expect } from '@playwright/test';

test('page d accueil — apparence stable', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('home.png');
});

Au premier lancement, Playwright n’a pas d image de référence. Il en cree une dans tests/visual/home.spec.ts-snapshots/home-chromium-linux.png et le test passe. Au deuxième lancement, il compare le rendu actuel a cette image. Si la différence est nulle, vert. Si elle dépasse le seuil, rouge avec un fichier de diff visuel dans le dossier test-results/. Cette mécanique simple cache plusieurs subtilites qu’on va traiter aux étapes suivantes.

Étape 2 — Stabiliser le rendu avec une viewport fixee

Le premier piège est la viewport. Si elle change entre deux exécutions (écran 1080p vs 1440p, devtools ouverts ou non), les screenshots sont différents. La solution est de fixer la viewport dans la configuration et de ne jamais la modifier.

// playwright.config.ts (extrait)
export default defineConfig({
  use: {
    baseURL: 'http://127.0.0.1:3000',
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 1,
  },
});

La viewport 1280×720 est un compromis raisonnable pour le desktop standard. Le deviceScaleFactor: 1 évite les screenshots HiDPI qui doublent la résolution sur les machines Retina. Avec ces deux options, vos screenshots ont la même dimension partout, qu’on tourne en local ou en CI.

Étape 3 — Désactiver les animations et les caretes clignotantes

Le deuxième piège classique est l animation. Une transition CSS ou un curseur clignotant change le rendu pixel-pres a chaque seconde. Playwright propose deux options conjointes pour le neutraliser.

test('page produit — sans animation', async ({ page }) => {
  await page.goto('/products/dattes-medjool');
  await expect(page).toHaveScreenshot('product-detail.png', {
    animations: 'disabled',
    caret: 'hide',
  });
});

animations: 'disabled' force Playwright a stopper toutes les animations CSS et les transitions au moment du screenshot, en attendant qu’elles soient terminées ou en sautant a leur état final. caret: 'hide' masqué le curseur des inputs, qui clignote a 60 Hz et casserait le test une exécution sur deux. Pour un site avec beaucoup d animations, on peut aussi ajouter au CSS de test un override : * { animation: none !important; transition: none !important; }.

Étape 4 — Masquer les zones dynamiques

Une page contient souvent des zones imprévisibles : la date du jour, un compteur en temps réel, une publicité, une image générée aleatoirement. Comparer ces zones est garanti de produire des faux positifs. Playwright permet de les masquer dans le screenshot avec une option mask.

test('dashboard — sans zones dynamiques', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    animations: 'disabled',
    mask: [
      page.locator('[data-testid="current-date"]'),
      page.locator('[data-testid="live-counter"]'),
      page.locator('.ad-banner'),
    ],
    maskColor: '#FF00FF',
  });
});

Chaque locator passe a mask est remplace par un rectangle de la couleur indiquée dans le screenshot. La structuré de la page reste comparable, seules les zones dynamiques disparaissent. Pour un dashboard typique, masquer cinq a dix éléments suffit a rendre le test fiable. La couleur magenta (#FF00FF) est un choix volontaire car elle n existe quasiment jamais dans une UI réelle, ce qui rend les zones masquées evidentes a l oeil quand on debugge.

Étape 5 — Tester les composants critiques individuellement

Tester la page entière a chaque modification est lourd. Une approche plus chirurgicale teste chaque composant critique de façon isolée. toHaveScreenshot peut être appelé sur un locator au lieu de la page complète.

test('bouton primaire — etats visuels', async ({ page }) => {
  await page.goto('/components/button');
  const button = page.getByRole('button', { name: 'Valider' });

  await expect(button).toHaveScreenshot('button-default.png');
  await button.hover();
  await expect(button).toHaveScreenshot('button-hover.png');
  await button.focus();
  await expect(button).toHaveScreenshot('button-focus.png');
});

Cette technique permet de constituer une bibliothèque de screenshots par composant qui devient un design system de référence. Quand le designer change une ombre ou un radius, on régénéré les screenshots des composants concernes et on les valide. C est aussi un excellent garde-fou pour les bibliothèques internes : on détecté instantanement les regressions visuelles dues a une mise a jour de Tailwind, Material UI, ou Radix.

Étape 6 — Configurer la tolerance et l’environnement

Sur différentes machines, les rendus de fonts varient légèrement (anti-aliasing, hinting). Pour éviter les faux positifs, on autorise une différence minime mais on l limite par une tolerance.

// playwright.config.ts
export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,
      threshold: 0.2,
    },
  },
});

maxDiffPixels limite le nombre absolu de pixels qui peuvent differer ; au-delà, le test échoue. threshold définit la tolerance par pixel sur une échelle de 0 (identique) a 1 (totalement different). Un threshold de 0.2 accepte des micro-différences de couleur dues a l anti-aliasing. Ces deux valeurs se combinent pour un compromis pratique. Pour les composants critiques (page de paiement), on peut surcharger localement avec maxDiffPixels: 0.

Étape 7 — Bien gérer la génération et la mise a jour des screenshots

La mise a jour des screenshots est l aspect opérationnel le plus delicat. Si on les genere n importe ou (machine du dev, MacOS vs Linux), ils ne seront jamais reproductibles. La pratique recommandée est de toujours les générer dans un container Docker fourni par Microsoft, qui fixe le système d exploitation, les fonts et la version Playwright.

# Mettre a jour les screenshots dans le container officiel
docker run --rm -it -v $(pwd):/work -w /work \
  mcr.microsoft.com/playwright:v1.59.1-noble \
  npx playwright test --update-snapshots

L image mcr.microsoft.com/playwright:v1.59.1-noble contient Node, Playwright avec les binaires des trois navigateurs, et les fonts Linux Ubuntu Jammy. Toute mise a jour de screenshot passe par ce container, qu’on tourne localement ou en CI. Les développeurs sur MacOS ou Windows ne committent jamais de screenshots generes nativement — ils seraient différents de ceux de la CI Linux et casseraient le test. Cette discipline est ce qui distingue une suite visuelle stable d’une suite cauchemardesque.

Étape 8 — Vérifier que la suite passe

Une fois les snapshots de référence committes, on lance la suite normalement. Le test échoue avec un message clair quand un pixel divergent dépasse le seuil, et le rapport HTML montre le diff visuel cote a cote.

docker run --rm -it -v $(pwd):/work -w /work \
  mcr.microsoft.com/playwright:v1.59.1-noble \
  npx playwright test
npx playwright show-report

Dans le rapport, chaque test visuel échoue présente trois images : la référence, le rendu actuel, et un diff colorise. Ce diff aide a identifier rapidement si la différence est intentionnelle (un changement de couleur voulu) ou un bug. En cas de changement intentionnel, on relance avec --update-snapshots, on revoie le diff dans la PR, et on merge.

Erreurs fréquentes

Symptome Cause probable Solution
Tests visuels qui échouent une fois sur deux Animation non désactivée ou caret visible Ajouter animations: 'disabled' et caret: 'hide'
Snapshot different selon la machine OS ou fonts différents Toujours générer dans le container Docker officiel
Le diff colorise ne montre rien d évident Différence subtile due a l anti-aliasing Augmenter threshold ou ajouter maxDiffPixels
Test passe en local, casse en CI Fonts manquantes dans l’image CI Utiliser uniquement le Docker Microsoft, pas le container Node nu
Trop de screenshots a maintenir Coverage trop ambitieuse Limiter aux composants critiques et aux pages a fort trafic, pas tout

Tutoriels associes

Bonnes pratiques approfondies

Une suite de tests visuels qui survit a six mois d existence respecte trois disciplines. La première est l ownership clair. Quand un test visuel échoue, qui regarde le diff ? Si la réponse est « tout le monde », c’est en réalité « personne ». L équipe doit nommer un proprietaire par ensemble de pages : le designer pour les pages marketing, le frontend lead pour le checkout, le tech lead pour les composants partagés. Ce proprietaire est le seul qui valide les mises a jour de snapshots, ce qui évite les pollutions accidentelles.

La deuxième discipline est la séparation entre captures de référence et captures dynamiques. Les images stockées dans Git sont les références, immuables sauf modification volontaire. Les images générées pendant un run de CI sont des artifacts ephemeres, jamais commitees. Cette distinction simple évite l’erreur classique du nouveau venu qui copie un screenshot depuis sa machine et casse le test pour toute l équipe.

La troisième discipline est de tester sur les viewports critiques, pas tous. Mobile (375×667) et desktop (1280×800) suffisent dans la grande majorité des cas. Tablet (768×1024) si on a des breakpoints spécifiques. Au-delà, on multiplie les snapshots sans bénéfice clair. Une seule résolution par viewport, on n’a pas besoin de tester 1366, 1440 et 1920 séparément — la règle CSS s adapté si elle est bien écrite.

Services externes : Percy, Chromatic, Argos

Pour les équipes qui investissent sérieusement dans la regression visuelle, des services externes apportent une revue visuelle dans la PR sans gérer le stockage et la comparaison soi-même. Percy par BrowserStack, Chromatic par Storybook, Argos open-source : chacun a un fonctionnement similaire — on push les screenshots vers leur infrastructure, ils comparent et postent un commentaire avec les diffs cliquables. Le coût commence vers 100 dollars par mois pour un projet moyen mais l économie de maintenance est réelle.

Pour un projet qui debute, rester sur la solution Playwright native est le bon choix : zero coût supplémentaire, pas de service tiers, snapshots dans Git. On migre vers un service externe quand le volume de screenshots dépasse 200 et que la maintenance prend plus de deux heures par semaine.

Tester les breakpoints responsive

Une discipline souvent oubliée est de tester les breakpoints critiques : 375 pixels (iPhone), 768 pixels (tablette), 1280 pixels (desktop courant). Plutôt que de configurer trois projects Playwright distincts, on peut utiliser page.setViewportSize dans le test pour basculer entre les viewports au sein du même test, ce qui simplifie la lecture quand on veut comparer les rendus.

Une autre approche, plus répandue, est de declarer plusieurs projects dans playwright.config.ts, chacun avec une viewport différente, et de tagger les tests visuels du label @responsive. On lance ensuite uniquement ces tests sur tous les projects en CI : trois projects multiplient par trois la durée mais donnent une couverture exhaustive sur les pages a fort enjeu.

La règle pratique : reserver la triple capture aux pages a fort enjeu (page d accueil, fiche produit, checkout). Pour les pages internes, un seul viewport desktop suffit, le mobile est vérifie manuellement par le QA. Cette discipline garde la suite proportionnée et évite l explosion combinatoire qu’on voit sur les projets qui veulent tout tester partout.

Ressources officielles

Pour la vue d’ensemble stratégique sur les tests modernes, voir le guide principal : Tests modernes en JavaScript en 2026.

Questions fréquentes

Faut-il committer les images de référence dans Git ?
Oui. Sans elles, le premier lancement en CI les regenererait et le test passerait toujours, ce qui rend la vérification inutile. Les images vivent dans le repo a cote du test. Si elles deviennent volumineuses, utiliser Git LFS.

Combien d images ai-je le droit de gérer ?
Sur un projet moyen, viser 50 a 200 screenshots. Au-delà, l effort de maintenance devient supérieur au gain. Pour aller plus loin, des services dédiés (Percy, Chromatic) externalisent la maintenance et ajoutent une revue visuelle dans la PR.

Les tests visuels remplacent-ils les tests E2E ?
Non, ils sont complémentaires. Un test E2E vérifie qu un parcours fonctionne. Un test visuel vérifie que l écran ressemble a ce qu’on attend. Les deux peuvent passer alors que la fonctionnalité est cassée, ou vice-versa.

Comment gérer les pages qui dépendent de données variables ?
Soit on seede la base avec des données fixes au debut du test (recommande), soit on mocke les API qui rendent ces données via page.route(). Ne jamais comparer un screenshot d’une page rendant des données aleatoires.

Quelle fréquence pour mettre a jour les références ?
Quand le designer ou le PO valide un changement visuel intentionnel, et seulement la. Une règle d or : la mise a jour des références est faite par le contributeur du changement, pas par l équipe d en face — sinon les diff visuels filent en silence.

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité