Files
deepseek-harness/packages/ui/desktop/tests/index.spec.ts
NI0317 c4584b5c19 feat(ui): rebuild desktop renderer on a shared trace graph and design tokens
Replace the full-innerHTML render loop with a static shell plus
per-region updates, so composer drafts, fold state, focus, and scroll
survive streaming turns. Fold session events into one trace graph
consumed by Chat, Trajectory, Waterfall, and the shared inspector
drawer, with live ACP updates patched into a keyed live-turn region.

Align the visual system with a tokenized design spec: a 4px spacing
base with fixed control/row height steps, foreground-derived text
tiers and borders (color-mix), neutral interaction overlays, tiered
motion durations with a reduced-motion collapse, hover-revealed
scrollbars, and drawer-aware layout elasticity. Localize trajectory
role chips and row previews.

The renderer entry (app.ts) joins the coverage exclude list as a
self-executing DOM bootstrap: jsdom lifecycle specs exercise its
behavior, and extractable logic lives in covered modules
(trace-graph.ts, renderer-content.ts).
2026-07-19 01:37:04 +08:00

141 lines
4.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
closedInspectorState,
createInspectorTargetId,
DEFAULT_FEEDBACK_AUTHOR,
DESKTOP_SURFACES,
fullDetailBelongsInInspector,
INSPECTOR_TABS,
openInspectorState,
opensInspector,
ownsComposer,
SURFACE_DEFINITIONS,
SURFACE_POLICIES,
type InspectorTarget,
} from '../src/index.ts'
import { translate } from '../src/i18n.ts'
const target: InspectorTarget = {
id: 'session:one:event:1',
kind: 'message',
title: 'user/message',
}
describe('desktop surface policies', () => {
it('routes trace-analysis detail through the inspector, but not Develop', () => {
for (const surface of DESKTOP_SURFACES.filter(surface => surface !== 'dev')) {
expect(opensInspector(surface, target)).toBe(true)
expect(fullDetailBelongsInInspector(surface)).toBe(true)
}
expect(opensInspector('dev', target)).toBe(false)
expect(fullDetailBelongsInInspector('dev')).toBe(false)
})
it('keeps empty selections from opening the inspector', () => {
expect(opensInspector('trajectory', undefined)).toBe(false)
})
it('keeps trajectory and context as summary-first surfaces', () => {
expect(SURFACE_POLICIES.trajectory).toMatchObject({
summaryFirst: true,
inlinePreview: true,
fullDetailInInspector: true,
})
expect(SURFACE_POLICIES.context).toMatchObject({
summaryFirst: true,
inlinePreview: true,
fullDetailInInspector: true,
})
})
it('keeps the session composer across the three live views', () => {
for (const surface of DESKTOP_SURFACES) {
expect(ownsComposer(surface)).toBe(['chat', 'trajectory', 'waterfall'].includes(surface))
}
})
it('keeps surface definitions aligned with the exported surface list', () => {
expect(Object.keys(SURFACE_DEFINITIONS).sort()).toEqual([...DESKTOP_SURFACES].sort())
expect(SURFACE_DEFINITIONS.trajectory.purpose).toBe('navigate')
expect(SURFACE_DEFINITIONS.context.purpose).toBe('request-anatomy')
expect(SURFACE_DEFINITIONS.compare.primaryQuestion).toContain('baseline')
expect(SURFACE_DEFINITIONS.dev.primaryQuestion).toContain('artifacts compose')
})
})
describe('desktop inspector contracts', () => {
it('uses stable target ids across surfaces', () => {
expect(createInspectorTargetId({
sessionId: 's1',
runId: 'r1',
kind: 'tool-call',
eventSeq: 42,
})).toBe('session:s1:run:r1:kind:tool-call:seq:42')
expect(createInspectorTargetId({
sessionId: 's1',
kind: 'message',
syntheticId: 'draft',
})).toBe('session:s1:kind:message:synthetic:draft')
})
it('opens output by default for produced data and metadata for structural targets', () => {
expect(openInspectorState({
id: 'session:s1:kind:tool-result:seq:9',
kind: 'tool-result',
title: 'tool/result',
}).activeTab).toBe('output')
expect(openInspectorState({
id: 'session:s1:kind:step:seq:3',
kind: 'step',
title: 'step 1',
}).activeTab).toBe('metadata')
const expected = new Map<InspectorTarget['kind'], string>([
['assistant-stream', 'output'],
['tool-result', 'output'],
['session', 'metadata'],
['run', 'metadata'],
['turn', 'metadata'],
['step', 'metadata'],
['waterfall-span', 'metadata'],
['dev-object', 'input'],
['message', 'input'],
])
for (const [kind, tab] of expected) {
expect(openInspectorState({ ...target, kind }).activeTab).toBe(tab)
}
})
it('keeps inspector tabs ordered with feedback last', () => {
expect(INSPECTOR_TABS).toEqual(['input', 'output', 'metadata', 'feedback'])
expect(openInspectorState(target).tabs.map(tab => tab.tab)).toEqual(INSPECTOR_TABS)
expect(closedInspectorState()).toEqual({
open: false,
activeTab: 'input',
tabs: [],
})
})
it('uses shentuni as the default feedback author', () => {
expect(DEFAULT_FEEDBACK_AUTHOR).toBe('shentuni')
})
})
describe('desktop i18n', () => {
it('switches visible shell labels between Chinese and English', () => {
expect(translate('zh-CN', 'app.newChat')).toBe('新对话')
expect(translate('en-US', 'app.newChat')).toBe('New chat')
expect(translate('zh-CN', 'app.language')).toBe('EN')
expect(translate('en-US', 'app.language')).toBe('中文')
})
it('keeps core Chinese labels localized rather than falling back to English', () => {
expect(translate('zh-CN', 'app.develop')).toBe('开发')
expect(translate('zh-CN', 'surface.trajectory')).toBe('轨迹')
expect(translate('zh-CN', 'chat.thinking')).toBe('思考')
expect(translate('zh-CN', 'dev.requestSystemPrompt')).toBe('当前请求的系统提示词')
expect(translate('zh-CN', 'inspector.feedback')).toBe('反馈')
})
})