From b56d5b781a3d51eb57d711d67efa6b31ca3850fe Mon Sep 17 00:00:00 2001 From: bymyself Date: Tue, 9 Sep 2025 20:59:29 -0700 Subject: [PATCH] add unit test for keybinding service event forwarding --- .../keybindingService.forwarding.test.ts | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 tests-ui/tests/services/keybindingService.forwarding.test.ts diff --git a/tests-ui/tests/services/keybindingService.forwarding.test.ts b/tests-ui/tests/services/keybindingService.forwarding.test.ts new file mode 100644 index 000000000..30753c87b --- /dev/null +++ b/tests-ui/tests/services/keybindingService.forwarding.test.ts @@ -0,0 +1,182 @@ +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { app } from '@/scripts/app' +import { useKeybindingService } from '@/services/keybindingService' +import { useCommandStore } from '@/stores/commandStore' +import { useDialogStore } from '@/stores/dialogStore' + +// Mock the app and canvas using factory functions +vi.mock('@/scripts/app', () => { + return { + app: { + canvas: { + processKey: vi.fn() + } + } + } +}) + +// Mock stores +vi.mock('@/stores/settingStore', () => ({ + useSettingStore: vi.fn(() => ({ + get: vi.fn(() => []) + })) +})) + +vi.mock('@/stores/dialogStore', () => ({ + useDialogStore: vi.fn(() => ({ + dialogStack: [] + })) +})) + +// Test utility for creating keyboard events with mocked methods +function createTestKeyboardEvent( + key: string, + options: { + target?: Element + ctrlKey?: boolean + altKey?: boolean + metaKey?: boolean + } = {} +): KeyboardEvent { + const { + target = document.body, + ctrlKey = false, + altKey = false, + metaKey = false + } = options + + const event = new KeyboardEvent('keydown', { + key, + ctrlKey, + altKey, + metaKey, + bubbles: true, + cancelable: true + }) + + // Mock event methods + event.preventDefault = vi.fn() + event.composedPath = vi.fn(() => [target]) + + return event +} + +describe('keybindingService - Event Forwarding', () => { + let keybindingService: ReturnType + let mockCommandExecute: ReturnType + let mockProcessKey: ReturnType + + beforeEach(() => { + vi.clearAllMocks() + setActivePinia(createPinia()) + + // Get reference to mocked function + mockProcessKey = vi.mocked(app.canvas.processKey) + + // Mock command store execute + mockCommandExecute = vi.fn() + const commandStore = useCommandStore() + commandStore.execute = mockCommandExecute + + // Reset dialog store mock to empty + vi.mocked(useDialogStore).mockReturnValue({ + dialogStack: [] + } as any) + + keybindingService = useKeybindingService() + keybindingService.registerCoreKeybindings() + }) + + it('should forward Delete key to canvas when no keybinding exists', async () => { + const event = createTestKeyboardEvent('Delete') + + await keybindingService.keybindHandler(event) + + // Should forward to canvas processKey + expect(mockProcessKey).toHaveBeenCalledWith(event) + // Should not execute any command + expect(mockCommandExecute).not.toHaveBeenCalled() + }) + + it('should forward Backspace key to canvas when no keybinding exists', async () => { + const event = createTestKeyboardEvent('Backspace') + + await keybindingService.keybindHandler(event) + + expect(mockProcessKey).toHaveBeenCalledWith(event) + expect(mockCommandExecute).not.toHaveBeenCalled() + }) + + it('should not forward Delete key when typing in input field', async () => { + const inputElement = document.createElement('input') + const event = createTestKeyboardEvent('Delete', { target: inputElement }) + + await keybindingService.keybindHandler(event) + + // Should not forward to canvas when in input field + expect(mockProcessKey).not.toHaveBeenCalled() + expect(mockCommandExecute).not.toHaveBeenCalled() + }) + + it('should not forward Delete key when typing in textarea', async () => { + const textareaElement = document.createElement('textarea') + const event = createTestKeyboardEvent('Delete', { target: textareaElement }) + + await keybindingService.keybindHandler(event) + + expect(mockProcessKey).not.toHaveBeenCalled() + expect(mockCommandExecute).not.toHaveBeenCalled() + }) + + it('should not forward Delete key when canvas processKey is not available', async () => { + // Temporarily replace processKey with undefined + const originalProcessKey = vi.mocked(app.canvas).processKey + vi.mocked(app.canvas).processKey = undefined as any + + const event = createTestKeyboardEvent('Delete') + + await keybindingService.keybindHandler(event) + + expect(mockCommandExecute).not.toHaveBeenCalled() + + // Restore processKey for other tests + vi.mocked(app.canvas).processKey = originalProcessKey + }) + + it('should not forward Delete key when canvas is not available', async () => { + // Temporarily set canvas to null + const originalCanvas = vi.mocked(app).canvas + vi.mocked(app).canvas = null as any + + const event = createTestKeyboardEvent('Delete') + + await keybindingService.keybindHandler(event) + + expect(mockCommandExecute).not.toHaveBeenCalled() + + // Restore canvas for other tests + vi.mocked(app).canvas = originalCanvas + }) + + it('should not forward non-canvas keys', async () => { + const event = createTestKeyboardEvent('Enter') + + await keybindingService.keybindHandler(event) + + // Should not forward Enter key + expect(mockProcessKey).not.toHaveBeenCalled() + expect(mockCommandExecute).not.toHaveBeenCalled() + }) + + it('should not forward when modifier keys are pressed', async () => { + const event = createTestKeyboardEvent('Delete', { ctrlKey: true }) + + await keybindingService.keybindHandler(event) + + // Should not forward when modifiers are pressed + expect(mockProcessKey).not.toHaveBeenCalled() + expect(mockCommandExecute).not.toHaveBeenCalled() + }) +})