mirror of
https://github.com/ggozad/oterm.git
synced 2026-10-10 09:13:20 +02:00
176 lines
5.3 KiB
Python
176 lines
5.3 KiB
Python
from textual.app import App, ComposeResult
|
|
from textual.widgets import TextArea
|
|
|
|
from oterm.app.widgets.prompt import FlexibleInput
|
|
|
|
|
|
class _Host(App):
|
|
def compose(self) -> ComposeResult:
|
|
yield FlexibleInput("", id="prompt")
|
|
|
|
|
|
async def test_mount_focuses_textarea():
|
|
app = _Host()
|
|
async with app.run_test():
|
|
focused = app.focused
|
|
assert isinstance(focused, TextArea)
|
|
assert focused.id == "promptArea"
|
|
|
|
|
|
async def test_clear_resets_text_and_textarea():
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
flex.text = "hello"
|
|
await pilot.pause()
|
|
assert flex.query_one("#promptArea", TextArea).text == "hello"
|
|
|
|
flex.clear()
|
|
await pilot.pause()
|
|
assert flex.text == ""
|
|
assert flex.query_one("#promptArea", TextArea).text == ""
|
|
|
|
|
|
async def test_submit_from_textarea_posts_message():
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
received: list[str] = []
|
|
original = flex.post_message
|
|
|
|
def record(msg):
|
|
if isinstance(msg, FlexibleInput.Submitted):
|
|
received.append(msg.value)
|
|
return original(msg)
|
|
|
|
flex.post_message = record # ty: ignore[invalid-assignment]
|
|
|
|
ta = flex.query_one("#promptArea", TextArea)
|
|
ta.text = "multi-line body"
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
assert received == ["multi-line body"]
|
|
|
|
|
|
async def test_textarea_changed_updates_flex_text():
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
ta = flex.query_one("#promptArea", TextArea)
|
|
ta.text = "line a\nline b"
|
|
await pilot.pause()
|
|
assert flex.text == "line a\nline b"
|
|
|
|
|
|
async def test_focus_delegates_to_textarea():
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
flex.focus()
|
|
await pilot.pause()
|
|
assert isinstance(app.focused, TextArea)
|
|
|
|
|
|
async def test_shift_enter_inserts_newline():
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
ta = flex.query_one("#promptArea", TextArea)
|
|
ta.text = "first"
|
|
ta.cursor_location = (0, 5)
|
|
await pilot.press("shift+enter")
|
|
await pilot.pause()
|
|
assert ta.text == "first\n"
|
|
|
|
|
|
async def test_add_image_action_pushes_image_select_screen(monkeypatch):
|
|
from oterm.app.image_browser import ImageSelect
|
|
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
flex.action_add_image()
|
|
await pilot.pause()
|
|
assert isinstance(app.screen, ImageSelect)
|
|
|
|
|
|
async def test_backspace_with_selection_uses_super():
|
|
"""A non-empty selection deletes via TextArea.action_delete_left, not token logic."""
|
|
from oterm.app.widgets.prompt import PostableTextArea
|
|
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
ta = flex.query_one("#promptArea", PostableTextArea)
|
|
ta.text = "hello world"
|
|
ta.selection = ta.selection.__class__((0, 0), (0, 5))
|
|
await pilot.pause()
|
|
ta.action_delete_left()
|
|
await pilot.pause()
|
|
assert ta.text == " world"
|
|
|
|
|
|
async def test_delete_with_selection_uses_super():
|
|
from oterm.app.widgets.prompt import PostableTextArea
|
|
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
ta = flex.query_one("#promptArea", PostableTextArea)
|
|
ta.text = "hello world"
|
|
ta.selection = ta.selection.__class__((0, 0), (0, 5))
|
|
await pilot.pause()
|
|
ta.action_delete_right()
|
|
await pilot.pause()
|
|
assert ta.text == " world"
|
|
|
|
|
|
async def test_delete_in_plain_text_no_token():
|
|
from oterm.app.widgets.prompt import PostableTextArea
|
|
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
ta = flex.query_one("#promptArea", PostableTextArea)
|
|
ta.text = "abc"
|
|
ta.cursor_location = (0, 1)
|
|
await pilot.pause()
|
|
ta.action_delete_right()
|
|
await pilot.pause()
|
|
assert ta.text == "ac"
|
|
|
|
|
|
async def test_add_image_callback_posts_image_added_message():
|
|
"""When the image screen returns a (path, b64), FlexibleInput posts ImageAdded."""
|
|
from pathlib import Path
|
|
|
|
from oterm.app.widgets.image import ImageAdded
|
|
|
|
app = _Host()
|
|
async with app.run_test() as pilot:
|
|
flex = app.query_one(FlexibleInput)
|
|
received: list = []
|
|
original = flex.post_message
|
|
|
|
def record(msg):
|
|
if isinstance(msg, ImageAdded):
|
|
received.append((msg.path, msg.image))
|
|
return original(msg)
|
|
|
|
flex.post_message = record # ty: ignore[invalid-assignment]
|
|
|
|
received_calls: list = []
|
|
|
|
def capture_push(screen, callback):
|
|
received_calls.append((screen, callback))
|
|
|
|
flex.app.push_screen = capture_push # ty: ignore[invalid-assignment]
|
|
flex.action_add_image()
|
|
await pilot.pause()
|
|
_, cb = received_calls[0]
|
|
await cb((Path("/tmp/img.png"), "b64"))
|
|
await cb(None) # callback with None is a no-op
|
|
await pilot.pause()
|
|
|
|
assert received == [(Path("/tmp/img.png"), "b64")]
|