Files
waooplus/tests/unit/components/global-asset-picker-preview.test.ts

151 lines
4.6 KiB
TypeScript

import * as React from 'react'
import { createElement } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it, vi } from 'vitest'
import { NextIntlClientProvider } from 'next-intl'
import type { AbstractIntlMessages } from 'next-intl'
const useQueryMock = vi.hoisted(() => vi.fn())
vi.mock('@tanstack/react-query', () => ({
useQuery: (options: unknown) => useQueryMock(options),
}))
vi.mock('@/components/ui/ImagePreviewModal', () => ({
__esModule: true,
default: () => null,
}))
vi.mock('@/components/task/TaskStatusInline', () => ({
__esModule: true,
default: () => null,
}))
vi.mock('@/lib/task/presentation', () => ({
resolveTaskPresentationState: () => null,
}))
vi.mock('@/components/media/MediaImageWithLoading', () => ({
MediaImageWithLoading: (props: { src: string; alt: string; className?: string; containerClassName?: string }) =>
createElement('img', {
src: props.src,
alt: props.alt,
className: [props.className, props.containerClassName].filter(Boolean).join(' '),
}),
}))
vi.mock('@/components/ui/icons', () => ({
AppIcon: (props: { name?: string; className?: string }) =>
createElement('span', { 'data-icon': props.name, className: props.className }),
}))
const messages = {
assetPicker: {
selectCharacter: '从资产中心选择角色',
selectLocation: '从资产中心选择场景',
selectProp: '从资产中心选择道具',
selectVoice: '从资产中心选择音色',
searchPlaceholder: '搜索资产名称或文件夹...',
noAssets: '资产中心暂无资产',
createInAssetHub: '请先在资产中心创建角色/场景/音色',
noSearchResults: '未找到匹配的资产',
appearances: '个形象',
images: '张图片',
cancel: '取消',
confirmCopy: '确认导入',
},
} as const
const TestIntlProvider = NextIntlClientProvider as React.ComponentType<{
locale: string
messages: AbstractIntlMessages
timeZone: string
children?: React.ReactNode
}>
describe('GlobalAssetPicker preview mapping', () => {
it('renders the real character preview image at 3:2 without the appearance count line', async () => {
Reflect.set(globalThis, 'React', React)
useQueryMock.mockReset()
useQueryMock.mockImplementation((options: { enabled?: boolean }) => ({
data: options.enabled ? [{
id: 'character-1',
kind: 'character',
family: 'visual',
scope: 'global',
name: '西装男',
folderId: null,
capabilities: {
canGenerate: true,
canSelectRender: true,
canRevertRender: true,
canModifyRender: true,
canUploadRender: true,
canBindVoice: true,
canCopyFromGlobal: false,
},
taskRefs: [],
taskState: { isRunning: false, lastError: null },
introduction: null,
profileData: null,
profileConfirmed: null,
profileTaskRefs: [],
profileTaskState: { isRunning: false, lastError: null },
voice: {
voiceType: null,
voiceId: null,
customVoiceUrl: null,
media: null,
},
variants: [{
id: 'variant-1',
index: 0,
label: '默认形象',
description: '黑西装',
selectionState: { selectedRenderIndex: 0 },
taskRefs: [],
taskState: { isRunning: false, lastError: null },
renders: [{
id: 'render-1',
index: 0,
imageUrl: 'https://example.com/character.png',
media: null,
isSelected: true,
previousImageUrl: null,
previousMedia: null,
taskRefs: [],
taskState: { isRunning: false, lastError: null },
}],
}],
}] : [],
isFetching: false,
refetch: vi.fn(),
}))
const { default: GlobalAssetPicker } = await import('@/components/shared/assets/GlobalAssetPicker')
const html = renderToStaticMarkup(
createElement(
TestIntlProvider,
{
locale: 'zh',
messages: messages as unknown as AbstractIntlMessages,
timeZone: 'Asia/Shanghai',
},
createElement(GlobalAssetPicker, {
isOpen: true,
onClose: () => undefined,
onSelect: () => undefined,
type: 'character',
}),
),
)
expect(html).toContain('src="https://example.com/character.png"')
expect(html).toContain('aspect-[3/2]')
expect(html).toContain('object-contain')
expect(html).not.toContain('data-icon="userAlt"')
expect(html).not.toContain('border-b')
expect(html).not.toContain('个形象')
})
})