"""Tests for sub-agent card renderers (spawn_agent + nested styling)."""
from __future__ import annotations
from rich.console import Console
from clients.terminal.tui.renderers.planning import PlanningStatusRenderer, PlanReadyRenderer
from clients.terminal.tui.renderers.subagent import (
SpawnAgentStartedRenderer,
SpawnAgentResultRenderer,
)
from clients.terminal.tui.renderers.thinking import ThinkingRenderer
from clients.terminal.tui.renderers.tool import ToolStartedRenderer, ToolResultRenderer
from clients.terminal.tui.themes import set_active_theme
def _render_text(renderable) -> str:
console = Console(record=True, width=80, force_terminal=True, color_system=None)
console.print(renderable)
return console.export_text()
def test_spawn_started_card_shows_profile_and_task() -> None:
set_active_theme("gnexus-dark")
renderer = SpawnAgentStartedRenderer()
msg = {
"type": "tool_started",
"tool": "spawn_agent",
"args": {"task": "audit the auth flow", "profile_id": "developer"},
"is_subagent": False,
}
assert renderer.accepts(msg)
panel = renderer.render(msg)
assert "subagent" in str(panel.title)
assert "developer" in str(panel.title)
assert "audit the auth flow" in _render_text(panel)
def test_spawn_started_does_not_accept_other_tools() -> None:
renderer = SpawnAgentStartedRenderer()
assert not renderer.accepts({"type": "tool_started", "tool": "filesystem", "args": {}})
def test_spawn_result_card_shows_status() -> None:
set_active_theme("gnexus-dark")
renderer = SpawnAgentResultRenderer()
msg = {
"type": "tool_call",
"tool": "spawn_agent",
"args": {"profile_id": "developer"},
"result": "auth flow is fine",
"success": True,
"is_subagent": False,
}
assert renderer.accepts(msg)
panel = renderer.render(msg)
title = str(panel.title)
assert "developer" in title
assert "✓" in title
assert "auth flow is fine" in _render_text(panel)
def test_subagent_thinking_is_indented_and_labeled() -> None:
set_active_theme("gnexus-dark")
renderer = ThinkingRenderer()
msg = {"type": "thinking_block", "content": "hmm nested", "is_subagent": True}
out = _render_text(renderer.render(msg))
assert "subagent thinking" in out
assert "hmm nested" in out
# Indented: the content lines start with spaces.
assert any(line.startswith(" ") for line in out.splitlines())
def test_subagent_planning_status_is_indented() -> None:
set_active_theme("gnexus-dark")
renderer = PlanningStatusRenderer()
msg = {"type": "planning_status", "phase": 1, "label": "Analysis", "is_subagent": True}
out = _render_text(renderer.render(msg))
assert "(subagent)" in out
assert any(line.startswith(" ") for line in out.splitlines())
def test_subagent_plan_card_is_indented() -> None:
set_active_theme("gnexus-dark")
renderer = PlanReadyRenderer()
msg = {"type": "plan_ready", "plan": "## Plan\n**Steps:**\n1. x\n", "is_subagent": True}
out = _render_text(renderer.render(msg))
assert "subagent plan" in out
assert any(line.startswith(" ") for line in out.splitlines())
def test_subagent_tool_card_is_indented() -> None:
set_active_theme("gnexus-dark")
started = ToolStartedRenderer().render(
{"type": "tool_started", "tool": "filesystem", "args": {}, "is_subagent": True}
)
result = ToolResultRenderer().render(
{"type": "tool_call", "tool": "filesystem", "result": "ok", "success": True, "is_subagent": True}
)
assert any(line.startswith(" ") for line in _render_text(started).splitlines())
assert any(line.startswith(" ") for line in _render_text(result).splitlines())
def test_non_subagent_tool_card_not_indented() -> None:
set_active_theme("gnexus-dark")
started = ToolStartedRenderer().render(
{"type": "tool_started", "tool": "filesystem", "args": {}, "is_subagent": False}
)
# A top-level card has border lines starting at column 0 (e.g. "╭──").
assert any(line.startswith("╭") for line in _render_text(started).splitlines())