From ff987eeae01748abb9fb1af17c3a007c3ec9f3ab Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Fri, 21 Aug 2026 23:07:54 -0400 Subject: [PATCH] [UPD] analyse: give every "go further" entry an icon, and guard it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Show every entry" was the only bare label in menus where every other line carried one, so it read as an oversight -- and it was. It takes the 📜 that "Show every table" already uses for the same gesture, and "List the known packages" gets one too rather than being left as the last bare line. The guard reads the labels straight out of the _analyse_follow_up calls and checks both languages, so the next entry added without an icon fails here instead of being noticed six months later. A second test bounds the extraction: were it to stop finding anything, the first would pass while checking nothing. --- FR --- « Tout afficher » était le seul libellé nu dans des menus où toutes les autres lignes en portaient une : ça se lisait comme un oubli, et c'en était un. Il prend le 📜 qu'« Afficher toutes les tables » utilise déjà pour le même geste, et « Lister les packages connus » en reçoit une plutôt que de rester la dernière ligne nue. Le garde lit les libellés directement dans les appels à _analyse_follow_up et vérifie les deux langues : la prochaine entrée sans icône tombera là, pas dans l'œil de quelqu'un six mois plus tard. Un second test borne l'extraction — si elle ne trouvait plus rien, le premier passerait sans rien vérifier. Assisted-by: Claude Opus 5 --- script/todo/todo_i18n.py | 8 +++--- test/test_check_filestore.py | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index d08c565..89918d7 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -6115,12 +6115,12 @@ TRANSLATIONS = { "en": "📦 Modules missing from the default package", }, "Show every entry": { - "fr": "Tout afficher", - "en": "Show every entry", + "fr": "📜 Tout afficher", + "en": "📜 Show every entry", }, "List the known packages": { - "fr": "Lister les packages connus", - "en": "List the known packages", + "fr": "📚 Lister les packages connus", + "en": "📚 List the known packages", }, "Already repaired: the group exists.": { "fr": "Déjà réparé : le groupe existe.", diff --git a/test/test_check_filestore.py b/test/test_check_filestore.py index 3834252..0afd69a 100644 --- a/test/test_check_filestore.py +++ b/test/test_check_filestore.py @@ -730,6 +730,62 @@ class TestTheExitCodes(unittest.TestCase): self.assertEqual(code, 2) +class TestTheFollowUpIcons(unittest.TestCase): + """Chaque entrée « Aller plus loin » porte une icône, ou aucune. + + Une seule entrée nue au milieu d'entrées ornées se lit comme un + oubli — et c'en est un. Le garde vaut mieux qu'une relecture : la + prochaine entrée ajoutée sans icône tombera ici, pas dans l'œil de + quelqu'un six mois plus tard. + """ + + RACINE = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) + + def libelles(self): + """Les clés passées en `prompt_description` à `_analyse_follow_up`.""" + import ast + + with io.open( + os.path.join(self.RACINE, "script", "todo", "todo.py"), + encoding="utf-8", + ) as handle: + arbre = ast.parse(handle.read()) + trouves = [] + for node in ast.walk(arbre): + if not ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) + and node.func.attr == "_analyse_follow_up" + and node.args + ): + continue + for element in ast.walk(node.args[0]): + if ( + isinstance(element, ast.Call) + and isinstance(element.func, ast.Name) + and element.func.id == "t" + and element.args + and isinstance(element.args[0], ast.Constant) + ): + trouves.append(element.args[0].value) + return trouves + + def test_there_are_follow_up_entries_to_check(self): + # Sans cette borne, un jour où l'extraction ne trouve plus rien, + # le test suivant passerait en ne vérifiant rien du tout. + self.assertGreater(len(self.libelles()), 5) + + def test_every_follow_up_entry_carries_an_icon(self): + for cle in self.libelles(): + for langue in ("fr", "en"): + texte = todo_i18n.TRANSLATIONS.get(cle, {}).get(langue, cle) + self.assertGreaterEqual( + ord(texte[0]), + 0x1F300, + f"entrée sans icône [{langue}] : {texte!r}", + ) + + class TestTranslations(unittest.TestCase): def test_every_key_exists(self): import ast