CoolFace
Apppublic

jim-bo/cli-textual-demo

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
test_tools_command.py105 linesDownload Raw Back to unit
1import pytest2from unittest.mock import patch, MagicMock3from pydantic_ai.models.test import TestModel4from textual.widgets import Label, OptionList, Static5from cli_textual.app import ChatApp6from cli_textual.plugins.commands.tools import ToolsWidget, ToolsCommand, _first_line7from cli_textual.agents.manager import manager_agent8 9 10# ---------------------------------------------------------------------------11# _first_line helper12# ---------------------------------------------------------------------------13 14def test_first_line_returns_first_non_empty():15    assert _first_line("\n\n  Hello world\n  more text") == "Hello world"16 17def test_first_line_empty_string():18    assert _first_line("") == ""19 20def test_first_line_only_whitespace():21    assert _first_line("   \n  \n") == ""22 23 24# ---------------------------------------------------------------------------25# ToolsWidget26# ---------------------------------------------------------------------------27 28@pytest.mark.asyncio29async def test_tools_widget_composes_option_list():30    """ToolsWidget should render an OptionList with one item per tool."""31    app = ChatApp()32    async with app.run_test(size=(120, 40)) as pilot:33        widget = ToolsWidget()34        history = app.query_one("#history-container")35        await history.mount(widget)36        await pilot.pause(0.1)37 38        option_list = widget.query_one("#tools-option-list", OptionList)39        assert option_list is not None40 41        tool_count = len(manager_agent._function_toolset.tools)42        assert option_list.option_count == tool_count43 44 45@pytest.mark.asyncio46async def test_tools_widget_shows_detail_on_selection():47    """Selecting a tool in the OptionList should swap to the detail view."""48    app = ChatApp()49    async with app.run_test(size=(120, 40)) as pilot:50        widget = ToolsWidget()51        history = app.query_one("#history-container")52        await history.mount(widget)53        await pilot.pause(0.1)54 55        option_list = widget.query_one("#tools-option-list", OptionList)56        option_list.focus()57        await pilot.pause(0.05)58 59        # Select the first item60        await pilot.press("enter")61        await pilot.pause(0.2)62 63        # OptionList should be gone, Static detail should be present64        assert not widget.query("#tools-option-list")65        assert widget.query(".tool-detail")66 67 68@pytest.mark.asyncio69async def test_tools_widget_detail_contains_tool_name():70    """Detail view Label should contain the selected tool's name."""71    first_tool_name = next(iter(manager_agent._function_toolset.tools))72 73    app = ChatApp()74    async with app.run_test(size=(120, 40)) as pilot:75        widget = ToolsWidget()76        history = app.query_one("#history-container")77        await history.mount(widget)78        await pilot.pause(0.1)79 80        option_list = widget.query_one("#tools-option-list", OptionList)81        option_list.focus()82        await pilot.pause(0.05)83        await pilot.press("enter")84        await pilot.pause(0.2)85 86        labels = list(widget.query(Label))87        assert any(first_tool_name in str(lbl.render()) for lbl in labels)88 89 90# ---------------------------------------------------------------------------91# ToolsCommand integration with ChatApp92# ---------------------------------------------------------------------------93 94@pytest.mark.asyncio95async def test_tools_command_mounts_widget():96    """/tools command should mount ToolsWidget into #interaction-container."""97    app = ChatApp()98    async with app.run_test(size=(120, 40)) as pilot:99        await pilot.press(*"/tools", "enter")100        await pilot.pause(0.3)101 102        container = app.query_one("#interaction-container")103        assert "visible" in container.classes104        assert container.query(ToolsWidget)105