Files
banban/banban-mini/tests/chat-presentation.test.cjs

80 lines
4.0 KiB
JavaScript

const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const { test } = require('node:test')
const ts = require('typescript')
function loadTypeScript(file, mocks = {}) {
const source = fs.readFileSync(path.join(__dirname, '../src', file), 'utf8')
const { outputText } = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017, esModuleInterop: true },
})
const module = { exports: {} }
new Function('require', 'module', 'exports', outputText)((id) => {
if (Object.hasOwn(mocks, id)) return mocks[id]
if (id.endsWith('.png')) return path.basename(id)
return require(id)
}, module, module.exports)
return module.exports
}
const { getParticipantArtwork, getRoleArtwork } = loadTypeScript('utils/artwork.ts')
const { formatConversationPreview } = loadTypeScript('utils/chat-preview.ts')
const story = '好呀,我给你讲一个《小水滴的旅行》的故事。\r\n\r\n有一天,小水滴在云妈妈的怀抱里睡着了。\n小水滴落到了一片荷叶上。\u2028小水滴继续向前,来到了大河里。\n这个故事告诉我们,水是不断循环的。💧'
const roles = ['Banban', 'Baikeboshi', 'Zhuli', 'Tuntunzai']
const { getConversations, getMessages } = loadTypeScript('services/chat.ts', {
'@tarojs/taro': {},
'./auth': { getCurrentUserId: () => 1 },
'./binding': { loadCurrentChildBindingContext: async () => ({
currentChild: { child_id: 7, child_name: '拌拌' },
currentBinding: { device_id: 'fixture-device' },
}) },
'./session': {},
'./api': { request: async (url) => {
if (url.endsWith('/roles')) return roles.map(role_key => ({ role_key, name: role_key }))
if (url.includes('/ai-conversations')) return { items: roles.map((role_key, index) => ({
conversation_id: 10 + index, role_key, message_count: 1, last_message_preview: story,
})) }
if (url.includes('/conversations?')) return { items: [
{ conversation_id: 1, conversation_type_name: 'parent_child', peer_type: 'parent', peer_id: '1' },
{ conversation_id: 2, conversation_type_name: 'parent_child', peer_type: 'parent', peer_id: '2', peer_name: '右右' },
{ conversation_id: 3, conversation_type_name: 'child_peer', peer_type: 'child', peer_id: '42', peer_name: '伴伴2号' },
] }
if (url.includes('/messages?')) return { items: [{
id: 100, conversation_id: 11, role_key: 'Baikeboshi', is_user: false, content: story,
}] }
throw new Error(`Unexpected fixture request: ${url}`)
} },
})
test('real conversation mapping gives both parent entries the flower avatar and preserves specialist AI roles', async () => {
const conversations = await getConversations()
const artworkById = Object.fromEntries(conversations.map(item => [
item.id, getParticipantArtwork(item.peerKind, item.roleKey),
]))
assert.equal(artworkById[1], 'avatar-parent.png')
assert.equal(artworkById[2], 'avatar-parent.png')
assert.equal(artworkById[3], 'role-tuntunzai.png')
assert.equal(artworkById[10], 'role-tuntunzai.png')
assert.equal(artworkById[11], 'role-baike.png')
assert.equal(artworkById[12], 'role-zhuli.png')
})
test('the role chooser and conversation avatars agree for each AI role', () => {
for (const role of roles) {
assert.equal(getParticipantArtwork('ai', role), getRoleArtwork(role))
assert.equal(getParticipantArtwork('ai', ` ${role.toUpperCase()} `), getRoleArtwork(role))
}
})
test('a multiline story becomes a single-line list preview while message history keeps its paragraphs', async () => {
const conversation = (await getConversations()).find(item => item.id === 11)
const preview = formatConversationPreview(conversation.lastMessage)
assert.doesNotMatch(preview, /[\r\n\u2028\u2029]/)
assert.match(preview, /故事。 有一天/)
assert.ok(preview.endsWith('💧'))
assert.equal(conversation.lastMessage, story)
const messages = await getMessages(11, 'ai')
assert.equal(messages[0].content, story)
})