diff options
Diffstat (limited to 'app/tests/test_tui.py')
| -rw-r--r-- | app/tests/test_tui.py | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/app/tests/test_tui.py b/app/tests/test_tui.py index d121c9a..ce408af 100644 --- a/app/tests/test_tui.py +++ b/app/tests/test_tui.py @@ -542,6 +542,150 @@ class FormTests(TuiTestCase): # math highlighted the blank line). self.assertEqual(texts[cursor], "Chunk: 250") + def test_custom_button_labels_and_start_on_buttons(self): + # buttons= overrides the labels; start_on_buttons=True means Enter + # alone (with no navigation) accepts the defaults. + screen = FakeScreen(keys=[10]) + result = tui.form(screen, "Convert", self._fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"fmt": "m4b", "chunk": "250"}) + texts = [text for _, _, text, _ in screen.strings] + self.assertIn("[ Generate! ]", texts) + self.assertIn("[ Cancel ]", texts) + self.assertNotIn("[ Save ]", texts) + + def test_bool_field_toggles_with_enter(self): + fields = [{"key": "combine", "label": "Combine", "kind": "bool", + "value": False}] + # Enter toggles the bool to True, Tab -> button, Enter saves. + screen = FakeScreen(keys=[10, 9, 10]) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"combine": True}) + + def test_bool_field_toggles_with_space(self): + fields = [{"key": "combine", "label": "Combine", "kind": "bool", + "value": False}] + screen = FakeScreen(keys=[ord(" "), 9, 10]) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"combine": True}) + + def test_hidden_field_keeps_value_and_is_not_drawn(self): + fields = self._fields() + fields.append({"key": "hidden", "label": "Secret", "kind": "text", + "value": "kept", "visible": False}) + screen = FakeScreen(keys=[9, 10]) + result = tui.form(screen, "Settings", fields) + # The hidden field's value is preserved and returned. + self.assertEqual(result, {"fmt": "m4b", "chunk": "250", + "hidden": "kept"}) + texts = [text for _, _, text, _ in screen.strings] + self.assertNotIn("Secret:", texts) + + def test_visible_callable_hides_row_after_change(self): + fields = [ + {"key": "fmt", "label": "Format", "kind": "choice", + "value": "mp3", "choices": ["mp3", "m4b"]}, + {"key": "combine", "label": "Combine", "kind": "bool", + "value": False, + "visible": lambda fs: next( + f["value"] for f in fs if f["key"] == "fmt") != "m4b"}, + ] + snapshots = [] + original_draw = tui.Frame.draw + + def spy(frame): + original_draw(frame) + snapshots.append([row["text"] if row["segments"] is None + else "".join(t for t, _ in row["segments"]) + for row in frame.rows]) + + # Enter on Format, Down to m4b, Enter; Tab -> button, Enter. + screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, 10, 9, 10]) + with patch.object(tui.Frame, "draw", spy): + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"fmt": "m4b", "combine": False}) + self.assertTrue(any("Combine:" in row for row in snapshots[0])) + self.assertFalse(any("Combine:" in row for row in snapshots[-1])) + + def test_callable_choices_resolve_at_open(self): + fields = [{"key": "fmt", "label": "Format", "kind": "choice", + "value": "a", "choices": lambda fs: ["a", "b", "c"]}] + screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, 10, 9, 10]) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"fmt": "b"}) + + def test_on_change_fires_after_value_change(self): + calls = [] + fields = [{"key": "fmt", "label": "Format", "kind": "choice", + "value": "a", "choices": ["a", "b"], + "on_change": lambda fs: calls.append("changed")}] + screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, 10, 9, 10]) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"fmt": "b"}) + self.assertEqual(calls, ["changed"]) + + def test_choice_field_validation_refuses_save(self): + fields = [ + {"key": "fmt", "label": "Format", "kind": "choice", + "value": "bad", "choices": ["good", "bad"], + "validate": lambda v: None if v == "good" else "pick good"}, + {"key": "other", "label": "Other", "kind": "text", "value": "x"}, + ] + # Tab -> Save fails on fmt; the flash consumes the next key; then + # re-open fmt, Down to 'good', Enter; Tab -> Save, Enter. + keys = [9, 10, 10, 10, FakeCurses.KEY_DOWN, 10, 9, 10] + screen = FakeScreen(keys=keys) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"fmt": "good", "other": "x"}) + + def _two_text_fields(self): + return [{"key": "first", "label": "First", "kind": "text", + "value": "a"}, + {"key": "second", "label": "Second", "kind": "text", + "value": "b"}] + + def test_up_from_buttons_lands_on_last_field(self): + # Focus starts on Generate!; Up wraps up through it onto the last + # field (the list wraps: Up from the first button -> the bottom). + screen = FakeScreen(keys=[ + FakeCurses.KEY_UP, 10, ord("x"), 10, 9, 10]) + result = tui.form(screen, "Convert", self._two_text_fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"first": "a", "second": "bx"}) + + def test_down_from_buttons_lands_on_first_field(self): + # Down wraps down through the buttons back onto the first field. + screen = FakeScreen(keys=[ + FakeCurses.KEY_DOWN, 10, ord("x"), 10, 9, 10]) + result = tui.form(screen, "Convert", self._two_text_fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"first": "ax", "second": "b"}) + + def test_k_from_buttons_lands_on_last_field(self): + screen = FakeScreen(keys=[ord("k"), 10, ord("x"), 10, 9, 10]) + result = tui.form(screen, "Convert", self._two_text_fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"first": "a", "second": "bx"}) + + def test_tab_from_buttons_lands_on_first_field(self): + screen = FakeScreen(keys=[9, 10, ord("x"), 10, 9, 10]) + result = tui.form(screen, "Convert", self._two_text_fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"first": "ax", "second": "b"}) + + def test_btab_from_buttons_lands_on_last_field(self): + screen = FakeScreen(keys=[FakeCurses.KEY_BTAB, 10, ord("x"), 10, + 9, 10]) + result = tui.form(screen, "Convert", self._two_text_fields(), + buttons=("Generate!", "Cancel"), + start_on_buttons=True) + self.assertEqual(result, {"first": "a", "second": "bx"}) + def _accept_audio_cpp(entry: Path): """auto_select callback that accepts an 'audio.cpp' checkout root.""" |
