03a4410272
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Co-authored-by: kdletters <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136 Co-authored-by: menghao <mh18530625731@163.com> Co-committed-by: menghao <mh18530625731@163.com>
237 lines
8.0 KiB
TypeScript
237 lines
8.0 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { fireEvent, render, screen, within } from '@testing-library/react';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
import type { StageMinimapModel } from './ImageCanvasInteractionModel';
|
|
import { ImageCanvasPanelDockView } from './ImageCanvasPanelDockView';
|
|
|
|
function renderPanelDock(
|
|
overrides: Partial<Parameters<typeof ImageCanvasPanelDockView>[0]> = {},
|
|
) {
|
|
const props: Parameters<typeof ImageCanvasPanelDockView>[0] = {
|
|
viewport: { x: 0, y: 0, scale: 1 },
|
|
canvasBackgroundColor: '#f8fafc',
|
|
canvasBackgroundHexValue: '#f8fafc',
|
|
canUndo: true,
|
|
canRedo: true,
|
|
isZoomMenuOpen: false,
|
|
isBackgroundSettingsOpen: false,
|
|
activeSidebarPanel: null,
|
|
isAgentConversationEnabled: true,
|
|
isAgentConversationOpen: false,
|
|
isMinimapOpen: false,
|
|
minimapModel: null,
|
|
onFitLayers: vi.fn(),
|
|
onUndoCanvasChange: vi.fn(),
|
|
onRedoCanvasChange: vi.fn(),
|
|
onUpdateScaleFromCenter: vi.fn(),
|
|
onToggleZoomMenu: vi.fn(),
|
|
onCloseZoomMenu: vi.fn(),
|
|
onToggleBackgroundSettings: vi.fn(),
|
|
onApplyCanvasBackgroundColor: vi.fn(),
|
|
onCanvasBackgroundHexChange: vi.fn(),
|
|
onToggleSidebarPanel: vi.fn(),
|
|
onToggleAgentConversation: vi.fn(),
|
|
onToggleMinimap: vi.fn(),
|
|
onMinimapPointerDown: vi.fn(),
|
|
...overrides,
|
|
};
|
|
|
|
render(<ImageCanvasPanelDockView {...props} />);
|
|
|
|
return props;
|
|
}
|
|
|
|
function mockElementRect(
|
|
element: Element,
|
|
rect: Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>,
|
|
) {
|
|
Object.defineProperty(element, 'getBoundingClientRect', {
|
|
configurable: true,
|
|
value: () => ({
|
|
...rect,
|
|
right: rect.left + rect.width,
|
|
bottom: rect.top + rect.height,
|
|
x: rect.left,
|
|
y: rect.top,
|
|
toJSON: () => ({}),
|
|
}),
|
|
});
|
|
}
|
|
|
|
describe('ImageCanvasPanelDockView', () => {
|
|
it('renders panel dock actions and forwards common controls', () => {
|
|
const props = renderPanelDock({
|
|
activeSidebarPanel: 'assets',
|
|
isMinimapOpen: true,
|
|
minimapModel: {
|
|
bounds: { minX: 0, minY: 0, maxX: 100, maxY: 100 },
|
|
scale: 1,
|
|
layers: [],
|
|
viewport: { left: '10%', top: '12%', width: '20%', height: '24%' },
|
|
} satisfies StageMinimapModel,
|
|
});
|
|
|
|
const toolbar = screen.getByRole('toolbar', { name: '画布面板入口' });
|
|
|
|
expect(toolbar.className).toContain(
|
|
'image-canvas-editor__panel-dock--sidebar-open',
|
|
);
|
|
expect(
|
|
within(toolbar)
|
|
.getByRole('button', { name: '打开素材' })
|
|
.getAttribute('aria-pressed'),
|
|
).toBe('true');
|
|
expect(within(toolbar).getByRole('button', { name: '恢复' })).toBeTruthy();
|
|
expect(screen.getByRole('button', { name: '画布小地图' })).toBeTruthy();
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '重置画布视图' }));
|
|
fireEvent.click(within(toolbar).getByRole('button', { name: '撤销' }));
|
|
fireEvent.click(within(toolbar).getByRole('button', { name: '恢复' }));
|
|
fireEvent.click(within(toolbar).getByRole('button', { name: '打开图层' }));
|
|
fireEvent.click(
|
|
within(toolbar).getByRole('button', { name: '画布 Agent' }),
|
|
);
|
|
fireEvent.click(
|
|
within(toolbar).getByRole('button', { name: '切换小地图' }),
|
|
);
|
|
|
|
expect(props.onFitLayers).toHaveBeenCalledTimes(1);
|
|
expect(props.onUndoCanvasChange).toHaveBeenCalledTimes(1);
|
|
expect(props.onRedoCanvasChange).toHaveBeenCalledTimes(1);
|
|
expect(props.onToggleSidebarPanel).toHaveBeenCalledWith('layers');
|
|
expect(props.onToggleAgentConversation).toHaveBeenCalledTimes(1);
|
|
expect(props.onToggleMinimap).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('hides the Agent entry when the Agent sidebar is disabled', () => {
|
|
renderPanelDock({ isAgentConversationEnabled: false });
|
|
|
|
expect(screen.queryByRole('button', { name: '画布 Agent' })).toBeNull();
|
|
});
|
|
|
|
it('keeps the background picker visible when the selected color is white', () => {
|
|
const props = renderPanelDock({
|
|
canvasBackgroundColor: '#ffffff',
|
|
canvasBackgroundHexValue: '#ffffff',
|
|
isBackgroundSettingsOpen: true,
|
|
});
|
|
|
|
const panel = screen.getByRole('dialog', { name: '画布背景设置' });
|
|
expect(
|
|
screen
|
|
.getByRole('button', { name: '画布背景色' })
|
|
.getAttribute('aria-expanded'),
|
|
).toBe('true');
|
|
const spectrumButton = within(panel).getByRole('button', {
|
|
name: '画布背景色盘',
|
|
});
|
|
const hueButton = within(panel).getByRole('button', {
|
|
name: '画布背景色相',
|
|
});
|
|
const spectrumHandle = spectrumButton.querySelector(
|
|
'.image-canvas-editor__background-spectrum-handle',
|
|
) as HTMLElement;
|
|
const hueHandle = hueButton.querySelector(
|
|
'.image-canvas-editor__background-hue-handle',
|
|
) as HTMLElement;
|
|
|
|
expect(
|
|
spectrumButton.style.getPropertyValue(
|
|
'--image-canvas-background-hue-color',
|
|
),
|
|
).toBe('#0080ff');
|
|
expect(
|
|
spectrumHandle.style.getPropertyValue(
|
|
'--image-canvas-background-spectrum-handle-left',
|
|
),
|
|
).toBe('clamp(0.42rem, 0%, calc(100% - 0.42rem))');
|
|
expect(
|
|
spectrumHandle.style.getPropertyValue(
|
|
'--image-canvas-background-spectrum-handle-top',
|
|
),
|
|
).toBe('clamp(0.42rem, 0%, calc(100% - 0.42rem))');
|
|
expect(
|
|
hueHandle.style.getPropertyValue(
|
|
'--image-canvas-background-hue-handle-left',
|
|
),
|
|
).toBe(
|
|
'clamp(0.39rem, 58.333333333333336%, calc(100% - 0.39rem))',
|
|
);
|
|
|
|
mockElementRect(hueButton, {
|
|
left: 0,
|
|
top: 0,
|
|
width: 100,
|
|
height: 20,
|
|
});
|
|
fireEvent.pointerDown(hueButton, {
|
|
pointerId: 1,
|
|
buttons: 1,
|
|
clientX: 20,
|
|
clientY: 10,
|
|
});
|
|
|
|
expect(props.onApplyCanvasBackgroundColor).toHaveBeenCalledWith('#ffffff');
|
|
});
|
|
|
|
it('renders zoom and background settings with callback wiring', () => {
|
|
const props = renderPanelDock({
|
|
viewport: { x: 0, y: 0, scale: 0.5 },
|
|
canvasBackgroundColor: '#ff0000',
|
|
canvasBackgroundHexValue: '#ff0000',
|
|
isZoomMenuOpen: true,
|
|
isBackgroundSettingsOpen: true,
|
|
});
|
|
|
|
expect(
|
|
screen.getByRole('button', { name: '当前缩放比例 100%' }),
|
|
).toBeTruthy();
|
|
|
|
fireEvent.click(screen.getByRole('menuitem', { name: '缩放至50%' }));
|
|
fireEvent.click(screen.getByRole('menuitem', { name: '缩放至100%' }));
|
|
fireEvent.click(screen.getByRole('menuitem', { name: '缩放至200%' }));
|
|
fireEvent.click(screen.getByRole('menuitem', { name: '显示画布所有元素' }));
|
|
|
|
expect(props.onUpdateScaleFromCenter).toHaveBeenCalledWith(0.25);
|
|
expect(props.onUpdateScaleFromCenter).toHaveBeenCalledWith(0.5);
|
|
expect(props.onUpdateScaleFromCenter).toHaveBeenCalledWith(1);
|
|
expect(props.onFitLayers).toHaveBeenCalledTimes(1);
|
|
expect(props.onCloseZoomMenu).toHaveBeenCalledTimes(4);
|
|
|
|
const panel = screen.getByRole('dialog', { name: '画布背景设置' });
|
|
|
|
expect(panel.querySelector('input[type="color"]')).toBeNull();
|
|
fireEvent.click(within(panel).getByRole('button', { name: '暖灰' }));
|
|
const spectrumButton = within(panel).getByRole('button', {
|
|
name: '画布背景色盘',
|
|
});
|
|
mockElementRect(spectrumButton, {
|
|
left: 0,
|
|
top: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
fireEvent.pointerDown(spectrumButton, {
|
|
pointerId: 1,
|
|
buttons: 1,
|
|
clientX: 50,
|
|
clientY: 50,
|
|
});
|
|
fireEvent.change(within(panel).getByLabelText('画布背景十六进制颜色'), {
|
|
target: { value: '#abc' },
|
|
});
|
|
fireEvent.click(within(panel).getByRole('button', { name: '恢复默认' }));
|
|
fireEvent.click(
|
|
within(panel).getByRole('button', { name: '关闭画布背景设置' }),
|
|
);
|
|
|
|
expect(props.onApplyCanvasBackgroundColor).toHaveBeenCalledWith('#f3f0ea');
|
|
expect(props.onApplyCanvasBackgroundColor).toHaveBeenCalledWith('#804040');
|
|
expect(props.onCanvasBackgroundHexChange).toHaveBeenCalledWith('#abc');
|
|
expect(props.onApplyCanvasBackgroundColor).toHaveBeenCalledWith('#f8fafc');
|
|
expect(props.onToggleBackgroundSettings).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|