// @vitest-environment jsdom // Remaining chat branch tails: MessageItem context/unknown/steering arms, // StatsLine no-cache join, PendingCard reason strip, AssistantMarkdown // single-line reasoning. (Tool-row dispatch tails live with the keyed-slot // machinery specs since the tool ring dissolved into renderSlot.) import { afterEach, describe, expect, it, vi } from 'vitest' import { cleanup, render } from '@testing-library/react' import { RpcId } from '@deepseek-ai/dsh-client-connection/client' import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client' import { PendingWait } from '@deepseek-ai/dsh-client-runtime/client' import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react' import { MessageItem } from '../src/client/chat/MessageItem.tsx' import { PendingCard } from '../src/client/chat/PendingCard.tsx' import { AssistantMarkdown } from '../src/client/chat/AssistantMarkdown.tsx' import { StatsLine, type StatsLineProps } from '../src/client/chat/StatsLine.tsx' afterEach(cleanup) describe('MessageItem arms', () => { it('steering bubbles carry the interjection badge and non-text rest blocks', () => { const view = render( , ) expect(view.getByText('插话')).toBeTruthy() expect(view.getByText('steer!')).toBeTruthy() expect(view.getByText(/附加内容块/)).toBeTruthy() }) it('context and unknown nodes render their JSON rows', () => { const ctxView = render( , ) expect(ctxView.getByText(/上下文注入/)).toBeTruthy() const unknownView = render( , ) expect(unknownView.getByText(/未知 surface 事件:surface\/next/)).toBeTruthy() }) }) describe('small branch tails', () => { it('PendingCard approval reason renders when present', () => { const view = render( ['payload'], vi.fn())} />, ) expect(view.getByText('careful')).toBeTruthy() }) it('AssistantMarkdown single-line reasoning summary skips the newline cut', () => { const view = render( , ) expect(view.getByText('one-liner')).toBeTruthy() }) it('StatsLine omits the cache-hit segment when no input accounting exists at all', () => { // cacheHitPct is null only when input+cacheRead are both zero (pure // output accounting) — any input makes it a real 0%. const snap = { nodes: [{ kind: 'assistant', seq: 1, turn: 1, step: 1, blocks: [], usage: { outputTokens: 10 } }], } const source = { getSnapshot: () => snap, subscribe: () => () => {} } const view = render( , ) expect(view.getByText('10 tokens · 1 turns · 1 steps')).toBeTruthy() }) })