cc-haha/desktop/src/lib/selectionComposer.test.ts
程序员阿江(Relakkes) eb68791ac5 fix: send preview selections as annotated chat turns
Element selection from the local preview should preserve the visual target
the user picked and send the confirmed request directly to chat. The
screenshot now captures the visible viewport before drawing the marker, and
the selection event sends a model-facing prompt while the chat UI shows only
the annotated image and compact element label.

Constraint: No issue number was provided for this task.
Constraint: Keep the preview-agent protocol shape unchanged.
Rejected: Prefill the composer with the selection note | it keeps the old extra confirmation step and exposes implementation text in the input.
Rejected: Full-document annotation coordinates | body-relative captures drift from the viewport selection users see.
Confidence: high
Scope-risk: moderate
Directive: Do not route confirmed preview selections back through composer prefill without rechecking the Codex-style direct-send flow.
Tested: cd desktop && bun run test --run src/preview-agent/screenshot.test.ts src/lib/previewEvents.test.ts src/lib/selectionComposer.test.ts src/components/chat/AttachmentGallery.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build:preview-agent
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: Real Tauri webview click-through smoke; in-app Browser automation timed out while local Vite HTTP smoke returned 200.
2026-06-01 01:07:08 +08:00

49 lines
2.0 KiB
TypeScript

import { expect, it } from 'vitest'
import { buildSelectionComposerText, buildSelectionDirectMessage } from './selectionComposer'
it('renders only the user instruction + concrete changes (no selector/DOM/page noise)', () => {
const text = buildSelectionComposerText({
pageUrl: 'http://127.0.0.1:4321/preview-fs/s1/index.html',
sourceHint: 'index.html',
element: { selector: '#title', tag: 'h1', text: '模型还没用熟', classes: [] } as never,
change: { text: { from: '模型还没用熟', to: '模型用熟了' }, description: '标题改积极一点' } as never,
})
// 用户的话 + 具体改动在
expect(text).toContain('标题改积极一点')
expect(text).toContain('模型用熟了')
// selector / 页面 URL / 「请在源码中落地」 等 DOM 噪音不在(截图才是引用)
expect(text).not.toContain('#title')
expect(text).not.toContain('index.html')
expect(text).not.toContain('请在源码中落地')
})
it('returns empty text when there is no description or change (image alone is the reference)', () => {
const text = buildSelectionComposerText({
pageUrl: 'http://x/',
element: { selector: '#t', tag: 'div', classes: [] } as never,
})
expect(text).toBe('')
})
it('builds a model prompt while keeping the visible selection label compact', () => {
const message = buildSelectionDirectMessage({
pageUrl: 'http://localhost:5174/',
sourceHint: 'Todo preview',
element: {
selector: '#todo-title',
tag: 'h1',
text: 'Todo 管理',
classes: ['title'],
boundingBox: { x: 10, y: 20, w: 300, h: 80 },
} as never,
change: { description: '这个标题更轻一点' } as never,
})
expect(message.modelText).toContain('截图中编号 1')
expect(message.modelText).toContain('<h1>')
expect(message.modelText).toContain('#todo-title')
expect(message.modelText).toContain('这个标题更轻一点')
expect(message.displayName).toBe('<h1>')
expect(message.note).toBe('这个标题更轻一点')
})