Testez les pixels : le contrôle par captures d'écran dans une petite équipe
Notre suite de tests était au vert et le menu déroulant ne se cliquait pas. Les tests unitaires ne voient ni les bugs d'empilement, ni les casses du thème sombre, ni les débordements sur mobile. Voici le petit dispositif de captures d'écran qui regarde maintenant chaque surface à notre place, et les bugs visuels qu'il attrape et que les tests n'attraperont jamais.
Nous l'avons adopté de la façon honnête : après avoir livré des régressions visuelles qu'une seule capture d'écran aurait attrapées. Un panneau dépoli qui ressortait pâle sur fond sombre. Un menu qui s'affichait mais ne se cliquait pas. Un champ de recherche qui existait, à la largeur d'un téléphone, sous la forme d'un ruban de 65 pixels. Chacun d'eux a traversé une suite de tests au vert, parce que la suite testait des fonctions et que les bugs vivaient dans les pixels.
Le dispositif est plus petit que vous ne le croyez
Le nôtre, c'est un environnement local garni de fausses données convaincantes, une connexion sans interface dont la session est enregistrée sur le disque pour que rien ne la ralentisse, et une boucle : les pages fois les thèmes fois les tailles d'écran. Ordinateur et téléphone, clair et sombre, chaque surface qui compte. Le tout tient dans un court script, et un balayage complet produit un dossier d'images en quelques minutes.
import { chromium } from 'playwright'; const browser = await chromium.launch();const ctx = await browser.newContext({ storageState: 'auth.json', // login once, reuse everywhere viewport: { width: 1440, height: 900 },});const page = await ctx.newPage(); for (const [name, url] of PAGES) { await page.goto(BASE + url, { waitUntil: 'load' }); for (const theme of ['light', 'dark']) { await page.evaluate((t) => document.documentElement.setAttribute('data-theme', t), theme); await page.screenshot({ path: `shots/${name}-${theme}.png`, fullPage: true }); }}Des captures et des preuves
Les images attrapent ce que les yeux attrapent. Pour les bugs d'interaction, vous voulez que le navigateur témoigne. Deux lignes font l'essentiel du travail : elementFromPoint au centre d'un contrôle vous dit ce qui recevrait vraiment le clic, et getBoundingClientRect vous dit si le contrôle tient réellement dans l'écran qu'il prétend prendre en charge. Nous vérifions désormais les deux pour tout ce qui nous a déjà brûlés.
// Is the menu item really clickable, or is something invisible covering it?const hit = await page.evaluate(() => { const el = document.querySelector('[role="menuitem"]'); const r = el.getBoundingClientRect(); const top = document.elementFromPoint(r.x + r.width / 2, r.y + r.height / 2); return el === top || el.contains(top);}); // Does the search input actually fit a 390px phone?const fits = await page.evaluate(() => { const r = document.querySelector('input[name="q"]').getBoundingClientRect(); return r.right <= window.innerWidth && r.width > 200;});Les deux thèmes, à chaque fois
Les bugs du mode sombre sont une catégorie à part, parce qu'ils se cachent des développeurs qui travaillent dans un seul thème. Un remplissage presque blanc écrit en dur a l'air intentionnel sur une page claire et radioactif sur une page sombre. Basculer l'attribut de thème pendant la même visite de page coûte une ligne et double votre couverture. Nous considérons comme non testée toute surface qui n'a pas été photographiée dans les deux thèmes.


Ce qu'il attrape
- Les bugs de contexte d'empilement : affiché, visible, et impossible à cliquer. Seul un test de collision les voit.
- Les fuites de thème : des surfaces claires qui brillent sur des pages sombres, des graphiques qui disparaissent, des illustrations avec un fond cuit dedans.
- Les débordements sur téléphone : des barres fixes qui couvrent le contenu, des champs réduits à des rubans, des titres coupés en plein milieu.
- Les textes en contexte : la troncature, la contradiction, la phrase qu'aucun analyseur ne signalera jamais.
Rien de tout cela ne remplace la suite unitaire. La suite prouve que les calculs d'argent tombent juste, les captures prouvent qu'un humain peut atteindre le bouton qui les déclenche. Une petite équipe n'a pas de service qualité, mais un dossier de captures fraîches relu avant chaque livraison en est une imitation correcte. Le test, c'est de regarder.
Les tests passent est une affirmation sur vos fonctions. La capture d'écran est une affirmation sur votre produit.