browserDrag.test.ts 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import {afterEach,expect,it,vi} from 'vitest'
  2. import {flushPromises} from '@vue/test-utils'
  3. import {BrowserDragSession} from '@/utils/browserDrag'
  4. import type {BrowserPointerAction} from '@shared/managedBrowser'
  5. afterEach(()=>vi.useRealTimers())
  6. it('copies only frame coordinates from the viewer pointer state into every request',async()=>{
  7. vi.useFakeTimers()
  8. const start={x:1,y:2,clientX:101,clientY:102,pointerId:3,element:document.createElement('img')}
  9. const send=vi.fn(async(_action:BrowserPointerAction)=>{}),session=new BrowserDragSession(start,send,vi.fn())
  10. start.x=99
  11. await flushPromises();await vi.advanceTimersByTimeAsync(1000)
  12. session.move({...start,x:30,y:40});await flushPromises()
  13. session.end({...start,x:50,y:60});await session.done
  14. expect(send.mock.calls.map(([action])=>action)).toEqual([
  15. {kind:'pointer',gestureId:session.id,phase:'start',x:1,y:2},
  16. {kind:'pointer',gestureId:session.id,phase:'move',x:1,y:2},
  17. {kind:'pointer',gestureId:session.id,phase:'move',x:30,y:40},
  18. {kind:'pointer',gestureId:session.id,phase:'end',x:50,y:60},
  19. ])
  20. })
  21. it('serializes input and coalesces pending movement without losing the final point',async()=>{
  22. vi.useFakeTimers()
  23. let release!:()=>void
  24. const send=vi.fn(async(_action:BrowserPointerAction)=>{})
  25. send.mockImplementationOnce(()=>new Promise<void>(resolve=>{release=resolve}))
  26. const failed=vi.fn(),session=new BrowserDragSession({x:1,y:2},send,failed)
  27. session.move({x:10,y:20});session.move({x:30,y:40});session.end({x:50,y:60})
  28. expect(send).toHaveBeenCalledTimes(1)
  29. release();await session.done
  30. expect(send.mock.calls.map(([action])=>[action.phase,action.x,action.y])).toEqual([['start',1,2],['move',30,40],['end',50,60]])
  31. expect(new Set(send.mock.calls.map(([action])=>action.gestureId)).size).toBe(1)
  32. await vi.advanceTimersByTimeAsync(10000);expect(send).toHaveBeenCalledTimes(3);expect(failed).not.toHaveBeenCalled()
  33. })
  34. it('keeps a stationary hold alive and discards queued moves on cancellation',async()=>{
  35. vi.useFakeTimers()
  36. const send=vi.fn(async(_action:BrowserPointerAction)=>{}),session=new BrowserDragSession({x:1,y:2},send,vi.fn())
  37. await flushPromises();await vi.advanceTimersByTimeAsync(1000)
  38. expect(send.mock.calls.map(([action])=>action.phase)).toEqual(['start','move'])
  39. let release!:()=>void
  40. send.mockImplementationOnce(()=>new Promise<void>(resolve=>{release=resolve}))
  41. session.move({x:10,y:20});session.move({x:30,y:40})
  42. const done=session.cancel();release();await done
  43. expect(send.mock.calls.map(([action])=>action.phase)).toEqual(['start','move','move','cancel'])
  44. await vi.advanceTimersByTimeAsync(10000);expect(send).toHaveBeenCalledTimes(4)
  45. })
  46. it('releases an uncertain input after failure without replaying it',async()=>{
  47. vi.useFakeTimers()
  48. const send=vi.fn(async(_action:BrowserPointerAction)=>{}).mockRejectedValueOnce(new Error('timeout')),failed=vi.fn()
  49. const start={x:1,y:2,clientX:101,clientY:102,pointerId:3,element:document.createElement('img')}
  50. const session=new BrowserDragSession(start,send,failed)
  51. await session.done
  52. expect(send.mock.calls.map(([action])=>action)).toEqual([
  53. {kind:'pointer',gestureId:session.id,phase:'start',x:1,y:2},
  54. {kind:'pointer',gestureId:session.id,phase:'cancel',x:1,y:2},
  55. ])
  56. expect(failed).toHaveBeenCalledWith(expect.objectContaining({message:'timeout'}))
  57. await vi.advanceTimersByTimeAsync(10000);expect(send).toHaveBeenCalledTimes(2)
  58. })