/** @vitest-environment jsdom */
import { afterEach, describe, expect, it, vi } from 'vitest'
import { REPLACEMENT_LABEL, SOURCE_LABEL } from '../src/client/copy.ts'
import { adornRunningStatuses, installRunningStatusOverride } from '../src/client/running-status.ts'
const LABEL = '深度求索中,用时 3秒 ···'
const REWRITTEN_LABEL = '正在连接至PRTS神经元,用时 3秒 ···'
/** ui-chat's authored running-status markup, trimmed to the parts the override reads. */
function runningRowMarkup(label: string = LABEL): string {
return `
`
}
const disposers: Array<() => void> = []
afterEach(() => {
for (const dispose of disposers.splice(0)) dispose()
document.body.innerHTML = ''
})
describe('adornRunningStatuses', () => {
it('replaces the whale icon with the animated image before the icon box', () => {
document.body.innerHTML = runningRowMarkup()
adornRunningStatuses()
const image = document.querySelector('img[data-prts-icon]')
const icon = document.querySelector('.runningIcon')
expect(image).not.toBeNull()
expect(image?.getAttribute('src')?.startsWith('data:image/gif;base64,')).toBe(true)
expect((image?.getAttribute('src') ?? '').length).toBeGreaterThan(1000)
expect(image?.getAttribute('aria-hidden')).toBe('true')
expect(icon?.previousElementSibling).toBe(image)
expect(icon instanceof HTMLElement ? icon.style.display : undefined).toBe('none')
})
it('doubles the icon with a transform, leaving the row layout box alone', () => {
document.body.innerHTML = runningRowMarkup()
adornRunningStatuses()
const image = document.querySelector('img[data-prts-icon]')
expect(image instanceof HTMLElement ? image.style.width : undefined).toBe('16px')
expect(image instanceof HTMLElement ? image.style.height : undefined).toBe('16px')
expect(image?.getAttribute('style') ?? '').toContain('scale(2)')
})
it('rewrites the visible label, the screen-reader status, and the shimmer attribute', () => {
document.body.innerHTML = runningRowMarkup()
adornRunningStatuses()
expect(document.querySelector('.shimmerText')?.textContent).toBe(REWRITTEN_LABEL)
expect(document.querySelector('[role="status"]')?.textContent).toBe(REPLACEMENT_LABEL)
expect(document.querySelector('[data-shimmer-text]')?.getAttribute('data-shimmer-text')).toBe(REWRITTEN_LABEL)
})
it('keeps the elapsed-time suffix and the whole label when no clock is running', () => {
document.body.innerHTML = runningRowMarkup(SOURCE_LABEL)
adornRunningStatuses()
expect(document.querySelector('.shimmerText')?.textContent).toBe(REPLACEMENT_LABEL)
})
it('adorns a row once even when called repeatedly', () => {
document.body.innerHTML = runningRowMarkup()
adornRunningStatuses()
adornRunningStatuses()
expect(document.querySelectorAll('img[data-prts-icon]').length).toBe(1)
expect(document.querySelectorAll('.shimmerText')[0]?.textContent).toBe(REWRITTEN_LABEL)
})
it('leaves markup outside a running row untouched', () => {
document.body.innerHTML = `${LABEL}
` + runningRowMarkup()
adornRunningStatuses()
expect(document.querySelector('.other')?.textContent).toBe(LABEL)
expect(document.querySelectorAll('img[data-prts-icon]').length).toBe(1)
})
it('restores the original icon when the image fails to load', () => {
document.body.innerHTML = runningRowMarkup()
adornRunningStatuses()
const image = document.querySelector('img[data-prts-icon]')
const icon = document.querySelector('.runningIcon')
image?.dispatchEvent(new Event('error'))
expect(document.querySelector('img[data-prts-icon]')).toBeNull()
expect(icon instanceof HTMLElement ? icon.style.display : undefined).toBe('')
})
})
describe('installRunningStatusOverride', () => {
it('re-applies the label after React writes the original copy back', async () => {
document.body.innerHTML = runningRowMarkup()
disposers.push(installRunningStatusOverride())
expect(document.querySelector('.shimmerText')?.textContent).toBe(REWRITTEN_LABEL)
const text = document.querySelector('.shimmerText')
if (text === null) throw new Error('fixture missing the label node')
text.textContent = '深度求索中,用时 4秒 ···'
await vi.waitFor(() => {
expect(document.querySelector('.shimmerText')?.textContent).toBe('正在连接至PRTS神经元,用时 4秒 ···')
})
})
it('adorns a row that mounts after installation', async () => {
disposers.push(installRunningStatusOverride())
document.body.insertAdjacentHTML('beforeend', runningRowMarkup())
await vi.waitFor(() => {
expect(document.querySelectorAll('img[data-prts-icon]').length).toBe(1)
})
})
it('stops rewriting after disposal', async () => {
document.body.innerHTML = runningRowMarkup()
const dispose = installRunningStatusOverride()
expect(document.querySelector('.shimmerText')?.textContent).toBe(REWRITTEN_LABEL)
dispose()
const text = document.querySelector('.shimmerText')
if (text === null) throw new Error('fixture missing the label node')
text.textContent = LABEL
await new Promise((resolve) => { setTimeout(resolve, 0) })
expect(document.querySelector('.shimmerText')?.textContent).toBe(LABEL)
})
})