Four papercuts met while using the CLI: the test menu returned a verdict nobody could find at the end of the output, the file browser never closed once a file was picked, a wrong KeePass password killed the whole CLI, and a configuration key had drifted from the code that reads it. --- FR --- Quatre irritants rencontrés à l'usage : le menu Test rendait un verdict introuvable en fin de sortie, le navigateur de fichiers ne se fermait jamais une fois le fichier choisi, un mauvais mot de passe KeePass tuait tout le CLI, et une clé de configuration avait dérivé du code qui la lit. Assisted-by: Claude Opus 5
126 lines
4.4 KiB
Python
126 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
"""Le sélecteur de fichier : choisir doit fermer l'écran, annuler doit exister.
|
|
|
|
Le défaut que ces tests verrouillent : `select_file` appelait bien le callback
|
|
mais ne quittait pas la boucle urwid. Le choix était donc enregistré et
|
|
l'écran restait ouvert, sans aucune touche pour le fermer — le sélecteur
|
|
paraissait figé au moment précis où il venait de faire son travail. Seul
|
|
Ctrl+C en sortait, ce qui abandonnait l'opération.
|
|
|
|
`ExitMainLoop` est ce qui termine une boucle urwid : la lever EST la
|
|
fermeture. Ces tests l'attendent donc comme un succès, pas comme une erreur.
|
|
"""
|
|
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
|
|
import urwid
|
|
|
|
from script.todo.todo_file_browser import FileBrowser
|
|
|
|
|
|
class BrowserCase(unittest.TestCase):
|
|
def setUp(self):
|
|
self.directory = tempfile.mkdtemp()
|
|
open(os.path.join(self.directory, "sauvegarde.zip"), "w").close()
|
|
os.mkdir(os.path.join(self.directory, "sous_dossier"))
|
|
self.chosen = []
|
|
|
|
def browser(self, **kwargs):
|
|
return FileBrowser(self.directory, self.chosen.append, **kwargs)
|
|
|
|
def button(self, browser, label):
|
|
for widget in browser.list_walker:
|
|
if isinstance(widget, urwid.Button) and widget.label == label:
|
|
return widget
|
|
raise AssertionError(f"bouton '{label}' absent")
|
|
|
|
|
|
class TestChoosingClosesTheBrowser(BrowserCase):
|
|
def test_selecting_a_file_reports_it_and_ends_the_loop(self):
|
|
browser = self.browser()
|
|
with self.assertRaises(urwid.ExitMainLoop):
|
|
browser.select_file(self.button(browser, "sauvegarde.zip"))
|
|
self.assertEqual(
|
|
self.chosen, [os.path.join(self.directory, "sauvegarde.zip")]
|
|
)
|
|
|
|
def test_selecting_a_directory_reports_it_and_ends_the_loop(self):
|
|
browser = self.browser(open_dir=True)
|
|
with self.assertRaises(urwid.ExitMainLoop):
|
|
browser.select_directory(self.button(browser, "."))
|
|
self.assertEqual(self.chosen, [self.directory])
|
|
|
|
|
|
class TestNavigatingDoesNotClose(BrowserCase):
|
|
"""Se déplacer n'est pas choisir : la boucle doit continuer."""
|
|
|
|
def test_entering_a_directory(self):
|
|
browser = self.browser()
|
|
browser.open_directory(self.button(browser, "sous_dossier/"))
|
|
self.assertEqual(
|
|
browser.current_path, os.path.join(self.directory, "sous_dossier")
|
|
)
|
|
self.assertEqual(self.chosen, [])
|
|
|
|
def test_going_up(self):
|
|
browser = self.browser()
|
|
browser.go_up_directory(None)
|
|
self.assertEqual(browser.current_path, os.path.dirname(self.directory))
|
|
self.assertEqual(self.chosen, [])
|
|
|
|
def test_arrow_keys_do_not_quit(self):
|
|
browser = self.browser()
|
|
for key in ("up", "down", "enter", "a"):
|
|
browser.unhandled_input(key)
|
|
self.assertEqual(self.chosen, [])
|
|
|
|
|
|
class TestCancelling(BrowserCase):
|
|
"""Sortir sans choisir doit être possible.
|
|
|
|
Toutes les autres sorties sélectionnent quelque chose. Un appelant qui
|
|
propose une solution de rechange — taper un chemin — ne devient
|
|
atteignable que si l'on peut renoncer.
|
|
"""
|
|
|
|
def test_q_and_escape_quit_without_choosing(self):
|
|
for key in ("q", "Q", "esc"):
|
|
browser = self.browser()
|
|
with self.assertRaises(urwid.ExitMainLoop):
|
|
browser.unhandled_input(key)
|
|
self.assertEqual(self.chosen, [], key)
|
|
|
|
|
|
class TestListing(BrowserCase):
|
|
def test_files_are_offered_when_picking_a_file(self):
|
|
labels = [
|
|
w.label
|
|
for w in self.browser().list_walker
|
|
if isinstance(w, urwid.Button)
|
|
]
|
|
self.assertIn("sauvegarde.zip", labels)
|
|
self.assertIn("sous_dossier/", labels)
|
|
|
|
def test_files_are_hidden_when_picking_a_directory(self):
|
|
labels = [
|
|
w.label
|
|
for w in self.browser(open_dir=True).list_walker
|
|
if isinstance(w, urwid.Button)
|
|
]
|
|
self.assertNotIn("sauvegarde.zip", labels)
|
|
self.assertIn(".", labels)
|
|
|
|
def test_an_unreadable_directory_does_not_crash(self):
|
|
browser = self.browser()
|
|
browser.current_path = os.path.join(self.directory, "nowhere")
|
|
browser.refresh_list()
|
|
self.assertTrue(len(browser.list_walker) >= 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|