Files
waooplus/tests/unit/components/asset-grid.test.ts
2026-03-28 18:58:21 +08:00

159 lines
4.6 KiB
TypeScript

import * as React from 'react'
import { createElement } from 'react'
import type { ComponentProps, ReactElement } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { renderToStaticMarkup } from 'react-dom/server'
import { NextIntlClientProvider } from 'next-intl'
import type { AbstractIntlMessages } from 'next-intl'
import { AssetGrid } from '@/app/[locale]/workspace/asset-hub/components/AssetGrid'
vi.mock('react', async (importOriginal) => {
const actual = await importOriginal<typeof import('react')>()
return {
...actual,
useState: <T,>(initialState: T | (() => T)) => {
const resolvedInitialState = typeof initialState === 'function'
? (initialState as () => T)()
: initialState
if (resolvedInitialState === 'all') {
return actual.useState('location' as T)
}
return actual.useState(resolvedInitialState)
},
}
})
vi.mock('@/app/[locale]/workspace/asset-hub/components/CharacterCard', () => ({
CharacterCard: () => null,
}))
vi.mock('@/app/[locale]/workspace/asset-hub/components/LocationCard', () => ({
LocationCard: () => null,
}))
vi.mock('@/app/[locale]/workspace/asset-hub/components/VoiceCard', () => ({
VoiceCard: () => null,
}))
vi.mock('@/components/task/TaskStatusInline', () => ({
default: () => null,
}))
const messages = {
assetHub: {
allAssets: '所有资产',
characters: '角色',
locations: '场景',
props: '道具',
voices: '音色',
addAsset: '新建资产',
addCharacter: '新建角色',
addLocation: '新建场景',
addProp: '新建道具',
addVoice: '新建音色',
downloadAll: '打包下载',
downloadAllTitle: '下载全部图片资产',
downloading: '打包中...',
emptyState: '暂无资产',
emptyStateHint: '点击上方按钮添加角色或场景',
filteredEmptyHint: '点击新建资产添加资产',
pagination: {
previous: '上一页',
next: '下一页',
},
},
} as const
const renderWithIntl = (node: ReactElement) => {
const providerProps: ComponentProps<typeof NextIntlClientProvider> = {
locale: 'zh',
messages: messages as unknown as AbstractIntlMessages,
timeZone: 'Asia/Shanghai',
children: node,
}
return renderToStaticMarkup(
createElement(NextIntlClientProvider, providerProps),
)
}
describe('AssetGrid', () => {
it('空状态下使用与资产库一致的 compact 分段控件,并在中间显示新建资产按钮', () => {
Reflect.set(globalThis, 'React', React)
const html = renderWithIntl(
createElement(AssetGrid, {
assets: [],
loading: false,
onAddCharacter: () => undefined,
onAddLocation: () => undefined,
onAddProp: () => undefined,
onAddVoice: () => undefined,
onDownloadAll: () => undefined,
isDownloading: false,
selectedFolderId: null,
}),
)
expect(html).toContain('inline-block max-w-full min-w-max')
expect(html).toContain('inline-grid grid-flow-col auto-cols-[minmax(96px,max-content)]')
expect(html).toContain('justify-center')
expect(html).toContain('>新建资产<')
})
it('当前筛选分类没有资产时显示添加提示文案', () => {
Reflect.set(globalThis, 'React', React)
const html = renderWithIntl(
createElement(AssetGrid, {
assets: [
{
id: 'character-1',
kind: 'character',
family: 'visual',
scope: 'project',
name: '角色A',
folderId: null,
capabilities: {
canGenerate: true,
canSelectRender: false,
canRevertRender: false,
canModifyRender: false,
canUploadRender: false,
canBindVoice: false,
canCopyFromGlobal: false,
},
taskRefs: [],
taskState: { isRunning: false, lastError: null },
variants: [],
introduction: null,
profileData: null,
profileConfirmed: null,
profileTaskRefs: [],
profileTaskState: { isRunning: false, lastError: null },
voice: {
voiceType: null,
voiceId: null,
customVoiceUrl: null,
media: null,
},
},
],
loading: false,
onAddCharacter: () => undefined,
onAddLocation: () => undefined,
onAddProp: () => undefined,
onAddVoice: () => undefined,
onDownloadAll: () => undefined,
isDownloading: false,
selectedFolderId: null,
}),
)
expect(html).toContain('点击新建资产添加资产')
})
})