Files
2026-07-13 11:59:58 +08:00

119 lines
4.4 KiB
Python

"""Smoke tests for the pure Rich renderers — they must render without error
and include the expected text."""
from rich.console import Console
from deerflow.tui.render import render_header, render_status, render_transcript
from deerflow.tui.view_state import (
AssistantDelta,
RunEnded,
RunStarted,
SystemMessage,
ToolResult,
ToolStarted,
UserSubmitted,
initial_state,
reduce,
)
def _render_to_text(renderable) -> str:
console = Console(width=100, no_color=True)
with console.capture() as capture:
console.print(renderable)
return capture.get()
def test_render_empty_transcript_shows_hint():
out = _render_to_text(render_transcript(initial_state()))
assert "Type a message" in out
def test_render_transcript_includes_all_row_kinds():
state = initial_state()
state = reduce(state, UserSubmitted("hello there"))
state = reduce(state, AssistantDelta(id="m1", text="hi back"))
state = reduce(state, ToolStarted(tool_call_id="t1", tool_name="read_file", args={"path": "a.py"}))
state = reduce(state, ToolResult(tool_call_id="t1", content="file body", is_error=False))
state = reduce(state, SystemMessage("a note"))
out = _render_to_text(render_transcript(state))
assert "hello there" in out
assert "hi back" in out
assert "Read" in out and "a.py" in out
assert "file body" in out
assert "a note" in out
def test_finalized_assistant_renders_markdown():
state = initial_state()
state = reduce(state, AssistantDelta(id="m1", text="**bold** text\n\n## A Heading\n\n- item one"))
out = _render_to_text(render_transcript(state))
# Markdown is rendered: the syntax markers are consumed, the content remains.
assert "bold" in out
assert "**bold**" not in out
assert "A Heading" in out
assert "## A Heading" not in out
assert "item one" in out
def test_actively_streaming_assistant_stays_plain():
state = initial_state()
state = reduce(state, RunStarted())
state = reduce(state, AssistantDelta(id="m1", text="**partial heading ##"))
out = _render_to_text(render_transcript(state))
# The streaming row must NOT be markdown-rendered (avoids reflow jumpiness).
assert "**partial heading ##" in out
def test_prior_message_stays_markdown_when_a_followup_run_starts():
# Regression: sending a follow-up must NOT revert a finalized markdown answer
# to raw text. Between RunStarted and the new answer's first delta (and during
# the client's re-emit of prior messages), the previous answer is the last
# assistant row — it must still render as Markdown.
state = initial_state()
state = reduce(state, AssistantDelta(id="m1", text="**bold answer**"))
state = reduce(state, RunEnded())
state = reduce(state, UserSubmitted("follow up"))
state = reduce(state, RunStarted())
state = reduce(state, AssistantDelta(id="m1", text="**bold answer**")) # re-emit, no new answer yet
out = _render_to_text(render_transcript(state))
assert "**bold answer**" not in out # still Markdown-rendered
assert "bold answer" in out
def test_only_the_actively_streaming_message_is_plain():
state = initial_state()
state = reduce(state, AssistantDelta(id="m1", text="**done**"))
state = reduce(state, RunEnded())
state = reduce(state, RunStarted())
state = reduce(state, AssistantDelta(id="m2", text="**streaming now"))
out = _render_to_text(render_transcript(state))
assert "**streaming now" in out # active m2 stays plain
assert "**done**" not in out # finalized m1 stays Markdown
assert "done" in out
def test_render_status_ready_and_working():
ready = _render_to_text(render_status(initial_state(), model="gpt", thread_label="new"))
assert "ready" in ready
state = reduce(initial_state(), RunStarted())
working = _render_to_text(render_status(state, model="gpt", thread_label="new", spinner="*"))
assert "working" in working
def test_render_status_shows_token_usage():
state = reduce(initial_state(), RunEnded(usage={"total_tokens": 42}))
out = _render_to_text(render_status(state, model="gpt", thread_label="t1"))
assert "42 tok" in out
def test_render_header_includes_model_and_cwd():
out = _render_to_text(render_header(model="claude", thread_label="new", cwd="/tmp/proj", skills=3))
assert "DeerFlow" in out
assert "claude" in out
assert "/tmp/proj" in out