oterm/tests/modals/test_chat_export.py
Yiorgis Gozadinos 28206897ac
Simplify the codebase and remove comments with no use
- Drop throttle and debounce; LogViewer polls with set_interval.
- OTerm actions share an _active_chat() lookup.
- ChatEdit dismisses with a ChatModel instead of JSON, drops the
  supported-settings filter (every parameter it offers is in the base
  ModelSettings, so the filter never removed anything), and holds its
  state in plain attributes instead of unwatched reactives.
- ToolMeta is a TypedDict; resolve_api_key and BUILTIN_OPENAI_COMPAT
  are public since agent.py imports them.
- Ollama URL helpers derive from one another and share one client
  constructor.
- Store maps rows to ChatModel in one place; build_user_prompt decodes
  images in one place.
- Remove mutable default arguments, unused attributes and parameters,
  and four tcss rules that match nothing.
- Rename supress_logging to suppress_logging.
- Delete comments that restate the code; correct ones that were false
  (the logging comment in cli, the pydantic-ai 2.0 note in the 0.18.0
  upgrade, a stale Textual version in cancel_streams).
2026-09-26 16:23:23 +03:00

77 lines
2.4 KiB
Python

from textual.app import App
from textual.widgets import Input
from oterm.app.chat_export import ChatExport, slugify
from oterm.types import ChatModel, MessageModel
class TestSlugify:
def test_lowercases_and_hyphenates(self):
assert slugify("Hello World!") == "hello-world"
def test_strips_diacritics(self):
assert slugify("café crème") == "cafe-creme"
def test_collapses_multiple_whitespace(self):
assert slugify("a b c") == "a-b-c"
class _Host(App):
pass
async def test_escape_cancels(tmp_path):
app = _Host()
async with app.run_test() as pilot:
received: list[str | None] = []
app.push_screen(
ChatExport(chat_id=1, file_name=str(tmp_path / "chat.md")),
lambda r: received.append(r),
)
await pilot.pause()
await pilot.press("escape")
await pilot.pause()
assert received == [None]
async def test_submit_writes_file_and_dismisses(tmp_path, store):
chat_id = await store.save_chat(ChatModel(name="c", model="m"))
for role, text in (("user", "hi"), ("assistant", "hello")):
await store.save_message(MessageModel(chat_id=chat_id, role=role, text=text))
out = tmp_path / "chat.md"
app = _Host()
async with app.run_test() as pilot:
received: list[str | None] = []
screen = ChatExport(chat_id=chat_id, file_name=str(out))
app.push_screen(screen, lambda r: received.append(r))
await pilot.pause()
inp = screen.query_one("#chat-name-input", Input)
inp.value = str(out)
await pilot.press("enter")
await pilot.pause()
content = out.read_text()
assert "*user*" in content
assert "hi" in content
assert "*assistant*" in content
assert "hello" in content
# Dismissed with no value: on_submit ends with dismiss().
assert received == [None]
async def test_empty_file_name_is_ignored(tmp_path, store):
await store.save_chat(ChatModel(name="c", model="m"))
app = _Host()
async with app.run_test() as pilot:
received: list[str | None] = []
screen = ChatExport(chat_id=1, file_name="")
app.push_screen(screen, lambda r: received.append(r))
await pilot.pause()
inp = screen.query_one("#chat-name-input", Input)
inp.value = ""
await pilot.press("enter")
await pilot.pause()
assert received == []