import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { AutoDetectImageKeySection } from '../../src/renderer/src/features/settings/image-decryption/AutoDetectImageKeySection'
import { ImageDecryptStatus } from '../../src/renderer/src/features/settings/image-decryption/ImageDecryptStatus'
import { ImageTestSection } from '../../src/renderer/src/features/settings/image-decryption/ImageTestSection'
import { initialImageDecryptionState } from '../../src/renderer/src/features/settings/image-decryption/imageDecryptionReducer'
import type { ImageBatchTestState } from '../../src/renderer/src/features/settings/image-decryption/types'
import type { ImageDecryptionStatus } from '../../src/shared/image-decryption'
const emptyBatchTest: ImageBatchTestState = {
running: false,
stopRequested: false,
elapsedMs: 0,
items: []
}
const imageStatus: ImageDecryptionStatus = {
configured: true,
saved: true,
encryptionAvailable: true,
source: 'secure-storage',
resourceRoot: '/fixture/images',
platform: 'darwin',
autoDetectSupported: true,
wechatRunning: true,
accountIdentified: true,
cacheState: 'normal',
decoder: {
installed: true,
available: true,
source: 'bundled',
selected: false
},
resources: {
imageIndex: { state: 'available', detail: '正常' },
imageDirectory: { state: 'available', detail: '正常' },
thumbnail: { state: 'available', detail: '正常' },
original: { state: 'available', detail: '正常' },
sticker: { state: 'available', detail: '正常' },
video: { state: 'available', detail: '正常' }
}
}
afterEach(() => {
vi.restoreAllMocks()
})
describe('ImageTestSection', () => {
it('uses shared buttons for status validation and automatic key actions', async () => {
const user = userEvent.setup()
const onValidate = vi.fn()
const onDetect = vi.fn()
const onSave = vi.fn()
const configuredState = {
...initialImageDecryptionState,
config: {
success: true,
configured: true,
saved: true,
encryptionAvailable: true,
source: 'secure-storage' as const,
resourceRoot: '/fixture/images'
},
status: imageStatus
}
const { rerender } = render(
)
const validate = screen.getByRole('button', { name: '重新验证' })
expect(validate).not.toHaveClass('database-key-secondary')
await user.click(validate)
expect(onValidate).toHaveBeenCalledOnce()
rerender(
)
const detect = screen.getByRole('button', { name: '开始自动获取' })
const save = screen.getByRole('button', { name: '保存图片密钥' })
expect(detect).not.toHaveClass('database-key-secondary')
expect(save).not.toHaveClass('database-key-primary')
await user.click(detect)
await user.click(save)
expect(onDetect).toHaveBeenCalledOnce()
expect(onSave).toHaveBeenCalledOnce()
})
it('keeps automatic detection disabled while scanning', () => {
render(
)
expect(screen.getByRole('button', { name: '扫描中…' })).toBeDisabled()
})
it('keeps the file step successful when decryption fails and copies diagnostics', async () => {
const onCopyLog = vi.fn()
render(
)
expect(screen.getByText('找到图片文件').closest('li')).toHaveClass('image-step-ok')
expect(screen.getByText('解密成功').closest('li')).toHaveClass('image-step-fail')
expect(screen.getByText('图片可以读取').closest('li')).toHaveClass('image-step-skipped')
expect(screen.getByText('仅当前会话的单次测试日志')).toHaveAttribute(
'title',
expect.stringContaining('不包含批量测试结果')
)
await userEvent.click(screen.getByRole('button', { name: '复制日志' }))
expect(onCopyLog).toHaveBeenCalledOnce()
})
it('disables log copying before a test result exists', () => {
render(
)
expect(screen.getByRole('button', { name: '复制日志' })).toBeDisabled()
})
it('filters groups and contacts before starting a batch test', async () => {
const onBatchTest = vi.fn()
const onSelect = vi.fn()
vi.spyOn(window, 'confirm').mockReturnValue(true)
render(
)
await userEvent.click(screen.getByRole('radio', { name: '群聊 1' }))
expect(screen.getByRole('radio', { name: '群聊 1' })).toBeChecked()
await userEvent.click(screen.getByRole('combobox', { name: '选择会话' }))
expect(screen.getByRole('option', { name: '测试群聊' })).toBeInTheDocument()
expect(screen.queryByRole('option', { name: '测试联系人' })).not.toBeInTheDocument()
await userEvent.click(screen.getByRole('option', { name: '测试群聊' }))
expect(onSelect).toHaveBeenCalledWith('group-md5')
await userEvent.click(screen.getByRole('button', { name: '测试筛选结果(1)' }))
expect(window.confirm).toHaveBeenCalledWith(expect.stringContaining('测试 1 个会话'))
expect(onBatchTest).toHaveBeenCalledWith([
expect.objectContaining({ md5: 'group-md5', type: 'group' })
])
})
it('shows per-conversation batch result and elapsed time', () => {
render(
)
expect(screen.getByText('成功群聊')).toBeInTheDocument()
expect(screen.getByText('没有图片的联系人')).toBeInTheDocument()
expect(screen.getByText('成功 1')).toBeInTheDocument()
expect(screen.getByText('无图片 1')).toBeInTheDocument()
expect(screen.getByText(/2\/2 · 已耗时 1\.3 秒/)).toBeInTheDocument()
})
})