/* @vitest-environment jsdom */ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { ApiClientError } from '../../services/apiClient'; import { useImageCanvasEditorChrome } from './useImageCanvasEditorChrome'; const renameEditorProjectMock = vi.hoisted(() => vi.fn()); vi.mock('../../services/image-editor/editorProjectClient', async () => { const actual = await vi.importActual< typeof import('../../services/image-editor/editorProjectClient') >('../../services/image-editor/editorProjectClient'); return { ...actual, renameEditorProject: renameEditorProjectMock, }; }); function ChromeHarness({ openEditorLoginModal = vi.fn(), }: { openEditorLoginModal?: (postLoginAction?: (() => void) | null) => void; }) { const chrome = useImageCanvasEditorChrome({ openEditorLoginModal }); return (
{chrome.projectTitle} {chrome.projectRenameValue} {String(chrome.isRenamingProject)} {String(chrome.isProjectRenameSaving)} {chrome.projectRenameError ?? '-'} {chrome.activeSidebarPanel ?? '-'} {chrome.activeTool} {String(chrome.isZoomMenuOpen)} {String(chrome.isBackgroundSettingsOpen)} {String(chrome.isMinimapOpen)} {chrome.canvasBackgroundColor} {chrome.canvasBackgroundHexValue}
); } describe('useImageCanvasEditorChrome', () => { beforeEach(() => { vi.clearAllMocks(); }); it('renames projects and resets rename state from the saved title', async () => { renameEditorProjectMock.mockResolvedValueOnce({ projectId: 'project-1', title: '后端项目名', viewport: { x: 0, y: 0, scale: 1 }, layers: [], resources: [], updatedAt: '2026-06-17T00:00:00.000Z', }); render(); fireEvent.click(screen.getByRole('button', { name: 'set title' })); fireEvent.click(screen.getByRole('button', { name: 'start rename' })); expect(screen.getByTestId('rename-value').textContent).toBe('已有项目'); fireEvent.click(screen.getByRole('button', { name: 'set rename' })); fireEvent.click(screen.getByRole('button', { name: 'submit rename' })); expect(screen.getByTestId('saving').textContent).toBe('true'); await waitFor(() => { expect(renameEditorProjectMock).toHaveBeenCalledWith( 'project-1', '新项目', ); }); expect(screen.getByTestId('title').textContent).toBe('后端项目名'); expect(screen.getByTestId('rename-value').textContent).toBe('后端项目名'); expect(screen.getByTestId('renaming').textContent).toBe('false'); expect(screen.getByTestId('saving').textContent).toBe('false'); }); it('validates rename input and opens login on rename auth errors', async () => { const openEditorLoginModal = vi.fn(); renameEditorProjectMock.mockRejectedValueOnce( new ApiClientError({ message: '未授权访问', status: 401, code: 'UNAUTHORIZED', }), ); render(); fireEvent.click(screen.getByRole('button', { name: 'blank rename' })); fireEvent.click(screen.getByRole('button', { name: 'submit rename' })); expect(screen.getByTestId('rename-error').textContent).toBe( '项目名称不能为空', ); expect(renameEditorProjectMock).not.toHaveBeenCalled(); fireEvent.click(screen.getByRole('button', { name: 'set rename' })); fireEvent.click(screen.getByRole('button', { name: 'submit rename' })); await waitFor(() => { expect(openEditorLoginModal).toHaveBeenCalledTimes(1); }); expect(screen.getByTestId('rename-error').textContent).toBe('未授权访问'); }); it('manages background colors and chrome panel toggles', () => { render(); fireEvent.click(screen.getByRole('button', { name: 'apply short hex' })); expect(screen.getByTestId('background-color').textContent).toBe('#aabbcc'); expect(screen.getByTestId('background-hex').textContent).toBe('#aabbcc'); fireEvent.click(screen.getByRole('button', { name: 'invalid hex' })); expect(screen.getByTestId('background-color').textContent).toBe('#aabbcc'); expect(screen.getByTestId('background-hex').textContent).toBe( '#not-a-color', ); fireEvent.click(screen.getByRole('button', { name: 'valid hex' })); expect(screen.getByTestId('background-color').textContent).toBe('#ffffff'); expect(screen.getByTestId('background-hex').textContent).toBe('#ffffff'); fireEvent.click(screen.getByRole('button', { name: 'toggle assets' })); expect(screen.getByTestId('sidebar').textContent).toBe('-'); fireEvent.click(screen.getByRole('button', { name: 'toggle layers' })); expect(screen.getByTestId('sidebar').textContent).toBe('layers'); fireEvent.click(screen.getByRole('button', { name: 'toggle zoom' })); fireEvent.click(screen.getByRole('button', { name: 'toggle background' })); fireEvent.click(screen.getByRole('button', { name: 'toggle minimap' })); fireEvent.click(screen.getByRole('button', { name: 'set hand' })); expect(screen.getByTestId('zoom').textContent).toBe('true'); expect(screen.getByTestId('background-open').textContent).toBe('true'); expect(screen.getByTestId('minimap').textContent).toBe('false'); expect(screen.getByTestId('tool').textContent).toBe('hand'); act(() => { screen.getByRole('button', { name: 'close panels' }).click(); }); expect(screen.getByTestId('sidebar').textContent).toBe('-'); expect(screen.getByTestId('zoom').textContent).toBe('false'); expect(screen.getByTestId('background-open').textContent).toBe('false'); }); });