/* @vitest-environment jsdom */ import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createRef, type RefObject } from 'react'; import { describe, expect, it, vi } from 'vitest'; import { ImageCanvasBottomToolbarView } from './ImageCanvasBottomToolbarView'; describe('ImageCanvasBottomToolbarView', () => { function renderToolbar( overrides: Partial[0]> = {}, ) { const switchTool = vi.fn(); const specToolWrapRef = createRef(); const musicToolWrapRef = createRef(); const publicationToolWrapRef = createRef(); const view = render( , ); return { ...view, toolbar: screen.getByRole('toolbar', { name: 'AI画布工具栏' }), switchTool, refs: { specToolWrapRef, musicToolWrapRef, publicationToolWrapRef, }, }; } function renderToolbarView( effectiveTool: Parameters< typeof ImageCanvasBottomToolbarView >[0]['effectiveTool'], refs: { specToolWrapRef: RefObject; musicToolWrapRef: RefObject; publicationToolWrapRef: RefObject; }, ) { return ( ); } it('renders the expected canvas tools and forwards user selections', async () => { const user = userEvent.setup(); const { toolbar, switchTool } = renderToolbar(); expect(toolbar.className).toContain('genarrative-image-canvas__toolbar'); expect(toolbar.className).toContain( 'genarrative-image-canvas__toolbar--floating', ); expect( toolbar.querySelectorAll('.genarrative-image-canvas__toolbar-group'), ).toHaveLength(12); expect( toolbar.querySelectorAll('.genarrative-image-canvas__toolbar-divider'), ).toHaveLength(2); const toolExpectations = [ ['选择工具', 'select'], ['抓手工具', 'hand'], ['上传工具', 'upload'], ['生成图片', 'generate'], ['生成视频', 'video'], ['生成音乐', 'music'], ['生成规范', 'spec'], ['生成角色形象', 'character'], ['生成图标素材', 'icon'], ['生成UI设计图', 'ui-design'], ['宣发素材', 'publication'], ['生成游戏场景', 'scene'], ] as const; for (const [label, tool] of toolExpectations) { await user.click(within(toolbar).getByRole('button', { name: label })); expect(switchTool).toHaveBeenLastCalledWith(tool); } expect(switchTool).toHaveBeenCalledTimes(toolExpectations.length); }); it('only exposes persistent pressed state for navigation tools', () => { const { toolbar, rerender, refs } = renderToolbar({ effectiveTool: 'select', }); expect( within(toolbar) .getByRole('button', { name: '选择工具' }) .getAttribute('aria-pressed'), ).toBe('true'); expect( within(toolbar) .getByRole('button', { name: '生成图片' }) .getAttribute('aria-pressed'), ).toBe('false'); rerender(renderToolbarView('hand', refs)); expect( within(toolbar) .getByRole('button', { name: '抓手工具' }) .getAttribute('aria-pressed'), ).toBe('true'); rerender(renderToolbarView('music', refs)); expect( within(toolbar) .getByRole('button', { name: '生成音乐' }) .getAttribute('aria-pressed'), ).toBe('false'); }); it('opens and closes bottom option tools on pointer and keyboard focus', async () => { const user = userEvent.setup(); const openToolOptions = vi.fn(); const closeToolOptions = vi.fn(); const { toolbar, refs } = renderToolbar({ onOpenToolOptions: openToolOptions, onCloseToolOptions: closeToolOptions, }); await user.hover(within(toolbar).getByRole('button', { name: '生成规范' })); await user.unhover( within(toolbar).getByRole('button', { name: '生成规范' }), ); await user.hover(within(toolbar).getByRole('button', { name: '生成音乐' })); await user.unhover( within(toolbar).getByRole('button', { name: '生成音乐' }), ); const publicationTool = within(toolbar).getByRole('button', { name: '宣发素材', }); publicationTool.focus(); publicationTool.blur(); expect(openToolOptions).toHaveBeenCalledWith('spec'); expect(closeToolOptions).toHaveBeenCalledWith('spec'); expect(openToolOptions).toHaveBeenCalledWith('music'); expect(closeToolOptions).toHaveBeenCalledWith('music'); expect(openToolOptions).toHaveBeenCalledWith('publication'); expect(closeToolOptions).toHaveBeenCalledWith('publication'); }); });