/* @vitest-environment jsdom */ import { fireEvent, render, screen, within } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import type { CanvasLayer } from './ImageCanvasEditorTypes'; import { ImageCanvasMetadataModalView } from './ImageCanvasMetadataModalView'; function createLayer(overrides: Partial = {}): CanvasLayer { return { id: 'layer-1', resourceId: 'resource-1', title: '生成主图', src: 'data:image/png;base64,layer', x: 0, y: 0, width: 320, height: 240, originalWidth: 1024, originalHeight: 768, zIndex: 1, sourceType: 'generated', ...overrides, }; } describe('ImageCanvasMetadataModalView', () => { it('renders generated layer metadata with generation inputs and references', () => { render( , ); const dialog = screen.getByRole('dialog', { name: '图片信息' }); expect(within(dialog).queryByText('生成主图')).toBeNull(); expect(within(dialog).getByText('生成图片')).toBeTruthy(); expect(within(dialog).getByText('生成提示词')).toBeTruthy(); expect(within(dialog).getByText('清爽游戏按钮')).toBeTruthy(); expect(within(dialog).getByText('参考图')).toBeTruthy(); expect(within(dialog).getByText('角色立绘')).toBeTruthy(); expect(within(dialog).getByText('项目资源 · resource-reference')).toBeTruthy(); expect(within(dialog).getByText('gpt-image-2')).toBeTruthy(); expect(within(dialog).getByText('1024 x 768 px')).toBeTruthy(); expect(within(dialog).queryByText('Provider')).toBeNull(); expect(within(dialog).queryByText(/VectorEngine/u)).toBeNull(); expect(within(dialog).queryByText(/task-/u)).toBeNull(); expect(within(dialog).getByText('123')).toBeTruthy(); expect(within(dialog).getByText('generated/object.png')).toBeTruthy(); }); it('renders nanobanana2 instead of the raw Gemini image model id', () => { render( , ); const dialog = screen.getByRole('dialog', { name: '图片信息' }); expect(within(dialog).getByText('nanobanana2')).toBeTruthy(); expect( within(dialog).queryByText('gemini-3.1-flash-image-preview'), ).toBeNull(); }); it('renders upload fallback values and closes through the modal shell', () => { const onClose = vi.fn(); render( , ); const dialog = screen.getByRole('dialog', { name: '图片信息' }); expect(within(dialog).queryByText('上传素材')).toBeNull(); expect(within(dialog).getByText('上传图片')).toBeTruthy(); expect(within(dialog).getAllByText('-').length).toBeGreaterThanOrEqual(3); expect(within(dialog).getByText('asset-object-1')).toBeTruthy(); fireEvent.click(within(dialog).getByRole('button', { name: '关闭图片信息' })); expect(onClose).toHaveBeenCalledTimes(1); }); it('renders generated video metadata with video labels and close action', () => { const onClose = vi.fn(); render( , ); const dialog = screen.getByRole('dialog', { name: '视频信息' }); expect(within(dialog).getByText('视频类型')).toBeTruthy(); expect(within(dialog).getByText('生成视频')).toBeTruthy(); expect(within(dialog).getByText('视频描述')).toBeTruthy(); expect(within(dialog).getByText('让角色向镜头挥手')).toBeTruthy(); expect(within(dialog).getByText('kling3.0-omni')).toBeTruthy(); expect(within(dialog).getByText('1280 x 720 px')).toBeTruthy(); fireEvent.click(within(dialog).getByRole('button', { name: '关闭视频信息' })); expect(onClose).toHaveBeenCalledTimes(1); }); it('renders generated audio metadata with duration instead of resolution', () => { render( , ); const dialog = screen.getByRole('dialog', { name: '音频信息' }); expect(within(dialog).getByText('音频类型')).toBeTruthy(); expect(within(dialog).getByText('游戏背景音乐')).toBeTruthy(); expect(within(dialog).getByText('音乐描述')).toBeTruthy(); expect(within(dialog).getByText('紧张的地下城循环音乐')).toBeTruthy(); expect(within(dialog).getByText('时长')).toBeTruthy(); expect(within(dialog).getByText('1:15')).toBeTruthy(); expect(within(dialog).queryByText('Resolution')).toBeNull(); expect(within(dialog).queryByText('420 x 120 px')).toBeNull(); }); it('does not render a dialog when no layer is selected', () => { render(); expect(screen.queryByRole('dialog', { name: '图片信息' })).toBeNull(); }); });