aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_tui.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-24 14:49:17 -0400
committerhistoria <historiavg@proton.me>2026-08-24 14:49:17 -0400
commit7ee1d4bb63c12982ec4900ec870ad96baba4b22b (patch)
tree3f4261201b1bf05546c7c9241f7f3bcb4ea42b5e /app/tests/test_tui.py
parente7a3d65f68659d17f37b79e8bfefea19d7ac0648 (diff)
downloadtts-audiobook-generator-7ee1d4bb63c12982ec4900ec870ad96baba4b22b.tar.gz
feat: combine wizard menus into a single generate options menu
Diffstat (limited to 'app/tests/test_tui.py')
-rw-r--r--app/tests/test_tui.py144
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."""