/ tests / message-bubble-stream-observability.test.tsx
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  })