From 951b88abab5eb46215dd8d828d9c59d7e5cd5f51 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Thu, 20 Aug 2026 02:08:15 -0400 Subject: [PATCH] [FIX] todo analyse: give the migration entry its icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An entry with no icon, in a menu where every other one has one, reads as unfinished. The section gets 🚚 and the entry 📐 — the same symbol the quality report already wears in its own title, because it is the same thing. The spacing follows a convention this menu keeps without stating it: an icon of « neutral » width — 🗄, 🖼 — occupies one column in a terminal and takes TWO spaces to line up, while a wide one — 📏, 📐 — takes one. Getting it wrong shifts the line, and nothing says so until it is under your eyes. A test now states it. --- FR --- [FIX] todo analyse : donner son icône à l'entrée de migration Une entrée sans icône, dans un menu où toutes les autres en ont une, se lit comme une entrée inachevée. La section reçoit 🚚 et l'entrée 📐 — le symbole que le rapport de qualité porte déjà dans son propre titre, puisque c'est la même chose. L'espacement suit une convention que ce menu tient sans la dire : une icône de largeur « neutre » — 🗄, 🖼 — s'affiche sur une colonne dans un terminal et prend DEUX espaces pour s'aligner, une icône large — 📏, 📐 — un seul. Se tromper décale la ligne, et rien ne le signale avant de l'avoir sous les yeux. Un test l'énonce désormais. Assisted-by: Claude Opus 5 --- script/todo/todo_i18n.py | 8 ++-- test/test_check_migration_quality.py | 69 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index 58198f3..aa3218e 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -5736,12 +5736,12 @@ TRANSLATIONS = { "en": "steps", }, "Migration": { - "fr": "Migration", - "en": "Migration", + "fr": "🚚 Migration", + "en": "🚚 Migration", }, "Quality of a migration, step by step": { - "fr": "Qualité d'une migration, palier par palier", - "en": "Quality of a migration, step by step", + "fr": "📐 Qualité d'une migration, palier par palier", + "en": "📐 Quality of a migration, step by step", }, "Quality": { "fr": "Qualité", diff --git a/test/test_check_migration_quality.py b/test/test_check_migration_quality.py index 21a8c49..b783271 100644 --- a/test/test_check_migration_quality.py +++ b/test/test_check_migration_quality.py @@ -512,6 +512,75 @@ class TestTheMissingFilesButton(Base): self.assertIn("press m", qtui.pane_text([], row)) +class TestTheMenuEntryLooksLikeItsNeighbours(Base): + """Une entrée sans icône au milieu d'un menu qui en a partout se lit + comme une entrée inachevée.""" + + ENTREES = ( + "Structure", + "Tables and database size", + "Customisation", + "Customised views, website copies included", + "Studio and hand-made x_ fields", + "Migration", + "Quality of a migration, step by step", + ) + + def libelle(self, cle, langue): + from script.todo import todo_i18n + + return todo_i18n.TRANSLATIONS[cle][langue] + + def test_every_entry_carries_an_icon(self): + import unicodedata + + for cle in self.ENTREES: + for langue in ("fr", "en"): + premier = self.libelle(cle, langue)[0] + self.assertNotEqual( + unicodedata.category(premier)[0], + "L", + f"{cle} ({langue}) : pas d'icône", + ) + + def test_the_spacing_follows_the_width_of_the_icon(self): + """Convention du menu, découverte en la lisant plutôt qu'écrite. + + Une icône de largeur « neutre » — 🗄, 🖼 — s'affiche sur une + colonne dans un terminal et prend DEUX espaces pour s'aligner ; + une icône large — 📏, 📐 — en prend un seul. Se tromper décale la + ligne, et rien ne le dit avant de l'avoir sous les yeux. + """ + import unicodedata + + for cle in self.ENTREES: + for langue in ("fr", "en"): + texte = self.libelle(cle, langue) + attendu = ( + 2 if unicodedata.east_asian_width(texte[0]) == "N" else 1 + ) + espaces = len(texte[1:]) - len(texte[1:].lstrip(" ")) + self.assertEqual( + espaces, attendu, f"{cle} ({langue}) : {texte!r}" + ) + + def test_the_quality_entry_wears_the_report_s_own_symbol(self): + # Le même symbole pour la même chose. Un libellé de MENU porte + # son icône dans la traduction — elle se traduit avec lui. + self.assertTrue( + self.libelle( + "Quality of a migration, step by step", "fr" + ).startswith("📐") + ) + # Le titre du rapport, lui, porte son symbole dans le CODE de + # rendu, à côté de ses voisins 📍, 🧪 et 🔷 : deux conventions + # distinctes, chacune tenable dans son contexte, et l'on vérifie + # chacune là où elle vit. + import inspect + + self.assertIn("📐", inspect.getsource(quality.render_text)) + + class TestItNeverWrites(Base): """Les bases de palier sont parfois la seule copie d'un état."""