import { fireEvent, render, screen } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useStore } from '../store/useStore'; import { resetStore } from '../test/storeTestUtils'; import { ToolsPalette } from './ToolsPalette'; describe('ToolsPalette', () => { beforeEach(() => { resetStore(); }); it('switches workspace editing tools without showing AI prompt or duplicate undo tools', () => { const setActiveTool = vi.fn(); render( , ); fireEvent.click(screen.getByTitle('创建多边形 (P)')); fireEvent.click(screen.getByTitle('调整多边形 (E)')); fireEvent.click(screen.getByTitle('画笔 (B)')); fireEvent.click(screen.getByTitle('橡皮擦 (X)')); expect(setActiveTool).toHaveBeenNthCalledWith(1, 'create_polygon'); expect(setActiveTool).toHaveBeenNthCalledWith(2, 'edit_polygon'); expect(setActiveTool).toHaveBeenNthCalledWith(3, 'brush'); expect(setActiveTool).toHaveBeenNthCalledWith(4, 'eraser'); expect(screen.queryByTitle('正向选点 (SAM)')).not.toBeInTheDocument(); expect(screen.queryByTitle('反向选点 (SAM)')).not.toBeInTheDocument(); expect(screen.queryByTitle('边界框选 (SAM)')).not.toBeInTheDocument(); expect(screen.queryByTitle('撤销操作 (Ctrl+Z)')).not.toBeInTheDocument(); expect(screen.queryByTitle('重做操作 (Ctrl+Shift+Z)')).not.toBeInTheDocument(); expect(screen.queryByTitle('创建点 (C)')).not.toBeInTheDocument(); expect(screen.queryByTitle('创建线段 (L)')).not.toBeInTheDocument(); }); it('shows size controls for brush and eraser tools', () => { const { rerender } = render(); const brushSize = screen.getByLabelText('画笔大小'); fireEvent.change(brushSize, { target: { value: '36' } }); expect(useStore.getState().brushSize).toBe(36); rerender(); const eraserSize = screen.getByLabelText('橡皮擦大小'); fireEvent.change(eraserSize, { target: { value: '48' } }); expect(useStore.getState().eraserSize).toBe(48); }); it('places GT mask import after overlap removal with a distinct violet style', () => { const onImportGtMask = vi.fn(); render( , ); const overlapButton = screen.getByTitle('重叠区域去除 (-)'); const importButton = screen.getByTitle('导入 GT Mask'); fireEvent.click(importButton); expect(onImportGtMask).toHaveBeenCalled(); expect(importButton).toHaveClass('bg-violet-500/10'); expect(overlapButton.compareDocumentPosition(importButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); }); it('exposes clear mask action in the left toolbar', () => { const onClearMasks = vi.fn(); render(); fireEvent.click(screen.getByTitle('清空遮罩')); expect(onClearMasks).toHaveBeenCalled(); }); it('separates drawing, editing, and external action tool groups', () => { render(); const separators = screen.getAllByTestId('tool-group-separator'); const circleButton = screen.getByTitle('创建圆 (O)'); const brushButton = screen.getByTitle('画笔 (B)'); const removeButton = screen.getByTitle('重叠区域去除 (-)'); const clearButton = screen.getByTitle('清空遮罩'); const importButton = screen.getByTitle('导入 GT Mask'); expect(separators).toHaveLength(2); expect(circleButton.compareDocumentPosition(separators[0]) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(separators[0].compareDocumentPosition(brushButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(removeButton.compareDocumentPosition(separators[1]) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(separators[1].compareDocumentPosition(clearButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(clearButton.compareDocumentPosition(importButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(separators[1].compareDocumentPosition(importButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); separators.forEach((separator) => { expect(separator).toHaveClass('bg-white/15'); }); }); it('switches to SAM trigger and calls the AI navigation hook', () => { const setActiveTool = vi.fn(); const onTriggerAI = vi.fn(); render(); const aiButton = screen.getByTitle('打开 AI 智能分割'); expect(aiButton.querySelector('[data-testid="ai-segmentation-icon"]')).toBeInTheDocument(); fireEvent.click(aiButton); expect(setActiveTool).toHaveBeenCalledWith('sam_trigger'); expect(onTriggerAI).toHaveBeenCalled(); }); it('uses compact vertically scrollable layout for smaller workspaces', () => { const { container } = render(); const palette = container.firstElementChild; expect(palette).toHaveClass('w-14'); expect(palette).toHaveClass('overflow-y-auto'); expect(palette).toHaveClass('seg-scrollbar'); expect(palette?.firstElementChild).toHaveClass('w-12'); expect(screen.getByTitle('创建多边形 (P)')).toHaveClass('h-9'); expect(screen.getByTitle('打开 AI 智能分割')).toHaveClass('h-9'); }); });