first commit
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import * as React from 'react'
|
||||
import { createElement } from 'react'
|
||||
import type { ComponentProps, ReactElement } 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'
|
||||
import CharacterSection from '@/app/[locale]/workspace/[projectId]/modes/novel-promotion/components/assets/CharacterSection'
|
||||
|
||||
const useProjectAssetsMock = vi.hoisted(() => vi.fn())
|
||||
const characterCardMock = vi.hoisted(() => vi.fn((_props: unknown) => null))
|
||||
|
||||
vi.mock('@/lib/query/hooks/useProjectAssets', () => ({
|
||||
useProjectAssets: (projectId: string | null) => useProjectAssetsMock(projectId),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/[locale]/workspace/[projectId]/modes/novel-promotion/components/assets/CharacterCard', () => ({
|
||||
__esModule: true,
|
||||
default: (props: unknown) => characterCardMock(props),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/[locale]/workspace/[projectId]/modes/novel-promotion/components/assets/CharacterProfileCard', () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/types/character-profile', () => ({
|
||||
parseProfileData: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/components/task/TaskStatusInline', () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/task/presentation', () => ({
|
||||
resolveTaskPresentationState: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/components/ui/icons', () => ({
|
||||
AppIcon: (props: { name?: string; className?: string }) =>
|
||||
createElement('span', { 'data-icon': props.name, className: props.className }),
|
||||
}))
|
||||
|
||||
const messages = {
|
||||
assets: {
|
||||
stage: {
|
||||
characterAssets: '角色资产',
|
||||
counts: '{characterCount} 个角色,{appearanceCount} 个形象',
|
||||
pendingProfilesBanner: '待确认角色',
|
||||
pendingProfilesHint: '确认角色设定',
|
||||
confirmAll: '全部确认',
|
||||
},
|
||||
character: {
|
||||
add: '新建角色',
|
||||
assetCount: '{count} 个形象',
|
||||
copyFromGlobal: '从资产中心导入',
|
||||
delete: '删除角色',
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
function 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('CharacterSection actions', () => {
|
||||
it('renders import and delete actions stacked vertically with the import icon', () => {
|
||||
Reflect.set(globalThis, 'React', React)
|
||||
useProjectAssetsMock.mockReturnValue({
|
||||
data: {
|
||||
characters: [
|
||||
{
|
||||
id: 'character-1',
|
||||
name: '西装男',
|
||||
introduction: null,
|
||||
appearances: [
|
||||
{
|
||||
id: 'appearance-1',
|
||||
appearanceIndex: 0,
|
||||
changeReason: '初始形象',
|
||||
imageUrl: null,
|
||||
imageUrls: [],
|
||||
selectedIndex: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const html = renderWithIntl(
|
||||
createElement(CharacterSection, {
|
||||
projectId: 'project-1',
|
||||
activeTaskKeys: new Set<string>(),
|
||||
onClearTaskKey: () => undefined,
|
||||
onRegisterTransientTaskKey: () => undefined,
|
||||
isAnalyzingAssets: false,
|
||||
onAddCharacter: () => undefined,
|
||||
onDeleteCharacter: () => undefined,
|
||||
onDeleteAppearance: () => undefined,
|
||||
onEditAppearance: () => undefined,
|
||||
handleGenerateImage: async () => undefined,
|
||||
onSelectImage: () => undefined,
|
||||
onConfirmSelection: () => undefined,
|
||||
onRegenerateSingle: async () => undefined,
|
||||
onRegenerateGroup: async () => undefined,
|
||||
onUndo: () => undefined,
|
||||
onImageClick: () => undefined,
|
||||
onImageEdit: () => undefined,
|
||||
onVoiceChange: () => undefined,
|
||||
onVoiceDesign: () => undefined,
|
||||
onVoiceSelectFromHub: () => undefined,
|
||||
onCopyFromGlobal: () => undefined,
|
||||
getAppearances: (character) => character.appearances,
|
||||
unconfirmedCharacters: [],
|
||||
isConfirmingCharacter: () => false,
|
||||
deletingCharacterId: null,
|
||||
batchConfirming: false,
|
||||
batchConfirmingState: null,
|
||||
onBatchConfirm: () => undefined,
|
||||
onEditProfile: () => undefined,
|
||||
onConfirmProfile: () => undefined,
|
||||
onUseExistingProfile: () => undefined,
|
||||
onDeleteProfile: () => undefined,
|
||||
}),
|
||||
)
|
||||
|
||||
expect(html).toContain('从资产中心导入')
|
||||
expect(html).toContain('删除角色')
|
||||
expect(html).toContain('data-icon="arrowDownCircle"')
|
||||
expect(html).toContain('flex flex-col items-end gap-1.5')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user