Files
WechatExplorer/tests/component/image-bubble.test.tsx
T
电摇小子 3af62783dd feat: 完善日报图片与公众号消息展示
修复日报图片读取格式与模型不支持时的友好降级
恢复话题关键词回退,确保词云在缺少顶层关键词时仍可展示
支持公众号多文章消息在聊天、日报、检索和 HTML 导出中完整呈现
迁移账号身份缓存并优化首次连接的账号占位信息
补充图片、词云、账号缓存与多文章消息回归测试
2026-08-07 00:00:52 +08:00

106 lines
3.9 KiB
TypeScript

import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const requestImage = vi.fn()
vi.mock('../../src/renderer/src/components/image-loader', () => ({
getCachedLoadedImage: vi.fn(() => undefined),
requestImage: (...args: unknown[]) => requestImage(...args)
}))
import { ImageBubble } from '../../src/renderer/src/components/ImageBubble'
import { RichMessageBubble } from '../../src/renderer/src/components/RichMessageBubble'
const thumbnail =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9ZQmcAAAAASUVORK5CYII='
const original = `${thumbnail}original`
describe('ImageBubble', () => {
beforeEach(() => {
requestImage.mockReset()
window.api = { copyImage: vi.fn().mockResolvedValue({ success: true }) } as typeof window.api
})
it('loads a thumbnail lazily, then requests the original when opened', async () => {
requestImage
.mockResolvedValueOnce({ data: thumbnail, isThumbnail: true })
.mockResolvedValueOnce({ data: original, isThumbnail: false })
const onImageClick = vi.fn()
render(
<ImageBubble
imageMd5="fixture-image"
imageDatName="fixture.dat"
sessionId="fixture-session"
onImageClick={onImageClick}
/>
)
const image = await screen.findByAltText('图片')
expect(image).toHaveAttribute('src', thumbnail)
await userEvent.click(image)
await waitFor(() => expect(onImageClick).toHaveBeenCalledWith(original))
expect(requestImage.mock.calls[1][3]).toMatchObject({ force: true })
})
it('shows an explicit error and allows retry', async () => {
requestImage.mockRejectedValueOnce(new Error('不支持的 DAT 版本'))
render(<ImageBubble imageMd5="unsupported" />)
expect(await screen.findByText('不支持的 DAT 版本')).toBeVisible()
requestImage.mockResolvedValueOnce({ data: thumbnail, isThumbnail: true })
await userEvent.click(screen.getByText('加载失败'))
expect(await screen.findByAltText('图片')).toBeVisible()
})
it('places a quoted image on the line below the quoted sender', async () => {
requestImage.mockResolvedValueOnce({ data: thumbnail, isThumbnail: true })
const { container } = render(
<RichMessageBubble
contentData={{
type: 'quote',
content: '回复内容',
quotedContent: '[图片]',
quotedSender: '测试群成员',
quotedImageMd5: 'fixture-image'
}}
sessionId="fixture-session"
/>
)
expect(screen.getByText('测试群成员')).toBeInTheDocument()
expect(container.querySelector('.quoted-message')).toHaveClass('quoted-message-image')
expect(await screen.findByAltText('图片')).toBeVisible()
})
})
describe('RichMessageBubble', () => {
it('renders every article in a public-account bundle', async () => {
const open = vi.spyOn(window, 'open').mockImplementation(() => null)
render(
<RichMessageBubble
contentData={{
type: 'share',
title: 'Article one',
url: 'https://example.test/one',
appname: 'Fixture Publisher',
articles: [
{ title: 'Article one', url: 'https://example.test/one' },
{ title: 'Article two', url: 'https://example.test/two' },
{ title: 'Article three', url: 'https://example.test/three' }
]
}}
/>
)
expect(screen.getByText('Fixture Publisher')).toBeVisible()
expect(screen.getByRole('button', { name: 'Article one' })).toBeVisible()
expect(screen.getByRole('button', { name: 'Article two' })).toBeVisible()
expect(screen.getByRole('button', { name: 'Article three' })).toBeVisible()
await userEvent.click(screen.getByRole('button', { name: 'Article three' }))
expect(open).toHaveBeenCalledWith('https://example.test/three', '_blank')
open.mockRestore()
})
})