80 lines
4.0 KiB
JavaScript
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)
|
|
})
|