message-bubble-stream-observability.test.tsx
1 // @vitest-environment jsdom 2 import { render, screen } from '@testing-library/react' 3 import { describe, expect, it } from 'vitest' 4 5 import { MessageBubble } from '@/features/chat/components/chat-page/message-bubble' 6 import type { ChatMessage } from '@/lib/shared/chat' 7 8 function buildAssistantMessage(text: string): ChatMessage { 9 return { 10 id: 'assistant-1', 11 sessionId: 'session-1', 12 role: 'assistant', 13 text, 14 createdAt: '2026-02-28T10:00:00.000Z', 15 attachments: [], 16 } 17 } 18 19 describe('MessageBubble stream observability', () => { 20 it('renders thinking timeline and stream timing footer', () => { 21 render( 22 <MessageBubble 23 message={buildAssistantMessage('Answer text')} 24 thinkingTimeline={[ 25 { 26 thinkingChunkId: 'thinking-1', 27 state: 'completed', 28 text: 'Planning steps...', 29 }, 30 ]} 31 streamMetrics={{ 32 latencyMs: 1234, 33 durationMs: 5678, 34 }} 35 />, 36 ) 37 38 expect(screen.getByText('Live Thinking')).not.toBeNull() 39 expect(screen.getByText(/Planning steps/)).not.toBeNull() 40 expect(screen.getByText(/latency: 1.23s • duration: 5.68s/)).not.toBeNull() 41 }) 42 43 it('shows in-progress duration label while streaming', () => { 44 render( 45 <MessageBubble 46 message={buildAssistantMessage('partial')} 47 isStreaming 48 streamMetrics={{ 49 latencyMs: 980, 50 durationMs: null, 51 }} 52 />, 53 ) 54 55 expect( 56 screen.getByText(/latency: 0.98s • duration: in progress/), 57 ).not.toBeNull() 58 }) 59 })