Précisons d'emblée notre intérêt : nous sommes Geonode et nous vendons des proxys ; les captures d'écran via un proxy constituent l'un des cas d'utilisation les plus « propres » de notre produit — en effet, aucune API ne permet de vérifier à quoi ressemble réellement une page depuis un autre pays. Il faut toutefois mentionner honnêtement le coût : un navigateur récupère toutes les images, polices, scripts et vidéos en préchargement ; la création de captures d’écran est donc l’activité la plus gourmande en bande passante que l’on puisse réaliser avec un forfait à volume limité. Vous trouverez ci-dessous une section consacrée à la réduction de cette consommation, et la technique qui y est décrite vous permettra de réaliser davantage d’économies que le choix d’un fournisseur moins cher.
Les trois types de captures d'écran
Fenêtre d'affichage — ce qui est actuellement visible, l'option par défaut :
await page.screenshot({ path: 'viewport.png' });
Page entière — la documentation la décrit comme « une capture d'écran d'une page entière défilable, comme si vous disposiez d'un écran très haut et que la page puisse y tenir entièrement » :
await page.screenshot({ path: 'full.png', fullPage: true });
Élément — « Il est parfois utile de réaliser une capture d’écran d’un seul élément » :
await page.getByRole('article').screenshot({ path: 'element.png' });
Le choix entre ces options dépend principalement de l’utilisation que vous comptez faire du résultat. Les captures d’écran de la fenêtre d’affichage répondent à la question « qu’est-ce que l’utilisateur voit en premier ? ». Les captures d’écran pleine page répondent à la question « qu’y a-t-il sur cette page ? ». Les captures d’écran d’élément répondent à la question « ce composant s’affiche-t-il correctement ? », et ce sont les plus fiables des trois à des fins de comparaison, car elles excluent tout ce que vous n’avez pas demandé.
Captures d’écran pleine page et leurs limites
fullPage: true est l’option la plus couramment utilisée, mais aussi celle qui présente le plus de restrictions.
Le contenu chargé de manière différée peut ne pas apparaître. Playwright effectue le défilement pour réaliser la capture, mais les images et les composants qui se chargent à l’intersection peuvent ne pas avoir fini de se charger lorsque la capture est terminée. La solution fiable consiste à faire défiler la page délibérément et à attendre que le contenu attendu s’affiche :
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await expect(page.getByRole('img').last()).toBeVisible();
await page.evaluate(() => window.scrollTo(0, 0));
await page.screenshot({ path: 'full.png', fullPage: true });
Les en-têtes fixes (sticky headers) peuvent se répéter ou se positionner de manière étrange. Les éléments avec position: fixed ou sticky se comportent de manière imprévisible dans une capture assemblée. L’option style — une « chaîne CSS à injecter dans la page pour la mise en forme pendant la capture » — est la solution la plus propre :
await page.screenshot({
path: 'full.png',
fullPage: true,
style: '.sticky-header { position: absolute !important; }',
});
Les pages très longues génèrent des fichiers très volumineux. Un flux à défilement infini n’a pas de fin naturelle. Envisagez plutôt d’utiliser clip pour capturer une zone définie, ce qui nécessite « un objet spécifiant le recadrage de l’image résultante ».
Les superpositions sont également capturées. Les bannières de cookies, les widgets de chat et les fenêtres modales apparaissent sur la capture d’écran exactement comme elles s’affichent pour l’utilisateur. Si vous ne souhaitez pas qu’elles apparaissent, fermez-les d’abord — et si vous ne pouvez pas le faire, l’outil « mask » est la solution.
Captures d'écran et tampons d'éléments
Les captures d'écran d'éléments font défiler l'élément jusqu'à ce qu'il soit visible et ne capturent que son cadre de sélection, ce qui en fait le paramètre par défaut idéal pour les vérifications au niveau des composants.
const card = page.getByTestId('product-card').first();
await card.screenshot({ path: 'card.png' });
Il y a deux choses qu'elles ne font pas : capturer le contenu masqué par overflow: hidden
, et capturer tout ce qui se trouve en dehors du cadre de l'élément, même si cela le chevauche visuellement.
Des tampons plutôt que des fichiers. La documentation précise que « plutôt que d’écrire dans un fichier, vous pouvez obtenir un tampon contenant l’image et le traiter ultérieurement ou le transmettre à un outil tiers de comparaison de pixels ». Omettez l’path
et vous récupérez les octets :
const buffer = await page.screenshot();
const base64 = buffer.toString('base64');
C’est le format qu’il vous faut lorsque la capture d’écran est destinée à un emplacement autre que le disque local — un magasin d’objets, une API, un rapport, un service de comparaison. Cela permet également d’éviter complètement le système de fichiers, ce qui est important dans les exécuteurs conteneurisés où le disque est éphémère.
Les options qui permettent de reproduire les captures d'écran
Si vous comparez plusieurs captures d'écran entre elles, ces options sont indispensables. Si vous vous contentez d'en examiner une à l'œil nu, vous pouvez les ignorer.
| Option | Valeurs | Fonction |
|---|---|---|
animations |
| disabled
, allow
| « Lorsqu'elle est définie sur « disabled », cette option arrête les animations CSS pendant la capture » |
| caret
| hide
, initial
| « Lorsqu'elle est définie sur « hide », cette option masque le curseur de texte pendant la capture d'écran » |
| mask
| Locator[]
| « Spécifie les éléments de localisation qui doivent être masqués lors de la capture d'écran » |
| maskColor
| Couleur CSS, valeur par défaut #F0F
| « Spécifie la couleur à utiliser pour les zones masquées » |
| scale
| css
, device
| « L'échelle d'affichage de la page Web » |
| omitBackground
| Booléen, valeur par défaut false
| « Masque l'arrière-plan blanc par défaut et permet de réaliser des captures d'écran transparentes » |
| type
| png
, jpeg
, valeur par défaut png
| « Spécifie le format de fichier de la capture d’écran » |
| quality
| 0–100 | « La qualité de l’image au format JPEG » — JPEG uniquement |
| style
| Chaîne CSS | Insérée dans la page pendant toute la durée de la capture |
Les quatre paramètres qui résolvent la plupart des problèmes de reproductibilité :
**animations: 'disabled'
** élimine la principale source de différence entre deux captures d’une même page. Toute transition CSS en cours de chargement produit un résultat en pixels différent à chaque exécution.
**mask
** remplace certaines zones par une couleur unie, ce qui permet d’exclure le contenu véritablement variable — horodatages, identifiants de session, recommandations personnalisées, publicités — sans pour autant renoncer complètement à la comparaison :
await page.screenshot({
path: 'page.png',
mask: [page.getByTestId('timestamp'), page.locator('.ad-slot')],
maskColor: '#000000',
});
**scale: 'css'
** effectue la capture aux dimensions en pixels CSS plutôt qu’au rapport de pixels de l’appareil, de sorte qu’un ordinateur à haute résolution (DPI) et un exécuteur CI produisent des images de taille comparable. Définissez-le explicitement plutôt que de vous fier à la valeur par défaut, car c’est l’option la plus susceptible de varier entre votre ordinateur portable et le serveur de compilation.
**caret: 'hide'
** supprime le curseur de texte clignotant, qui apparaît sinon dans environ la moitié de vos captures de toute page comportant un champ de saisie actif.
Trois autres éléments à définir pour garantir la reproductibilité, dont aucun ne concerne les options de capture d’écran : fixez la taille de la fenêtre d’affichage dans votre configuration, fixez les paramètres régionaux et le fuseau horaire, et fixez les polices — la disponibilité des polices diffère entre la machine d’un développeur et un conteneur, et des polices différentes entraînent une mise en page différente.
Captures d’écran automatiques en cas d’échec d’un test
Il s’agit de la configuration de capture d’écran la plus utile dans Playwright, et elle ne nécessite qu’une seule ligne :
export default defineConfig({
use: {
screenshot: 'only-on-failure',
trace: 'retain-on-failure',
video: 'retain-on-failure',
},
});
screenshot: 'only-on-failure'
capture la page au moment où un test échoue et la joint au rapport. Les options disponibles sont off
, on
et only-on-failure
; on
effectue une capture à chaque test et génère ainsi un grand nombre de fichiers.
Il est particulièrement important d’associer cette fonctionnalité à trace
, car une trace inclut des instantanés du DOM, l’activité réseau et chaque action avec ses durées. Une capture d’écran vous indique que la page s’affichait de manière incorrecte ; une trace vous explique pourquoi. Pour toute exécution en mode automatisé, ces deux options doivent être activées.
Cette configuration est également le moyen le plus rapide de diagnostiquer cette catégorie déroutante d’échecs où une page s’est bien affichée — mais pas celle à laquelle vous vous attendiez. Une page d’authentification, une redirection de connexion ou une variante régionale génèrent toutes des délais d’expiration qui ressemblent à des problèmes d’éléments jusqu’à ce que vous consultiez la capture d’écran.
Comparaison visuelle avec toHaveScreenshot
Pour effectuer de véritables tests de régression visuelle plutôt que des captures ponctuelles :
await expect(page).toHaveScreenshot('homepage.png');
await expect(page.getByRole('navigation')).toHaveScreenshot('nav.png');
Lors de la première exécution, l’outil génère une référence ; lors des exécutions suivantes, il compare les résultats et signale un échec en cas de différence. Mettez à jour les références de manière ciblée à l’aide de --update-snapshots
.
Trois remarques pratiques.
Les références sont spécifiques à chaque plateforme. Le rendu des polices diffère d’un système d’exploitation à l’autre ; ainsi, une référence générée sous macOS ne correspondra pas à celle générée dans un conteneur Linux. Générez les références dans le même environnement que celui où s’exécutent vos tests — généralement en CI, via un conteneur que vous pouvez également exécuter localement.
Définissez une tolérance. Une correspondance exacte au pixel près entraîne des échecs dus à des différences d’anticrénelage qu’aucun être humain ne remarquerait. L’ajout de maxDiffPixels
ou maxDiffPixelRatio
dans votre configuration rend la suite de tests utilisable.
Masquez toutes les variables avant de commencer. Un test visuel qui échoue dès qu’un horodatage change sera désactivé en moins d’une semaine, ce qui est pire que de ne pas l’avoir du tout.
Captures d'écran via un proxy
C'est un cas où cela relève véritablement de notre domaine de compétence, et c'est une bonne chose.
Configurez le proxy dans votre installation Playwright :
const context = await browser.newContext({
proxy: { server: 'http://proxy.example.com:9000', username: 'u', password: 'p' },
locale: 'de-DE',
timezoneId: 'Europe/Berlin',
});
Notez les adresses locale
et timezoneId
associées au proxy. Une adresse de sortie allemande avec une locale en-US
et un fuseau horaire londonien est une combinaison qu’aucun visiteur réel ne produit, et de nombreux sites utilisent la locale indépendamment de l’adresse pour déterminer le contenu à afficher. Définissez ces trois paramètres ensemble, sinon vous testerez autre chose que ce que vous aviez prévu.
Ce à quoi cela répond réellement : ce que voit un véritable visiteur dans ce pays. Les tarifs régionaux, l’affichage de la devise, la disponibilité, les bannières promotionnelles, la diffusion effective de vos publicités là où vous avez payé pour cela, et ce qui apparaît à côté de votre contenu. Aucune API ne vous fournit ces informations, car la réponse est une page rendue.
Combien cela coûte-t-il, et comment réduire ces coûts ? Un navigateur récupère tout. Avec un forfait Internet résidentiel limité — le nôtre commence à 0,79 $/Go, selon notre page de tarifs en septembre 2026 —, une série de captures d’écran sur vingt marchés fait rapidement grimper la facture. Bloquer les types de ressources dont vous n’avez pas besoin est le levier le plus efficace :
await page.route('**/*.{woff,woff2,mp4,webm}', route => route.abort());
Notez ce qui ne figure pas dans cette liste. Si la capture d’écran est le résultat attendu, vous ne pouvez pas bloquer les images — cela irait à l’encontre du but recherché. Bloquez les polices et les médias, conservez les images, et acceptez que la vérification visuelle soit par nature le type de travail de proxy le plus coûteux. Lorsque vous avez uniquement besoin de confirmer le contenu textuel plutôt que l’apparence, bloquez également les images et évitez complètement la capture d’écran.
Et vérifiez que la géolocalisation a bien été prise en compte. Prenez la capture d’écran et examinez-la. Si une page capturée via une sortie brésilienne affiche les mêmes prix que votre poste de travail, le ciblage ne fonctionne pas, quel que soit le résultat de la recherche d’adresse IP. C’est exactement l’échec silencieux que nous avons décrit dans l’importance de tester les proxys — et les captures d’écran sont particulièrement efficaces pour le détecter, car un humain peut le voir d’un seul coup d’œil.
Captures d'écran à grande échelle
Dès que vous en prenez plus d'une poignée, certaines pratiques permettent d'éviter que la tâche ne devienne ingérable.
Réutilisez le navigateur, pas le contexte. Lancer un navigateur coûte cher ; créer un contexte ne coûte presque rien. Pour une exécution sur plusieurs pages ou plusieurs régions, lancez le navigateur une seule fois et créez un nouveau contexte par unité de travail — cela vous permet de disposer de cookies et d’un espace de stockage isolés sans avoir à payer à plusieurs reprises le coût de démarrage :
const browser = await chromium.launch();
for (const country of countries) {
const ctx = await browser.newContext({ proxy: { server: proxyFor(country) } });
const page = await ctx.newPage();
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: `shots/${country}.png`, fullPage: true });
await ctx.close();
}
await browser.close();
N’utilisez pas networkidle comme condition d’attente. Les pages comportant des balises d’analyse, des WebSockets ou des requêtes de sondage ne restent jamais inactives, et le délai d’attente expire. Attendez l’élément qui vous indique que la page est prête :
await page.goto(url);
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
await page.screenshot({ path: 'shot.png' });
Limitez délibérément la concurrence. Chaque contexte de navigateur consomme de la mémoire physique — quelques centaines de mégaoctets sont normaux une fois la page chargée. L’exécution de trente contextes en parallèle sur un petit serveur entraîne des échecs qui ressemblent à des délais d’attente, mais qui sont en réalité dus à un manque d’espace sur la machine. Commencez par quatre ou cinq et augmentez progressivement tout en surveillant la mémoire.
Nommez les fichiers de manière à pouvoir les retrouver. Un répertoire contenant des fichiers de type screenshot-1.png à screenshot-400.png est inutilisable. Incluez la cible, la région et un horodatage dans le nom de fichier, et enregistrez l’URL avec l’image.
Compressez avant d’archiver. Le format PNG est sans perte mais volumineux. Si les images sont destinées à un examen humain plutôt qu’à une comparaison de pixels, le format JPEG (quality: 80) occupe généralement une fraction de la taille et est visuellement indiscernable — et pour une campagne couvrant vingt marchés selon un calendrier précis, cette différence se traduit par la facture de stockage.
Gérez les échecs sans interrompre la campagne. Une page qui ne se charge pas ne doit pas entraîner l’abandon des dix-neuf autres. Encadrez chaque capture, enregistrez l’erreur et poursuivez — puis signalez quelles cibles ont échoué plutôt que de découvrir que l’ensemble de la campagne a échoué dès la troisième cible.
Quand une capture d’écran n’est pas l’outil adéquat
Lorsque vous recherchez des données. Si vous avez besoin du prix, extrayez-le. Une capture d’écran d’un chiffre n’est qu’un chiffre que vous devrez ensuite lire à partir d’une image. Les captures d’écran servent à montrer l’apparence ; les sélecteurs servent à extraire le contenu.
Lorsque vous souhaitez savoir pourquoi un test a échoué. Une trace est nettement plus informative et inclut de toute façon la capture d’écran.
Lorsque la page est très longue. Les captures d’écran pleine page de pages à défilement infini génèrent des fichiers volumineux que personne n’ouvrira. Limitez-vous à la zone qui vous intéresse.
Lorsque vous vérifiez du texte. Effectuez une assertion sur le texte. expect(locator).toHaveText() fournit un message d’échec lisible ; une comparaison de pixels ne vous donne qu’une image de celui-ci.
Lorsque vous devez archiver à grande échelle. Les captures d’écran sont volumineuses, et des milliers d’entre elles sur de nombreux marchés représentent un poids considérable en termes de stockage et de bande passante. Stockez des hachages ou des différences et ne conservez les images complètes que lorsque quelque chose a changé.
Questions fréquentes
Comment faire une capture d'écran dans Playwright ?
await page.screenshot({ path: 'shot.png' }) pour la fenêtre d'affichage, { fullPage: true } pour l'intégralité de la page défilable, et locator.screenshot() pour un élément unique. Omettez path pour obtenir un tampon au lieu d'enregistrer un fichier.
Comment réaliser une capture d’écran de la page entière ?
Passez l’option fullPage: true. Sachez que le contenu chargé de manière différée peut ne pas être encore disponible et que les éléments « sticky » ou fixes peuvent se comporter de manière inhabituelle dans le résultat assemblé — faites d’abord défiler la page de manière ciblée, puis utilisez l’option style pour neutraliser le positionnement « sticky » pendant la capture.
Comment faire une capture d’écran d’un seul élément ?
Appelez screenshot() sur un localisateur plutôt que sur la page : await page.getByTestId('card').screenshot({ path: 'card.png' }). Playwright fait défiler l’élément jusqu’à ce qu’il soit visible et capture son cadre de sélection. Le contenu masqué par overflow: hidden n’est pas inclus.
Comment garantir la cohérence des captures d’écran de Playwright d’une exécution à l’autre ?
Définissez animations: 'disabled' et caret: 'hide', masquez les zones variables avec mask, et définissez explicitement scale. Fixez ensuite la taille de la fenêtre d’affichage, les paramètres régionaux, le fuseau horaire et les polices disponibles, car ces quatre éléments affectent la mise en page et aucun d’entre eux ne fait partie des options de capture d’écran.
Comment capturer automatiquement une capture d’écran lorsqu’un test échoue ?
Définissez screenshot: 'only-on-failure' dans le bloc use de votre configuration Playwright. Associez-le à trace: 'retain-on-failure' : une trace inclut des instantanés du DOM, l’activité réseau et les durées des actions, ce qui explique l’échec plutôt que de simplement l’afficher.
Puis-je obtenir une capture d’écran au format base64 plutôt que sous forme de fichier ?
Oui. Omettez l’option path et screenshot() renvoie un tampon, que vous pouvez convertir à l’aide de buffer.toString('base64'). La documentation recommande cette méthode pour le post-traitement ou pour transmettre les données à un service de comparaison de pixels ; cela permet également d’éviter l’utilisation du système de fichiers dans les exécuteurs CI éphémères.
Comment masquer du contenu dynamique sur une capture d’écran ?
Utilisez l’option mask avec un tableau de localisateurs, ce qui remplace ces zones par une couleur unie — maskColor utilise par défaut #F0F et peut être modifié. C’est ainsi que vous préservez l’utilité de la comparaison visuelle sur les pages contenant des horodatages, des données de session ou de la publicité.
Puis-je réaliser des captures d’écran via un proxy pour voir les pages régionales ?
Oui, et c’est l’une des meilleures façons de l’utiliser. Configurez le proxy dans les paramètres du navigateur, puis définissez locale et timezoneId en fonction du pays — de nombreux sites utilisent les paramètres régionaux indépendamment de l’adresse. Examinez ensuite l’image obtenue pour vérifier que le contenu régional diffère réellement, plutôt que de vous fier à une simple recherche d’adresse IP.
Conclusion
Pour réaliser une capture d'écran dans Playwright, une seule ligne suffit. Mais pour en réaliser une qui ait un sens, il en faut un peu plus.
Si la capture d’écran est destinée à être consultée immédiatement par un humain — pour signaler un problème, un bug ou vérifier l’affichage d’une page depuis le Brésil —, les paramètres par défaut suffisent et l’screenshot: 'only-on-failure' dans votre configuration est la ligne la plus utile que vous puissiez ajouter. Associez-la à une trace, car celle-ci explique ce que la capture d’écran se contente de montrer.
Si la capture d’écran doit être comparée à une autre, tout change. Désactivez les animations, masquez le curseur, masquez les zones variables, fixez l’échelle, et définissez la fenêtre d’affichage, les paramètres régionaux, le fuseau horaire et les polices. Générez ensuite des références dans le même environnement que celui où s’exécutent les tests, car le rendu des polices varie d’une plateforme à l’autre et une référence issue de votre ordinateur portable ne correspondra jamais à celle d’un conteneur.
Et pour la vérification géographique — domaine dans lequel une page affichée l’emporte véritablement sur les données structurées —, configurez ensemble le proxy, les paramètres régionaux et le fuseau horaire, puis examinez l’image pour confirmer que le ciblage a fonctionné. La bande passante représente le coût, les images sont le seul type de ressource que vous ne pouvez pas bloquer, et c’est tout simplement ce que coûte la vérification visuelle.
