 
 Tester dans un navigateur headless
Depuis plus de 12 ans, je développe des visualisations de données sur le web, et l’automatisation des tests dans un environnement navigateur a toujours été compliqué. C’est possible, mais souvent pénible et difficile à maintenir. De plus, l’automatisation des benchmarks dans un environnement navigateur a toujours été encore plus difficile à mettre en place.
Dans cet article, je vais démontrer à quel point il est facile aujourd’hui de configurer Vite, Vitest et Playwright pour tester et faire des benchmarks de code TypeScript dans un navigateur headless. Cette configuration permet d’exécuter les tests et les benchmarks depuis la ligne de commande et en CI, rendant la configuration beaucoup plus simple.
Pourquoi j’utilise Vitest
En travaillant récemment sur sigma.js v3, j’ai commencé à moderniser tous le tooling. L’état existant consistait en des configurations Webpack obsolètes, des scripts ad hoc et beaucoup trop de boilerplate. C’était difficile à maintenir et à faire évoluer, avec plusieurs problèmes non résolus, notamment l’exécution de tests unitaires dans un environnement navigateur.
J’ai passé des heures à chercher une solution de test moderne et facile à configurer, en vain. Divers articles de blog suggéraient des solutions basées sur Puppeteer, PhantomJS, voire CasperJS, mais elles semblaient toutes obsolètes et/ou difficiles à configurer (et probablement à maintenir).
Vitest pour les tests unitaires
Finalement, je suis tombé sur le mode navigateur de Vitest basé sur Playwright, qui correspondait parfaitement à mes besoins. À ce moment-là, sigma.js dépendait déjà de Playwright pour les tests end to end. J’aurais aimé que cette solution soit plus facile à trouver, et j’espère que cet article en accélérera la découverte pour d’autres.
Vitest pour les benchmarks
Au cours de mes recherches, j’ai également découvert la fonctionnalité de benchmarking de Vitest, basée sur TinyBench. Ça permet à Vitest d’exécuter TinyBench pour réaliser des benchmarks. Et ce n’est pas que du sucre : combinée avec le mode navigateur, ça permet de faire des benchmarks du code TypeScript depuis la ligne de commande dans un environnement navigateur.
Cependant, l’intégration de ces fonctionnalités n’était initialement pas possible en raison d’un problème dans Vitest. L’équipe Vitest a résolu ce ticket en quelques mois, avec une communication claire sur les progrès, ce qui était très agréable à observer.

Tester et benchmarker un projet d’exemple
Pour démontrer à quel point il est facile d’utiliser Vitest et Playwright pour tester et lancer des benchmarks de code TypeScript dans un navigateur headless, j’ai créé un projet d’exemple. Il vise à comparer deux méthodes de dessin de lignes épaisses sur un élément canvas : en utilisant CanvasRenderingContext2D.drawRect ou en utilisant CanvasRenderingContext2D.stroke (avec une lineWidth épaisse).
Le projet est disponible ici : github.com/jacomyal/canvas-benchmark
Le Code Principal
Essentiellement, les deux fonctions que j’ai testées et benchmarkées ressemblent à ceci :
function fillBasedThickLine(ctx, from, to, thickness, color) {
  const dx = to.x - from.x;
  const dy = to.y - from.y;
  const d = Math.sqrt(dx ** 2 + dy ** 2);
  const angle = Math.atan2(dy, dx);
  ctx.save();
  ctx.fillStyle = color;
  ctx.translate(from.x, from.y);
  ctx.rotate(angle);
  ctx.fillRect(0, 0 - thickness / 2, d, thickness);
  ctx.restore();
}
function strokeBasedThickLine(ctx, from, to, thickness, color) {
  ctx.beginPath();
  ctx.moveTo(from.x, from.y);
  ctx.lineTo(to.x, to.y);
  ctx.lineWidth = thickness;
  ctx.strokeStyle = color;
  ctx.stroke();
}Le code exact est disponible dans le fichier src/index.ts. Pour vérifier qu’elles fonctionnent correctement, j’ai ajouté un petit exemple, où les utilisateurs peuvent cliquer sur un canvas pour dessiner des lignes épaisses. Vous pouvez jouer avec ici :
Tests Unitaires
J’ai commencé par écrire un test pour chaque fonction afin de vérifier que la fonction colorie au moins un pixel sur un canvas de 1x1. Ils ressemblent à ceci :
test("it should colorize pixels", () => {
  // Dessiner une ligne épaisse contenant l'unique pixel
  drawThickLine(ctx, { x: -1, y: -1 }, { x: 1, y: 1 }, 2, "#ff0000");
  // Récupérer la couleur du pixel :
  const {
    data: [r, g, b, a],
  } = ctx.getImageData(0, 0, 1, 1);
  expect([r, g, b, a]).toStrictEqual([255, 0, 0, 255]);
});La version complète avec le boilerplate est disponible dans le fichier src/index.spec.ts.
Pour exécuter les tests unitaires, j’ai d’abord installé Vitest et Playwright :
npm install --save-dev vitest @vitest/browser playwrightPlaywright doit également installer ses navigateurs headless localement :
npx playwright installEnsuite, j’ai ajouté un fichier vitest.config.mts, indiquant à Vitest d’utiliser Playwright et le mode navigateur :
import { defineConfig } from "vitest/config";
export default defineConfig({
  test: {
    include: ["*.ts"],
    dir: "src",
    browser: {
      provider: "playwright",
      name: "chromium",
      enabled: true,
      headless: true,
    },
  },
});À ce stade, lorsque j’ai exécuté npx vitest test src/index.spec.ts, cela a affiché ce qui suit :
DEV  v1.5.3 canvas-benchmark
Browser runner started at http://localhost:5173/
✓ src/index.spec.ts (2)
✓ fillBasedRectangle (1)
✓ it should colorize pixels
✓ strokeBasedRectangle (1)
✓ it should colorize pixels
Test Files  1 passed (1)
Tests  2 passed (2)
Start at  16:58:52
Duration  800ms (transform 0ms, setup 0ms, collect 20ms, tests 6ms, environment 0ms, prepare 0ms)Les tests unitaires se sont exécutés avec succès depuis la ligne de commande dans un navigateur headless !
Benchmarks
Pour faire des benchmarks de ces fonctions, j’ai écrit quelque chose comme ceci :
bench(
  "Canvas methods to draw a thick line",
  () => {
    const angle = 2 * Math.PI * Math.random();
    drawThickLine(
      ctx,
      {
        x: 500 - 500 * Math.cos(angle),
        y: 500 - 500 * Math.sin(angle),
      },
      {
        x: 500 + 500 * Math.cos(angle),
        y: 500 + 500 * Math.sin(angle),
      },
      50,
      getRandomColor(),
    );
  },
  { iterations: 1000 },
);Encore une fois, la version complète avec le boilerplate est disponible dans le fichier src/index.bench.ts.
Pour activer la fonctionnalité de benchmarking, j’ai ajouté une ligne dans la configuration de Vitest :
export default defineConfig({
  mode: "benchmark",
  test: {
    /* ... */
  },
});Maintenant, exécuter npx vitest bench ./src/index.bench.ts affiche la sortie suivante :
DEV  v1.5.3 canvas-benchmark
Browser runner started at http://localhost:5173/
✓ src/index.bench.ts (2) 8624ms
✓ Canvas methods to draw a thick line (2) 8615ms
name                             hz     min       max    mean     p75     p99    p995    p999       rme  samples
· Using a filled rectangle  15,341.30  0.0000  1,785.50  0.0652  0.0000  0.1000  0.1000  0.1000  ±184.32%    29127   fastest
· Using a wide stroke       14,882.96  0.0000  2,427.90  0.0672  0.0000  0.1000  0.1000  0.1000  ±189.11%    37450
BENCH  Summary
Using a filled rectangle - src/index.bench.ts > Canvas methods to draw a thick line
1.03x faster than Using a wide strokeLes deux méthodes affichent des performances très similaires. Et cette analyse est automatisable et reproductible depuis la ligne de commande.
Conclusion
Je développe des bibliothèques JavaScript ciblant le navigateur depuis plus de 10 ans, et les tests unitaires ont toujours été un défi - et c’était encore pire pour les benchmarks. Aujourd’hui, grâce à Vitest et Playwright, l’intégration de ces processus dans n’importe quel projet TypeScript est devenue remarquablement facile.