package main import ( "strings" "testing" ) func TestFragmentUserMessage(t *testing.T) { f := FragmentUserMessage("Hello ", false) if !strings.Contains(f, "message-user") { t.Error("missing message-user class") } if !strings.Contains(f, "Hello <world>") { t.Error("should escape HTML") } if !strings.Contains(f, `hx-swap-oob="beforeend"`) { t.Error("missing OOB swap") } if strings.Contains(f, "message-user-plan") { t.Error("should not have plan class in code mode") } // Plan mode fp := FragmentUserMessage("analyze this", true) if !strings.Contains(fp, "message-user-plan") { t.Error("missing plan class in plan mode") } } func TestFragmentAssistantStart(t *testing.T) { f := FragmentAssistantStart("msg-1") if !strings.Contains(f, `id="msg-1"`) { t.Error("missing message ID") } if !strings.Contains(f, "message-assistant") { t.Error("missing message-assistant class") } } func TestFragmentAssistantChunk(t *testing.T) { f := FragmentAssistantChunk("msg-1", "chunk") if !strings.Contains(f, `id="msg-1"`) { t.Error("missing message ID") } if !strings.Contains(f, "chunk<text>") { t.Error("should escape HTML") } } func TestFragmentAssistantComplete(t *testing.T) { f := FragmentAssistantComplete("msg-1", "

Hello

") if !strings.Contains(f, `id="msg-1"`) { t.Error("missing message ID") } if !strings.Contains(f, "

Hello

") { t.Error("should include raw HTML content") } } func TestFragmentToolCall(t *testing.T) { f := FragmentToolCall("Read", `{"file_path":"/tmp/test.go"}`) if !strings.Contains(f, "message-tool") { t.Error("missing message-tool class") } if !strings.Contains(f, "Read") { t.Error("missing tool name") } if !strings.Contains(f, "/tmp/test.go") { t.Error("should show file path, not raw JSON") } if strings.Contains(f, "file_path") { t.Error("should not show JSON key name for Read") } } func TestFragmentToolCallBash(t *testing.T) { f := FragmentToolCall("Bash", `{"command":"git status"}`) if !strings.Contains(f, "") { t.Error("Bash command should be in code tag") } if !strings.Contains(f, "git status") { t.Error("should show command") } } func TestFragmentToolCallGrep(t *testing.T) { f := FragmentToolCall("Grep", `{"pattern":"TODO","path":"/root/projects"}`) if !strings.Contains(f, "TODO") { t.Error("should show pattern") } if !strings.Contains(f, "/root/projects") { t.Error("should show path") } } func TestFragmentToolCallEdit(t *testing.T) { f := FragmentToolCall("Edit", `{"file_path":"/tmp/main.go","old_string":"foo","new_string":"bar"}`) if !strings.Contains(f, "/tmp/main.go") { t.Error("should show file path") } } func TestFragmentToolCallFallback(t *testing.T) { f := FragmentToolCall("UnknownTool", `{"key1":"value1","key2":"value2"}`) if !strings.Contains(f, "key1") || !strings.Contains(f, "value1") { t.Error("fallback should show key=value pairs") } } func TestFragmentToolCallNonJSON(t *testing.T) { f := FragmentToolCall("Something", "plain text input") if !strings.Contains(f, "plain text input") { t.Error("should show plain text for non-JSON") } } func TestFragmentToolCallTruncation(t *testing.T) { longInput := strings.Repeat("x", 300) f := FragmentToolCall("Something", longInput) if !strings.Contains(f, "...") { t.Error("should truncate long input") } } func TestFragmentSystemMessage(t *testing.T) { f := FragmentSystemMessage("Connected") if !strings.Contains(f, "message-system") { t.Error("missing message-system class") } if !strings.Contains(f, "Connected") { t.Error("missing text") } } func TestFragmentTypingIndicator(t *testing.T) { show := FragmentTypingIndicator(true) if !strings.Contains(show, "typing-indicator") { t.Error("missing typing indicator") } hide := FragmentTypingIndicator(false) if !strings.Contains(hide, `id="typing-indicator"`) { t.Error("missing ID") } } func TestFragmentStatus(t *testing.T) { connected := FragmentStatus(true) if !strings.Contains(connected, "connected") { t.Error("missing connected class") } disconnected := FragmentStatus(false) if strings.Contains(disconnected, `class="status connected"`) { t.Error("should not have connected class when disconnected") } } func TestFragmentClearInput(t *testing.T) { f := FragmentClearInput() if !strings.Contains(f, `id="message-input"`) { t.Error("missing input ID") } } func TestFragmentCombine(t *testing.T) { combined := FragmentCombine("a", "b", "c") if combined != "a\nb\nc" { t.Errorf("got %q", combined) } }