Travail · 2026
Ce site : un portfolio trilingue dont les tests tournent sur le build
Astro 5, trois langues avec des URL localisées, et une suite de tests qui lit dist/ plutôt que les sources. Y compris le bug que cette suite ne pouvait pas voir, et qui a envoyé du lorem ipsum en production.
Le site que vous êtes en train de lire. C’est un site Astro 5, prérendu en HTML statique, publié en anglais, en italien et en français. Je le raconte ici surtout pour une décision et un échec.
Des URL qui appartiennent à leur langue
L’anglais occupe la racine sans préfixe ; l’italien vit sous /it/, le français
sous /fr/. Ce qui coûte du vrai travail, c’est que les segments de chemin sont
traduits eux aussi, pas seulement le contenu :
| Anglais | Italien | Français |
|---|---|---|
/projects/ | /it/progetti/ | /fr/projets/ |
/about/ | /it/chi-sono/ | /fr/a-propos/ |
/hobbies/ | /it/hobby/ | /fr/loisirs/ |
Un projet, c’est un fichier Markdown par langue partageant un même slug, et
c’est lui qui permet aux trois versions de se pointer mutuellement. Une page n’a
besoin d’exister que dans les langues pour lesquelles il y a du contenu : le
hreflang est émis vers les traductions qui existent réellement, jamais vers
une URL qui renverrait un 404. Chaque page se référence aussi elle-même, ce que
la spécification exige et qui est la règle la plus souvent oubliée.
Les tests lisent le build, pas les sources
La suite — quinze fichiers, 64 tests sur le lanceur de tests de Node — exécute
d’abord npm run build, puis lit dist/. Rien n’est simulé, parce que ce qui
mérite d’être vérifié, c’est le HTML qu’un navigateur recevra vraiment.
Elle vérifie qu’aucun lien interne n’est cassé, que chaque page a un titre et
une description non vides et que celles de premier niveau sont distinctes, que
chaque image référencée localement existe sur le disque, que les ensembles
hreflang sont complets et auto-référencés, que les brouillons n’atteignent
jamais le build, et que le thème est appliqué avant le premier rendu pour que la
page ne clignote pas en blanc.
Vérifier sur le build attrape toute une catégorie de bugs que les tests au niveau des sources ratent : un lien correct dans un composant et faux après le routage, un chemin d’image qui se résolvait en développement et pas dans la sortie.
Le bug que les tests ne pouvaient pas voir
Voici la partie qui mérite d’être racontée. Pendant des semaines, ce site a servi cinq projets intitulés des choses comme « Lorem ipsum ingestion pipeline », et le pied de page de chaque page affichait « Lorem ipsum dolor sit amet ». La suite de tests est restée verte tout du long.
Elle était verte parce que chaque assertion était structurelle. Les fixtures que les tests utilisaient pour vérifier le comportement — un projet publié, un non mis en avant, un brouillon — étaient les fichiers mêmes qui partaient en production comme contenu. La suite demandait y a-t-il un projet listé sur cette page, et un projet était listé. Elle n’avait aucun avis sur ce que ce projet racontait.
C’est le piège qu’il y a à utiliser ses fixtures comme contenu. Les tests continuent de passer, avec précision et honnêteté, pendant que le site ne dit rien de vrai. Une suite verte vous dit que la structure tient ; elle ne peut pas vous dire que cette structure soutient quelque chose de réel.
Ce qui a corrigé cela n’est pas une assertion plus maligne, c’est la suppression des fixtures et le repointage des tests vers des projets qui existent. La couverture comportementale est inchangée — il faut toujours que quelque chose soit publié-mais-pas-mis-en-avant pour que ce test veuille dire quelque chose, et ce quelque chose, c’est désormais cette page.
Ce que /projects/ a servi pendant des semaines, reconstruit depuis le commit précédant le nettoyage. Tous les tests étaient verts sur cette page.
Publier exprès
Le build est synchronisé par rsync vers son hébergeur, et npm run deploy
s’exécute en --dry-run par défaut. Publier pour de vrai exige un -- --live
explicite, après quoi le script demande à l’opérateur de taper le nom de domaine
avant de continuer.
Cette friction est voulue, parce que rsync tourne avec --delete : tout ce qui
se trouve sur le serveur sans être dans dist/ disparaît. Un drapeau aussi
destructeur ne devrait pas être à une faute de frappe d’un soir de fatigue.
Des visuels de couverture sans appel réseau
Chaque projet, article et page affiche une couverture. Si le champ cover:
n’est pas renseigné, on retombe sur un SVG plat généré de façon déterministe à
partir du slug par un script — même slug, même visuel, à chaque fois, sans
télécharger la moindre police ni la moindre image. Un test fait échouer le build
si une page référence une couverture inexistante : c’est ainsi qu’une couverture
manquante devient un test rouge plutôt qu’une image cassée.