/* @vitest-environment jsdom */ import { fireEvent, render, screen } from '@testing-library/react'; import { createRef, useState } from 'react'; import { describe, expect, it, vi } from 'vitest'; import type { GenerateDialogState, SpecGenerationType, UploadTarget, } from './ImageCanvasEditorTypes'; import { ImageCanvasCharacterGenerationComposerView } from './ImageCanvasCharacterGenerationComposerView'; function createDialog( patch: Partial = {}, ): GenerateDialogState { return { mode: 'character', prompt: '旧角色设定', status: 'idle', characterSpecReference: { id: 'spec-a', label: '角色规范A', src: 'data:image/png;base64,c3BlYw==', }, characterReferences: [ { id: 'ref-a', label: '参考图A', src: 'data:image/png;base64,cmVm', }, ], ...patch, }; } function CharacterGenerationHarness({ initialDialog = createDialog(), onOpenSpecDialog = vi.fn(), onRequestUpload = vi.fn(), onSubmit = vi.fn(), onRememberImageModel = vi.fn(), }: { initialDialog?: GenerateDialogState; onOpenSpecDialog?: (specType: SpecGenerationType) => void; onRequestUpload?: (target: UploadTarget) => void; onSubmit?: (dialog: GenerateDialogState) => void; onRememberImageModel?: (model: string) => void; }) { const [dialog, setDialog] = useState( initialDialog, ); const [isCharacterSpecMenuOpen, setIsCharacterSpecMenuOpen] = useState(false); const [ isCharacterReferenceMenuOpen, setIsCharacterReferenceMenuOpen, ] = useState(false); const [isPickingSpec, setIsPickingSpec] = useState(false); const [isPickingReference, setIsPickingReference] = useState(false); const characterSpecButtonRef = createRef(); const characterReferenceButtonRef = createRef(); return dialog ? (
node} buildPortalMenuStyle={() => ({ position: 'fixed', left: 0, top: 0 })} onOpenSpecDialog={onOpenSpecDialog} onRequestUpload={onRequestUpload} onRememberImageModel={onRememberImageModel} onSubmit={onSubmit} /> {dialog.prompt} {dialog.status} {dialog.errorMessage ?? '-'} {String(isPickingSpec)} {String(isPickingReference)}
) : null; } describe('ImageCanvasCharacterGenerationComposerView', () => { it('updates character prompt, clears failed state and submits', () => { const submitCharacter = vi.fn(); render( , ); fireEvent.change(screen.getByLabelText('角色设定'), { target: { value: '新的角色设定' }, }); fireEvent.click(screen.getByRole('button', { name: '生成' })); expect(screen.getByLabelText('当前角色设定').textContent).toBe( '新的角色设定', ); expect(screen.getByLabelText('当前状态').textContent).toBe('idle'); expect(screen.getByLabelText('当前错误').textContent).toBe('-'); expect(submitCharacter).toHaveBeenCalledWith( expect.objectContaining({ prompt: '新的角色设定', status: 'idle' }), ); }); it('routes character reference menus to canvas picking, spec creation and upload', () => { const openSpecDialog = vi.fn(); const requestUpload = vi.fn(); render( , ); fireEvent.click(screen.getByRole('button', { name: '角色规范A' })); fireEvent.click(screen.getByRole('menuitem', { name: '从画布中选择' })); expect(screen.getByLabelText('选择角色规范').textContent).toBe('true'); fireEvent.click(screen.getByRole('button', { name: '角色规范A' })); fireEvent.click(screen.getByRole('menuitem', { name: '新建角色形象规范' })); expect(openSpecDialog).toHaveBeenCalledWith('character'); fireEvent.click(screen.getByRole('button', { name: '角色规范A' })); fireEvent.click(screen.getByRole('menuitem', { name: '上传图片' })); expect(requestUpload).toHaveBeenCalledWith('character-spec'); fireEvent.click(screen.getByRole('button', { name: '上传常规参考图' })); fireEvent.click(screen.getByRole('menuitem', { name: '从画布中选择' })); expect(screen.getByLabelText('选择常规参考图').textContent).toBe('true'); }); });