import { mount } from '@vue/test-utils' import { describe, expect, it, vi } from 'vitest' import { defineComponent, h } from 'vue' import { useDragOrClick } from '@/composables/timetable/useDragOrClick' interface ComponentInstance { begin: (e: Event) => void } function makeHost(opts: Parameters[0]) { return defineComponent({ setup(_, { expose }) { const ctl = useDragOrClick(opts) expose({ begin: ctl.begin }) return () => h('div') }, }) } function makePointerEvent(type: string, x: number, y: number, pointerId = 1): PointerEvent { const e = new Event(type, { bubbles: true, cancelable: true }) as PointerEvent Object.defineProperty(e, 'pointerId', { value: pointerId }) Object.defineProperty(e, 'clientX', { value: x }) Object.defineProperty(e, 'clientY', { value: y }) return e } describe('useDragOrClick', () => { it('fires onClick when movement < threshold', async () => { const onClick = vi.fn() const onDragStart = vi.fn() const wrapper = mount(makeHost({ thresholdPx: 4, onClick, onDragStart })) const inst = wrapper.vm as unknown as ComponentInstance inst.begin(makePointerEvent('pointerdown', 10, 10)) window.dispatchEvent(makePointerEvent('pointermove', 11, 11)) window.dispatchEvent(makePointerEvent('pointerup', 11, 11)) expect(onClick).toHaveBeenCalledTimes(1) expect(onDragStart).not.toHaveBeenCalled() }) it('enters drag mode and emits onDragStart + onDragEnd when movement crosses threshold', async () => { const onClick = vi.fn() const onDragStart = vi.fn() const onDragEnd = vi.fn() const wrapper = mount(makeHost({ thresholdPx: 4, onClick, onDragStart, onDragEnd })) const inst = wrapper.vm as unknown as ComponentInstance inst.begin(makePointerEvent('pointerdown', 10, 10)) window.dispatchEvent(makePointerEvent('pointermove', 50, 10)) window.dispatchEvent(makePointerEvent('pointerup', 50, 10)) expect(onDragStart).toHaveBeenCalledTimes(1) expect(onDragEnd).toHaveBeenCalledTimes(1) expect(onClick).not.toHaveBeenCalled() }) it('Esc cancels an in-flight drag', async () => { const onDragEnd = vi.fn() const wrapper = mount(makeHost({ thresholdPx: 4, onDragEnd })) const inst = wrapper.vm as unknown as ComponentInstance inst.begin(makePointerEvent('pointerdown', 10, 10)) window.dispatchEvent(makePointerEvent('pointermove', 50, 10)) const esc = new KeyboardEvent('keydown', { key: 'Escape' }) window.dispatchEvent(esc) expect(onDragEnd).toHaveBeenCalledWith(expect.anything(), true) }) })