36 lines
3.9 KiB
TypeScript
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')
|