60 lines
3.6 KiB
TypeScript
60 lines
3.6 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
import { freezeAssistantModelMetadata } from '../src/shared/assistant-model'
|
|
import { AssistantMessageDetails, formatMessageTime } from '../src/renderer/src/components/AssistantMessageDetails'
|
|
import { getModelIconByName } from '../src/renderer/src/workbuddy/composer/model-selector/source.mjs'
|
|
import type { ChatMessage } from '../src/shared/types'
|
|
|
|
const renderTime = new Date()
|
|
renderTime.setHours(12, 0, 0, 0)
|
|
const profile = { id: 'profile-1', name: 'Frozen Model' }
|
|
const metadata = freezeAssistantModelMetadata(profile)
|
|
profile.name = 'Renamed Model'
|
|
assert.deepEqual(metadata, { modelId: 'profile-1', modelName: 'Frozen Model' })
|
|
|
|
const newAssistantMessage: ChatMessage = {
|
|
id: 'assistant-1', role: 'assistant', content: 'answer', createdAt: renderTime.getTime(), status: 'done', ...metadata
|
|
}
|
|
const newFooter = renderToStaticMarkup(<AssistantMessageDetails message={newAssistantMessage} />)
|
|
assert.match(newFooter, /cb-credit-usage-text cb-credit-usage-text--feedback/)
|
|
assert.match(newFooter, /title="profile-1"/)
|
|
assert.match(newFooter, /class="cb-model-tag-icon"[^>]*aria-hidden="true"/)
|
|
assert.match(newFooter, /Frozen Model/)
|
|
assert.doesNotMatch(newFooter, /Renamed Model/)
|
|
assert.match(newFooter, /_message_time_tip_1p6pj_1 cb-message-time-tip/)
|
|
assert.match(newFooter, />12:00<\/span>/)
|
|
|
|
const historicalMessage: ChatMessage = {
|
|
id: 'assistant-old', role: 'assistant', content: 'legacy answer', createdAt: renderTime.getTime(), status: 'done'
|
|
}
|
|
const historicalFooter = renderToStaticMarkup(<AssistantMessageDetails message={historicalMessage} />)
|
|
assert.doesNotMatch(historicalFooter, /cb-credit-usage-text/)
|
|
assert.doesNotMatch(historicalFooter, /Frozen Model|Renamed Model/)
|
|
|
|
const footerCss = readFileSync(resolve('src/renderer/src/components/assistant-message-details.css'), 'utf8')
|
|
assert.doesNotMatch(footerCss, /\.cb-credit-usage-text--feedback\s*\{[^}]*opacity:\s*0/s)
|
|
assert.doesNotMatch(footerCss, /top:4px/, 'metadata shares a flex centerline instead of a timestamp offset')
|
|
assert.match(footerCss, /cb-assistant-message-details\{[^}]*font-family:[^}]*sans-serif[^}]*font-size:15px[^}]*line-height:24px/s)
|
|
const brandMarkup = (name: string): string => renderToStaticMarkup(<AssistantMessageDetails message={{ ...newAssistantMessage, modelName: name }} />)
|
|
const gptGlyph = renderToStaticMarkup(<>{getModelIconByName('gpt-6-luna')}</>)
|
|
const deepseekGlyph = renderToStaticMarkup(<>{getModelIconByName('deepseek-flash')}</>)
|
|
assert.notEqual(gptGlyph, deepseekGlyph, 'GPT and DeepSeek must use different brand logos')
|
|
assert.ok(brandMarkup('gpt-6-luna').includes(gptGlyph))
|
|
assert.ok(brandMarkup('deepseek-flash').includes(deepseekGlyph))
|
|
assert.match(footerCss, /\.cb-assistant-message:hover \.cb-credit-usage-text--feedback,\.cb-assistant-message:hover \.cb-message-time-tip\{opacity:1\}/)
|
|
|
|
const now = new Date(2025, 3, 10, 12, 0)
|
|
const today = new Date(2025, 3, 10, 8, 4).getTime()
|
|
const yesterday = new Date(2025, 3, 9, 9, 5).getTime()
|
|
const earlierThisYear = new Date(2025, 3, 7, 9, 5).getTime()
|
|
const earlierYear = new Date(2024, 11, 31, 9, 5).getTime()
|
|
assert.equal(formatMessageTime(today, now), '08:04')
|
|
assert.equal(formatMessageTime(yesterday, now), '4月9日 09:05')
|
|
assert.equal(formatMessageTime(earlierThisYear, now), '4月7日 09:05')
|
|
assert.equal(formatMessageTime(earlierYear, now), '2024年12月31日 09:05')
|
|
assert.equal(formatMessageTime(Number.NaN, now), undefined)
|
|
|
|
console.log('assistant-message-footer-regression: frozen model metadata, legacy history, and date formatting passed')
|