Files
ycode/scripts/reference-content-regression.tsx

36 lines
3.9 KiB
TypeScript

import assert from 'node:assert/strict'
import { renderToStaticMarkup } from 'react-dom/server'
import { normalizeUserContent, trimUserContent } from '../src/shared/user-content'
import type { UserContentBlock } from '../src/shared/conversation'
import { userContentToSlate, slateToUserContent } from '../src/renderer/src/workbuddy/references/ReferenceContentEditor'
import { MessageQuoteTag } from '../src/renderer/src/workbuddy/conversation/MessageQuoteTag'
import { UserInlineContent } from '../src/renderer/src/workbuddy/references/UserInlineContent'
import { ReferenceIcon } from '../src/renderer/src/workbuddy/references/ContentReferenceTag'
const collections = { quotes: [{ id: 'q' }], attachments: [{ id: 'a' }], resources: ['C:/资料/说明.md'], skills: [{ id: 's' }] }
const blocks: UserContentBlock[] = [{ type: 'text', text: '前😀' }, { type: 'quote', id: 'q' }, { type: 'text', text: '\n中\n' },
{ type: 'attachment', id: 'a' }, { type: 'resource', id: collections.resources[0] }, { type: 'skill', id: 's' }, { type: 'text', text: '后' }]
const normalized = normalizeUserContent(blocks, '前😀\n中\n后', collections)
assert.deepEqual(normalized, blocks, 'mixed order must survive validation')
assert.deepEqual(normalizeUserContent(slateToUserContent(userContentToSlate(blocks)), '前😀\n中\n后', collections), blocks,
'paragraphs, inline voids, adjacent references and Unicode must round trip without changing the prompt')
assert.deepEqual(normalizeUserContent([{ type: 'text', text: 'safe' }, { type: 'attachment', id: 'forged' }, { type: 'quote', id: 'q' }, { type: 'quote', id: 'q' }], 'safe', { quotes: [{ id: 'q' }] }),
[{ type: 'text', text: 'safe' }, { type: 'quote', id: 'q' }], 'layout cannot introduce payloads or duplicate reference identities')
assert.deepEqual(normalizeUserContent([{ type: 'text', text: 'changed' }], 'trusted', {}), [{ type: 'text', text: 'trusted' }], 'view metadata cannot replace model text')
assert.deepEqual(trimUserContent([{ type: 'text', text: ' 前' }, { type: 'quote', id: 'q' }, { type: 'text', text: '后 ' }]),
[{ type: 'text', text: '前' }, { type: 'quote', id: 'q' }, { type: 'text', text: '后' }], 'trimming cannot flatten the inline reference order')
const quote = { id: 'q', sourceId: 'm:table:t', kind: 'table' as const, markdown: '| sensitive snapshot |', preview: '| sensitive snapshot |' }
const tag = renderToStaticMarkup(<MessageQuoteTag quote={quote} readOnly />)
assert.ok(tag.includes('会话表格') && !tag.includes('sensitive snapshot'), 'legacy table previews cannot leak raw Markdown into the label or tooltip')
assert.ok(tag.includes('viewBox="0 0 14 14"') && tag.includes('_fileTag_1krka_111'), 'table must use its actual SVG and pill variant')
assert.ok(!tag.includes('<button'), 'read-only references cannot expose removal')
const message = renderToStaticMarkup(<UserInlineContent message={{ id: 'm', role: 'user', content: '前后', createdAt: 0, quotes: [quote],
attachments: [{ id: 'a', name: '报告.pdf', size: 10, mimeType: 'application/pdf' }],
userContent: [{ type: 'text', text: '前' }, { type: 'quote', id: 'q' }, { type: 'attachment', id: 'a' }, { type: 'text', text: '后' }] }} />)
assert.ok(message.indexOf('前') < message.indexOf('会话表格') && message.indexOf('会话表格') < message.indexOf('报告.pdf') && message.indexOf('报告.pdf') < message.lastIndexOf('后'), 'read-only projection must keep the original inline order')
for (const name of ['说明.md','表格.csv','报告.pdf','文档.docx','代码.ts','页面.html','图片.png','压缩.zip','音频.wav','视频.mp4','other.unknown']) {
const icon = renderToStaticMarkup(<ReferenceIcon kind="file" name={name} />)
assert.ok(icon.includes('<svg') && icon.includes('width="14"'), 'recovered icon must render without unresolved path constants: ' + name)
}
console.log('reference-content-regression: inline order, Unicode/newlines, validation, snapshots, legacy labels, read-only tags and recovered icon family passed')