diff --git a/src/chat_tui/app.py b/src/chat_tui/app.py index 993988b..9f2bbeb 100644 --- a/src/chat_tui/app.py +++ b/src/chat_tui/app.py @@ -116,17 +116,20 @@ class ChatApp(App[None]): self.messages.extend((user_message, agent_message)) conversation = self.query_one("#conversation", VerticalScroll) - self._mount_message(conversation, user_message, "user-message") - self._mount_message(conversation, agent_message, "response-message") + self._mount_message(conversation, user_message) + self._mount_message(conversation, agent_message) conversation.scroll_end(animate=False) @staticmethod def _mount_message( conversation: VerticalScroll, message: ChatMessage, - style_class: str, ) -> None: - """메시지 역할을 라벨과 호환 가능한 구조적 스타일로 변환한다.""" + """메시지 역할에서 라벨과 구조적 스타일을 일관되게 파생한다.""" + style_class = { + MessageRole.USER: "user-message", + MessageRole.AGENT: "response-message", + }[message.role] text = f"{message.role.label}: {message.content}" conversation.mount( Label(text, classes=f"chat-message {style_class} {message.role.value}-role") diff --git a/tests/test_app.py b/tests/test_app.py index 6394c20..2bc2a3b 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import patch -from textual.widgets import Input, Label +from textual.widgets import Input from chat_tui.app import ( RESPONSE_MESSAGES, @@ -78,13 +78,46 @@ class ChatAppTest(unittest.IsolatedAsyncioTestCase): labels[1].classes, frozenset({"chat-message", "response-message", "agent-role"}), ) - self.assertIn("사용자:", str(labels[0].renderable)) - self.assertIn("에이전트:", str(labels[1].renderable)) + self.assertEqual( + str(labels[0].renderable), + "사용자: 역할 확인", + ) + self.assertEqual( + str(labels[1].renderable), + f"에이전트: {RESPONSE_MESSAGES[0]}", + ) self.assertIn("border-left", app.CSS) self.assertIn("border-right", app.CSS) self.assertIn("align: left middle", app.CSS) self.assertIn("align: right middle", app.CSS) + async def test_rendering_styles_are_derived_from_message_roles(self) -> None: + app = ChatApp() + async with app.run_test() as pilot: + conversation = app.query_one("#conversation") + app._mount_message( + conversation, + ChatMessage(MessageRole.USER, "사용자 본문"), + ) + app._mount_message( + conversation, + ChatMessage(MessageRole.AGENT, "에이전트 본문"), + ) + await pilot.pause() + + labels = list(app.query("#conversation Label")) + self.assertEqual(len(labels), 2) + self.assertEqual( + labels[0].classes, + frozenset({"chat-message", "user-message", "user-role"}), + ) + self.assertEqual( + labels[1].classes, + frozenset({"chat-message", "response-message", "agent-role"}), + ) + self.assertEqual(str(labels[0].renderable), "사용자: 사용자 본문") + self.assertEqual(str(labels[1].renderable), "에이전트: 에이전트 본문") + async def test_response_selection_uses_standard_random_choice(self) -> None: app = ChatApp() with patch( @@ -112,6 +145,7 @@ class ChatAppTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(app.selected_responses, []) self.assertEqual(app.messages, []) self.assertEqual(len(app.query("#conversation Label")), 0) + self.assertEqual(input_widget.value, "") choice_mock.assert_not_called() async def test_empty_input_is_not_added(self) -> None: