4db6d28603
* 提示词:完善组件建议,增加容器背景与节点大小一致的实现 * 支持Delete删除节点 ctrl + -缩放 * 步骤结束弹窗提醒   * UI 树快捷删除  * zoom滑动条样式 before:  after:  Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/278 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
177 lines
5.3 KiB
TypeScript
177 lines
5.3 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { fireEvent } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import {
|
|
handlePreviewZoomKeyDown,
|
|
type PreviewZoomKeyboardActions,
|
|
type PreviewZoomKeyboardContext,
|
|
previewZoomUsesMetaModifier,
|
|
} from '../src/view/ui-editor/components/preview/previewZoomKeyboard';
|
|
|
|
function createActions(): PreviewZoomKeyboardActions {
|
|
return {
|
|
fit: vi.fn(),
|
|
resetToActualSize: vi.fn(),
|
|
zoomIn: vi.fn(),
|
|
zoomOut: vi.fn(),
|
|
};
|
|
}
|
|
|
|
function dispatchShortcut({
|
|
actions = createActions(),
|
|
context = {},
|
|
event,
|
|
target,
|
|
}: {
|
|
actions?: PreviewZoomKeyboardActions;
|
|
context?: Partial<PreviewZoomKeyboardContext>;
|
|
event: KeyboardEventInit;
|
|
target?: HTMLElement;
|
|
}) {
|
|
const resolvedTarget = target ?? document.createElement('div');
|
|
document.body.append(resolvedTarget);
|
|
const handler = vi.fn((keyboardEvent: KeyboardEvent) =>
|
|
handlePreviewZoomKeyDown(
|
|
keyboardEvent,
|
|
{
|
|
hasZoomableViewport: true,
|
|
isFocused: false,
|
|
isHovered: true,
|
|
usesMetaModifier: false,
|
|
...context,
|
|
},
|
|
actions,
|
|
),
|
|
);
|
|
resolvedTarget.addEventListener('keydown', handler);
|
|
fireEvent.keyDown(resolvedTarget, event);
|
|
const keyboardEvent = handler.mock.calls[0]?.[0];
|
|
return { actions, handler, keyboardEvent };
|
|
}
|
|
|
|
afterEach(() => {
|
|
document.body.replaceChildren();
|
|
});
|
|
|
|
describe('preview zoom keyboard shortcuts', () => {
|
|
it.each([
|
|
{ key: '+', code: 'Equal' },
|
|
{ key: '=', code: 'Equal' },
|
|
{ key: '+', code: 'NumpadAdd' },
|
|
])('zooms in for the supported main and numpad keys (%o)', (event) => {
|
|
const { actions, keyboardEvent } = dispatchShortcut({
|
|
event: { ...event, ctrlKey: true, cancelable: true, repeat: true },
|
|
});
|
|
|
|
expect(actions.zoomIn).toHaveBeenCalledTimes(1);
|
|
expect(keyboardEvent?.defaultPrevented).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
{ key: '-', code: 'Minus' },
|
|
{ key: '-', code: 'NumpadSubtract' },
|
|
])('zooms out for the supported main and numpad keys (%o)', (event) => {
|
|
const { actions, keyboardEvent } = dispatchShortcut({
|
|
event: { ...event, ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions.zoomOut).toHaveBeenCalledTimes(1);
|
|
expect(keyboardEvent?.defaultPrevented).toBe(true);
|
|
});
|
|
|
|
it('does not treat underscore as zoom out', () => {
|
|
const { actions, keyboardEvent } = dispatchShortcut({
|
|
event: {
|
|
key: '_',
|
|
code: 'Minus',
|
|
ctrlKey: true,
|
|
shiftKey: true,
|
|
cancelable: true,
|
|
},
|
|
});
|
|
|
|
expect(actions.zoomOut).not.toHaveBeenCalled();
|
|
expect(keyboardEvent?.defaultPrevented).toBe(false);
|
|
});
|
|
|
|
it.each([
|
|
{ key: '0', action: 'fit' as const },
|
|
{ key: '1', action: 'resetToActualSize' as const },
|
|
])('keeps the existing $key shortcut', ({ key, action }) => {
|
|
const { actions } = dispatchShortcut({
|
|
event: { key, ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions[action]).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('uses Cmd on Apple platforms and Ctrl elsewhere', () => {
|
|
expect(previewZoomUsesMetaModifier('MacIntel')).toBe(true);
|
|
expect(previewZoomUsesMetaModifier('iPad')).toBe(true);
|
|
expect(previewZoomUsesMetaModifier('Win32')).toBe(false);
|
|
|
|
const appleActions = createActions();
|
|
dispatchShortcut({
|
|
actions: appleActions,
|
|
context: { usesMetaModifier: true },
|
|
event: { key: '=', ctrlKey: true, cancelable: true },
|
|
});
|
|
expect(appleActions.zoomIn).not.toHaveBeenCalled();
|
|
dispatchShortcut({
|
|
actions: appleActions,
|
|
context: { usesMetaModifier: true },
|
|
event: { key: '=', metaKey: true, cancelable: true },
|
|
});
|
|
expect(appleActions.zoomIn).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it.each([
|
|
{ isHovered: false, isFocused: false, hasZoomableViewport: true },
|
|
{ isHovered: true, isFocused: false, hasZoomableViewport: false },
|
|
])('leaves inactive preview shortcuts to the host (%o)', (context) => {
|
|
const { actions, keyboardEvent } = dispatchShortcut({
|
|
context,
|
|
event: { key: '=', ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions.zoomIn).not.toHaveBeenCalled();
|
|
expect(keyboardEvent?.defaultPrevented).toBe(false);
|
|
});
|
|
|
|
it('works while the preview is focused without being hovered', () => {
|
|
const { actions } = dispatchShortcut({
|
|
context: { isFocused: true, isHovered: false },
|
|
event: { key: '=', ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions.zoomIn).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it.each(['input', 'button', 'a'])('ignores interactive %s targets', (tag) => {
|
|
const target = document.createElement(tag);
|
|
if (target instanceof HTMLAnchorElement) target.href = '#preview';
|
|
const { actions, keyboardEvent } = dispatchShortcut({
|
|
target,
|
|
event: { key: '=', ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions.zoomIn).not.toHaveBeenCalled();
|
|
expect(keyboardEvent?.defaultPrevented).toBe(false);
|
|
});
|
|
|
|
it('does not override an event already handled by another control', () => {
|
|
const target = document.createElement('div');
|
|
target.addEventListener('keydown', (event) => event.preventDefault(), {
|
|
once: true,
|
|
});
|
|
const { actions } = dispatchShortcut({
|
|
target,
|
|
event: { key: '=', ctrlKey: true, cancelable: true },
|
|
});
|
|
|
|
expect(actions.zoomIn).not.toHaveBeenCalled();
|
|
});
|
|
});
|