From d31e9149c7f141b2d3c14a79e9f067d524c8c420 Mon Sep 17 00:00:00 2001 From: historia Date: Sun, 30 Aug 2026 04:29:13 -0400 Subject: fix: menu highlight numbering in tui --- app/tests/test_tui.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'app/tests/test_tui.py') diff --git a/app/tests/test_tui.py b/app/tests/test_tui.py index 72465c1..085a1f1 100644 --- a/app/tests/test_tui.py +++ b/app/tests/test_tui.py @@ -306,6 +306,21 @@ class MenuTests(TuiTestCase): if text == "second option") self.assertEqual(arrows, [y_first, y_second]) + def test_corner_indicator_counts_highlighted_options(self): + # The corner " n/n " names the highlighted option, not the + # first visible line: with the list still scrolled to the top, + # two Downs must read 2/20 then 3/20 (the old label stayed at + # 1/ until entries scrolled off-screen). + options = [(f"option {i}", i) for i in range(20)] + screen = FakeScreen(keys=[FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN, + 10]) + value = tui.menu(screen, "Pick one", options, + help_lines=["Help text"]) + self.assertEqual(value, 2) + labels = [text for _, _, text, _ in screen.strings + if text in (" 1/20 ", " 2/20 ", " 3/20 ")] + self.assertEqual(labels, [" 1/20 ", " 2/20 ", " 3/20 "]) + class MenuTableTests(TuiTestCase): """The optional status table: aligned columns and colored statuses.""" @@ -1103,6 +1118,20 @@ class BrowseDirectoryTests(TuiTestCase): self.assertEqual(chosen, (self.root / "alpha").resolve()) self.assert_inside_border(screen) + def test_corner_indicator_counts_use_and_parent_rows(self): + # "[ Use this directory ]" and ".." are selectable rows like + # any other: with the 23 subdirectories (3 from setUp + 20), + # moving onto the first one ("alpha/") reads 3/25 in the corner. + for i in range(20): + (self.root / f"dir{i:02d}").mkdir() + screen = FakeScreen(keys=[FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN, + 27]) + with self.assertRaises(tui.WizardCancelled): + tui.browse_directory(screen, "Pick", start=self.root) + labels = [text for _, _, text, _ in screen.strings + if text in (" 1/25 ", " 2/25 ", " 3/25 ")] + self.assertEqual(labels, [" 1/25 ", " 2/25 ", " 3/25 "]) + def test_esc_returns_back_value(self): marker = object() screen = FakeScreen(keys=[27]) @@ -1407,6 +1436,56 @@ class FrameStatusTests(TuiTestCase): self.assertEqual(footer_y, status_y + 1) +class FrameIndicatorTests(TuiTestCase): + """The corner overflow label's default reading. + + With a cursor the " n/n " counts selectable rows (which entry is + highlighted); without one it keeps the first-visible-line reading. + The Help screen's viewer overrides the label entirely, so nothing + here touches its " lines x-y of z " counter. + """ + + def _frame(self): + frame = tui.Frame(self.screen, "Title", "footer") + for i in range(30): + frame.mark(f"row {i}", selectable=True, align="left") + return frame + + def test_label_counts_selectable_rows_with_a_cursor(self): + # 30 rows with 17 visible: the cursor on the third row reads + # 3/30 wherever the viewport sits. + frame = self._frame() + frame.cursor = 2 + frame.draw() + self.assertIn(" 3/30 ", + [t for _, _, t, _ in self.screen.strings]) + + def test_label_falls_back_to_visible_lines_without_a_cursor(self): + # Frames without a cursor (e.g. focus on a form's buttons) + # keep the old first-visible-line reading. + frame = self._frame() + frame.draw() + self.assertIn(" 1/30 ", + [t for _, _, t, _ in self.screen.strings]) + frame.scroll = 10 + frame.draw() + self.assertIn(" 11/30 ", + [t for _, _, t, _ in self.screen.strings]) + + def test_non_selectable_rows_are_not_numbered(self): + # Help/note rows do not shift the count: two unselectable rows + # above 20 selectable ones put the third entry at 3/20. + frame = tui.Frame(self.screen, "Title", "footer") + frame.mark("note one") + frame.mark("note two") + for i in range(20): + frame.mark(f"row {i}", selectable=True, align="left") + frame.cursor = 4 # the third selectable row + frame.draw() + self.assertIn(" 3/20 ", + [t for _, _, t, _ in self.screen.strings]) + + class TextViewerTests(TuiTestCase): """tui.text_viewer: a scrollable read-only dialog; Esc/q/Enter closes.""" -- cgit v1.2.3