Comment utiliser Playwright pour les tests de capture d'écran
Créez des tests de capture d'écran Playwright avec des images de référence, des projets de navigateur, des attentes stables, des seuils de différence et une relecture des mises à jour.
14 min de lectureCapture de site webPar Temposhot Team
Le test de capture d'écran compare l'apparence actuelle d'une page avec une image approuvée antérieurement. Si les pixels diffèrent au-delà de la tolérance autorisée, le test échoue et affiche la différence. Playwright Test intègre cette fonction avec expect(page).toHaveScreenshot(), sans extension supplémentaire. La méthode s'appelle aussi test de régression visuelle.
Les exemples de ce guide construisent une seule suite : 12 pages marketing et applicatives testées dans 3 projets de navigateur, plus 8 composants partagés. Chaque chiffre ci-dessous, du nombre d'images de référence aux seuils de différence, provient de cette suite.
Quand les tests de capture d'écran valent l'effort
Les tests visuels détectent des problèmes que les assertions sur le DOM ne voient pas : une feuille de style non chargée, un bouton décalé hors de l'écran, une police revenue à la police système par défaut. Leur maintenance en vaut la peine dans les cas suivants :
- l'interface repose sur un design system ou des composants partagés utilisés sur de nombreuses pages
- le CSS change souvent et les relecteurs ne peuvent pas vérifier chaque page à la main
- des bogues de mise en page ont déjà atteint la production
Ils coûtent plus qu'ils ne rapportent sur les pages qui contiennent des données en direct, des publicités ou du contenu utilisateur. Chaque exécution produit un résultat différent et la suite devient du bruit.
Dimensionner la suite avant d'écrire les tests
Chaque assertion crée une image de référence par projet. La suite d'exemple compte 12 assertions sur page entière et 8 assertions sur des composants, chacune exécutée dans 3 projets. Cela représente 60 fichiers PNG à créer, relire et conserver dans le dépôt.
Pages exclues de la suite
Certaines pages échouent à chaque exécution, quelle que soit l'écriture du test. Un tableau de bord avec des graphiques en direct, un fil de publications d'utilisateurs ou une page avec des bannières rotatives entrent dans cette catégorie. Pour ces pages, testez plutôt l'état vide ou un composant avec des données fixes.
L'équipe de l'exemple a exclu 5 pages candidates de la suite : 2 tableaux de bord, un index de blog qui reçoit de nouveaux articles chaque semaine, une page d'état et une page de résultats de recherche. Chacune est couverte par des assertions sur les éléments communs qu'elle contient. La liste des pages entières est restée à 12.
Une capture PNG en page entière d'une longue page marketing peut atteindre un mégaoctet ou plus. Soixante fichiers de cette taille s'accumulent dans l'historique Git à chaque mise à jour des références. Les assertions sur les composants produisent des fichiers plus petits et changent moins souvent, donc elles constituent la majeure partie de la suite.
Installer Playwright Test
npm init playwright@latest
L'installateur crée playwright.config.ts, un dossier de tests d'exemple et un workflow CI facultatif, puis télécharge les navigateurs. Les assertions de capture d'écran n'ont besoin de rien d'autre.
Épingler la version de Playwright
Chaque version de Playwright embarque des builds de navigateur précis. Une mise à jour du navigateur peut modifier légèrement le rendu du texte, ce qui fait échouer des tests de capture qui n'ont pas changé. Épinglez une version exacte dans package.json et mettez-la à jour dans une pull request distincte.
{
"devDependencies": {
"@playwright/test": "1.48.0"
}
}
Quand la version change, régénérez toutes les références dans la même pull request et relisez-les en un seul lot. Mélanger une montée de version et une modification fonctionnelle empêche de savoir laquelle a causé une différence.
Créer le premier test de capture d'écran
// tests/visual.spec.ts
import { test, expect } from '@playwright/test';
test('home page matches baseline', async ({ page }) => {
await page.goto('https://example.com/');
await expect(page).toHaveScreenshot('home.png', { fullPage: true });
});
La première exécution n'a aucune référence à comparer. Playwright enregistre la capture comme image attendue et fait échouer le test pour que la nouvelle référence soit relue. Relancez npx playwright test : le test passe alors avec le fichier enregistré.
Quand une exécution ultérieure diffère, Playwright enregistre l'image attendue, la capture réelle et une image de différence qui met en évidence les pixels modifiés. Le rapport HTML généré par npx playwright show-report affiche les trois.
Tester les 12 pages à partir d'une seule liste
Écrire 12 tests distincts répète les mêmes lignes. Une liste de pages et une boucle créent un test nommé par page. Le titre du test contient le nom de la page, ce qui permet ensuite de cibler une page avec -g.
// tests/pages.visual.spec.ts
import { test, expect } from '@playwright/test';
const pages = [
{ name: 'home', path: '/' },
{ name: 'pricing', path: '/pricing' },
{ name: 'features', path: '/features' },
{ name: 'docs-index', path: '/docs' },
// ...8 more entries
];
for (const p of pages) {
test(`page ${p.name} matches baseline`, async ({ page }) => {
await page.goto(p.path);
await page.getByRole('heading', { level: 1 }).waitFor();
await page.evaluate(() => document.fonts.ready);
await expect(page).toHaveScreenshot(`${p.name}.png`, { fullPage: true });
});
}
Les chemins relatifs nécessitent un baseURL dans la configuration. La section suivante l'ajoute avec un serveur web local.
Exécuter les tests sur un build local, pas en production
Les pages de production changent pour des raisons étrangères au code testé : modifications de contenu, tests A/B et scripts tiers. Un build local de la branche ne montre que ce que la branche a modifié. L'option webServer le démarre avant les tests.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: { baseURL: 'http://localhost:3000' },
webServer: {
command: 'npm run build && npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
reuseExistingServer permet à un développeur de garder son propre serveur actif pendant les exécutions locales. En CI, cette option vaut false : le job démarre donc toujours un build neuf.
Tester une partie plus petite de la page
test('pricing card', async ({ page }) => {
await page.goto('https://example.com/pricing');
await expect(page.getByTestId('pro-plan')).toHaveScreenshot('pro-card.png');
});
Les assertions au niveau des éléments sont plus petites et échouent moins souvent pour des raisons sans rapport. Une modification de l'en-tête ne fera pas échouer le test de la carte tarifaire. Utilisez les assertions sur page entière pour quelques pages clés et les assertions sur éléments pour les composants.
Choisir l'assertion de chaque page
Dans la suite d'exemple, les 12 pages se répartissent en 4 types. Seules les pages que les personnes jugent dans leur ensemble reçoivent une assertion sur page entière. Les pages construites à partir de composants partagés reçoivent à la place des assertions sur ces composants.
Ajouter des projets de navigateur et d'appareil
Les projets définis dans playwright.config.ts exécutent les mêmes tests dans plusieurs navigateurs et profils d'appareil.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
],
});
Chaque projet conserve ses propres références. Les noms de fichiers incluent le projet et la plateforme, par exemple home-chromium-darwin.png. Trois projets impliquent donc trois références par assertion : ajoutez des projets uniquement là où les mises en page diffèrent réellement.
Garder les références dans un dossier prévisible
L'emplacement par défaut place un dossier -snapshots à côté de chaque fichier de test. Avec plusieurs fichiers de test, les références se dispersent dans l'arborescence des tests. L'option snapshotPathTemplate les regroupe dans un dossier par fichier de test, sous une racine commune.
// playwright.config.ts
export default defineConfig({
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{-projectName}{ext}',
});
Le modèle supprime le suffixe de plateforme, ce qui fonctionne parce que chaque exécution utilise la même image Docker Linux. Si certaines exécutions ont encore lieu sur macOS, conservez {-snapshotSuffix} dans le modèle pour que les deux plateformes ne partagent jamais un fichier.
Un relecteur peut ouvrir un seul dossier et voir toutes les références d'un fichier de spécification. Renommer un fichier de test déplace ses références vers un nouveau dossier : renommez-le donc dans une pull request qui régénère aussi les références.
Stabiliser l'état de la page
Une capture n'est comparable que si la page atteint le même état à chaque exécution.
- Attendez un élément prêt, comme un titre ou une image chargée, avant l'assertion.
- Utilisez des données de test fixes plutôt que des comptes réels.
- Définissez une fenêtre d'affichage fixe dans la configuration.
- Attendez les polices web avec
await page.evaluate(() => document.fonts.ready).
toHaveScreenshot réessaie déjà jusqu'à ce que deux captures consécutives correspondent, et il arrête par défaut les animations CSS. Ces deux comportements suppriment une grande partie du bruit lié au timing sans code supplémentaire.
Attendre un signal, pas un délai fixe
Un page.waitForTimeout(3000) fixe ralentit la suite et échoue quand même sur un runner lent. Attendre un élément précis lie l'assertion à l'état de la page. L'attente du titre dans la boucle de pages ci-dessus en est un exemple.
Attendre que le réseau devienne calme est moins fiable qu'il n'y paraît. Les pages qui interrogent une API ou maintiennent une connexion ouverte n'atteignent jamais cet état, et l'attente se termine sur son délai d'expiration. Préférez un élément qui n'apparaît qu'après le chargement des données, comme la première ligne d'un tableau.
Les images à chargement différé sous la ligne de flottaison ne se chargent qu'au moment où elles entrent dans la zone visible. Une capture en page entière d'une telle page montre des cadres vides. Faites défiler la page jusqu'en bas une fois avant l'assertion, ou désactivez le chargement différé dans le build de test.
Figer le temps et bloquer les scripts tiers
Les dates du type « Mis à jour il y a 3 minutes » changent d'une exécution à l'autre. L'API d'horloge fixe l'heure que voit la page. Les scripts tiers de widgets de chat et d'analyse peuvent insérer des éléments tardivement, donc le test les bloque au niveau réseau.
test.beforeEach(async ({ page }) => {
await page.clock.setFixedTime(new Date('2026-05-01T10:00:00Z'));
await page.route(/(analytics|chat-widget)\./, (route) => route.abort());
});
Bloquer un script supprime aussi ce qu'il dessine. C'est voulu ici : le test visuel couvre la mise en page du site lui-même, et non le widget.
Sources d'instabilité et leurs corrections
La suite d'exemple a échoué 9 fois sans modification de code pendant ses deux premières semaines. Chaque échec remontait à l'une de cinq causes. Le tableau associe chaque cause au changement qui l'a supprimée.
Masquer ou couvrir les éléments volatils
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [page.locator('.timestamp'), page.locator('.user-avatar')],
});
Les zones masquées sont recouvertes d'un bloc plein dans les deux images, donc leur contenu est ignoré. Pour les éléments qui doivent disparaître complètement, transmettez une feuille de style avec l'option stylePath et masquez-les-y. Le masquage vaut mieux que la suppression des éléments, car la mise en page qui les entoure reste vérifiée.
Masquer par rôle, pas par mise en page
Un localisateur de masque qui dépend de classes de mise en page casse quand le balisage change. Un identifiant de test ou un rôle d'accessibilité reste stable lors des refontes. Dans la suite d'exemple, chaque élément volatil porte un data-testid qui commence par volatile-.
await expect(page).toHaveScreenshot('account.png', {
mask: [page.locator('[data-testid^="volatile-"]')],
maskColor: '#FF00FF',
});
Une maskColor vive rend les zones masquées évidentes dans le rapport. Les relecteurs voient alors d'un coup d'œil les zones que le test ignore.
Définir prudemment un seuil de différence
| Option | Signification |
|---|---|
maxDiffPixels | Nombre de pixels autorisés à différer. |
maxDiffPixelRatio | Part des pixels autorisés à différer, de 0 à 1. |
threshold | Écart de couleur minimal d'un pixel pour qu'il compte, de 0 à 1. Par défaut 0.2. |
Gardez des tolérances basses. Un maxDiffPixelRatio de 0.01 autorise 1 % des pixels à changer, soit plus de 50 000 pixels sur une capture en page entière de 1280×4000. Cela suffit à masquer une icône manquante. Commencez avec un maxDiffPixels d'environ 100 et ne l'ajustez que lorsqu'un faux échec précis l'exige.
Le calcul de seuil pour une capture en page entière
Les chiffres ci-dessous proviennent d'une capture de 1280 sur 4000 pixels, la taille de la page tarifs de l'exemple. Une icône de 24 sur 24 pixels couvre 576 pixels. Une tolérance en ratio de 1 % laisserait cette icône disparaître 88 fois sans déclencher d'échec.
Définir les valeurs par défaut une seule fois dans la configuration
Les options passées à chaque appel sont faciles à oublier dans un nouveau test. Le bloc expect.toHaveScreenshot de la configuration définit les valeurs par défaut de chaque assertion. Un test individuel peut toujours les remplacer.
// playwright.config.ts
export default defineConfig({
expect: {
toHaveScreenshot: {
maxDiffPixels: 100,
threshold: 0.2,
animations: 'disabled',
caret: 'hide',
scale: 'css',
},
},
});
scale: 'css' fait correspondre un pixel CSS à un pixel d'image, de sorte qu'un profil d'appareil de type Retina ne double pas la taille de l'image. Les références restent plus petites et les nombres de pixels ci-dessus gardent leur sens.
Relire et mettre à jour les références
Quand une modification de design est voulue, régénérez les références concernées :
npx playwright test --update-snapshots
Limitez les mises à jour aux tests en échec en passant un nom de fichier ou un -g avec le titre du test. Une mise à jour globale de la suite rend les modifications accidentelles faciles à manquer.
npx playwright test tests/pages.visual.spec.ts -g "page pricing" --update-snapshots
Lire les trois images dans l'ordre
Commencez par l'image réelle, pas par la différence. L'image réelle montre si la page paraît cassée pour une personne. Ouvrez ensuite la différence pour trouver où les pixels ont changé, puis l'image attendue pour voir ce qui était présent auparavant.
La différence marque les pixels modifiés en rouge. Un fin contour rouge autour de chaque bloc de texte signale en général une modification de police ou d'anticrénelage, et non un bogue de mise en page. Un rectangle rouge plein à un endroit indique en général qu'un élément a bougé, est apparu ou a disparu.
Le rapport HTML propose aussi une vue à curseur entre l'image attendue et l'image réelle. En faisant glisser le curseur sur la zone modifiée, on distingue mieux les petits décalages, comme une marge modifiée de 2 pixels, que dans la différence.
La vie d'une référence
Une référence passe par le même cycle chaque fois que la page change volontairement. Ce cycle comporte un seul point de décision : savoir si la différence est voulue. Tout le reste est mécanique.
Relire les modifications de références dans la pull request
Un PNG modifié dans une pull request est une modification de code. GitHub et la plupart des outils de relecture affichent les différences d'images côte à côte. La liste ci-dessous est celle que les relecteurs de l'équipe d'exemple vérifient avant d'approuver.
Exécuter les tests de capture d'écran en CI
Les références créées sous macOS ne correspondront pas aux captures d'un runner Linux, car les polices et l'anticrénelage diffèrent. La configuration fiable consiste à générer et comparer les références dans le même environnement, en général l'image Docker officielle de Playwright, à la même version que le package installé.
Une routine qui fonctionne :
- Les développeurs exécutent la suite visuelle dans l'image Docker, pas directement sous macOS.
- La CI exécute la même image et téléverse le rapport HTML comme artefact en cas d'échec.
- Les modifications voulues reçoivent de nouvelles références, committées avec la modification de code.
- Les relecteurs vérifient les différences d'images dans la pull request.
Générer les références dans Docker sur un Mac
La balise de l'image doit correspondre à la version épinglée de Playwright. Un décalage rétablit les différences de polices et de rendu que l'image est censée supprimer. Lancez la commande depuis la racine du projet.
docker run --rm --ipc=host -v "$(pwd)":/work -w /work \
mcr.microsoft.com/playwright:v1.48.0-noble \
/bin/bash -c "npm ci && npx playwright test --update-snapshots -g 'page pricing'"
Un job CI pour la suite visuelle
Le job s'exécute dans la même image, donc aucune étape de téléchargement de navigateur n'est nécessaire. Il ne téléverse le rapport qu'en cas d'échec de tests. Le rapport contient les images attendue, réelle et de différence pour chaque échec.
# .github/workflows/visual.yml
name: visual
on: pull_request
jobs:
visual:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.0-noble
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test tests/*.visual.spec.ts
env:
HOME: /root
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
Dans l'exemple, le job complet dure environ 5 minutes. Exécuter les 8 assertions de composants sur chaque pull request, et les 36 assertions de page uniquement sur la branche principale, ramène le job de pull request à moins de 3 minutes.
Où l'automatisation de captures classique reste pertinente
Les tests visuels répondent à une seule question : cette page a-t-elle changé par rapport à sa référence ? Ils ne produisent pas d'ensemble de revue pour une réunion de design, un rapport client ou une archive datée, et les personnes hors de l'ingénierie les exécutent rarement.
Pour ces besoins, un outil de capture classique est plus simple. Temposhot Website Capture prend une liste d'URL sur un Mac et capture chaque page en Desktop 1920×1080, Tablet 768×1024 ou Mobile 390×844, sans aucun code de test. Pour comparer deux captures à l'œil, le Compare Images met en évidence les différences dans le navigateur. L'entrée comparaison de captures d'écran décrit la méthode générale.
Deux outils pour deux questions différentes
La suite visuelle et un outil de capture fonctionnent souvent côte à côte dans une même équipe. Les ingénieurs gèrent la suite. Les designers et les chefs de produit utilisent les captures pour les revues et les archives.
Erreurs courantes
- Committer des références générées sur un ordinateur portable et les comparer dans une CI Linux.
- Asserter des pages entières qui incluent des dates, des carrousels ou des publicités.
- Relever les seuils pour faire taire un test instable au lieu de corriger l'état de la page.
- Lancer
--update-snapshotssur toute la suite après une seule modification voulue. - Ajouter de nombreux projets d'appareils avant que les références de bureau soient stables.
Trier un build visuel en échec
Un job visuel en échec demande une décision, pas une nouvelle exécution. Relancer un test instable jusqu'à ce qu'il passe masque la cause et laisse passer la prochaine régression réelle. L'ordre ci-dessous règle la plupart des échecs en quelques minutes.
- Téléchargez l'artefact de rapport et comptez les assertions en échec.
- Si toutes les assertions d'un même projet ont échoué, vérifiez d'abord une modification du navigateur ou de la police dans ce projet.
- Si une page a échoué dans les 3 projets, ouvrez son image réelle : la cause se trouve en général dans la page.
- Si une page a échoué dans un seul projet, cherchez un problème de CSS propre à ce navigateur ou une source d'instabilité du tableau ci-dessus.
- Corrigez la cause ou mettez à jour cette seule référence, jamais les deux dans le même commit.
FAQ
Où sont stockées les images de référence ?
Par défaut, dans un dossier à côté du fichier de test, nommé d'après ce fichier avec le suffixe -snapshots. L'option de configuration snapshotPathTemplate modifie cet emplacement.
Puis-je comparer des captures sans Playwright Test ?
Oui. Capturez avec n'importe quel outil et comparez les fichiers avec un outil de comparaison d'images. Playwright Test ajoute à cette comparaison les nouvelles tentatives, le masquage et le reporting.
Les tests de capture doivent-ils s'exécuter sur chaque pull request ?
Pour les assertions au niveau des composants, en général oui. Les grandes suites en page entière peuvent s'exécuter selon un planning ou avant les versions si elles ralentissent le pipeline.
Pourquoi un test réussit-il en local et échoue-t-il en CI sans modification de code ?
Les deux environnements rendent le texte différemment. Les polices, l'anticrénelage et le build du navigateur peuvent tous varier entre macOS et Linux. Exécutez la suite dans la même image Docker en local et en CI.
Avec combien de projets une nouvelle suite doit-elle commencer ?
Avec un seul. Commencez par un projet de bureau jusqu'à ce que ses références restent vertes pendant une ou deux semaines. Ajoutez ensuite le projet mobile, car les mises en page mobiles diffèrent le plus. Ajoutez un deuxième navigateur de bureau en dernier, et seulement si les utilisateurs signalent des bogues de mise en page propres à un navigateur.
La suite peut-elle tester des pages derrière une connexion ?
Oui. Connectez-vous une fois dans un projet de configuration, enregistrez la session avec storageState et chargez ce fichier dans les projets visuels. Utilisez un compte de test dédié avec des données fixes, afin que les noms et les chiffres de la page restent identiques à chaque exécution.
Les références doivent-elles être stockées avec Git LFS ?
Cela aide lorsque la suite contient des centaines d'images ou que les fichiers PNG changent souvent. Pour une suite d'environ 60 fichiers, Git classique suffit en général. Vérifiez la taille du dépôt après quelques mois de mises à jour de références.
Références
Besoin d'une automatisation de captures de site sans code ?
Lorsque l'objectif est un ensemble de captures plutôt qu'un résultat de réussite ou d'échec, Temposhot Website Capture exécute une liste d'URL localement sur un Mac. Consultez automatisation de captures d'écran de sites web ou le démarrage rapide de Website Capture.
playwrightvisual-testingscreenshot-comparisonci
Termes utilisés ici : playwright, responsive screenshot, screenshot automation, screenshot comparison, viewport screenshot, visual regression testing
Transformez vos captures d'écran répétitives en résultats prêts à l'emploi
Temposhot capture l'écran à intervalles réguliers ou à chaque clic et exporte le résultat au format PDF, image, ZIP ou MP4.
Articles connexes
- Comment automatiser les captures d'écran de sites web avec PlaywrightUtilisez Playwright pour automatiser les captures d'écran de sites web avec JavaScript. Capturez une page, des pages entières, des vues mobiles et des lots d'URL avec des nouvelles tentatives.
- Comment automatiser les captures d'écran de sites web avec PuppeteerUtilisez Puppeteer pour automatiser les captures d'écran de sites web en JavaScript. Capturez une page, des pages entières, des lots d'URL et des vues mobiles avec un script réutilisable.
- Comment automatiser les captures d'écran de sites web sans codeChoisissez une méthode sans code pour automatiser les captures d'écran de sites web : liste d'URL, plateforme d'automatisation avec API, outil de surveillance ou extension de navigateur.
- Meilleures alternatives à Xnapper pour Mac en 2026Comparez les alternatives à Xnapper pour Mac : captures répétées, capture et enregistrement de défilement, arrière-plans gratuits, guides multiplateformes et l'application intégrée.