vOn.spec.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { patchEvent } from '../../src/modules/events'
  2. import { withKeys, withModifiers } from '@vue/runtime-dom'
  3. function triggerEvent(
  4. target: Element,
  5. event: string,
  6. process?: (e: any) => any,
  7. ) {
  8. const e = new Event(event, {
  9. bubbles: true,
  10. cancelable: true,
  11. })
  12. if (event === 'click') {
  13. ;(e as any).button = 0
  14. }
  15. if (process) process(e)
  16. target.dispatchEvent(e)
  17. return e
  18. }
  19. describe('runtime-dom: v-on directive', () => {
  20. test('it should support "stop" and "prevent"', () => {
  21. const parent = document.createElement('div')
  22. const child = document.createElement('input')
  23. parent.appendChild(child)
  24. const childNextValue = withModifiers(vi.fn(), ['prevent', 'stop'])
  25. patchEvent(child, 'onClick', null, childNextValue, null)
  26. const parentNextValue = vi.fn()
  27. patchEvent(parent, 'onClick', null, parentNextValue, null)
  28. expect(triggerEvent(child, 'click').defaultPrevented).toBe(true)
  29. expect(parentNextValue).not.toBeCalled()
  30. })
  31. test('it should support "self"', () => {
  32. const parent = document.createElement('div')
  33. const child = document.createElement('input')
  34. parent.appendChild(child)
  35. const fn = vi.fn()
  36. const handler = withModifiers(fn, ['self'])
  37. patchEvent(parent, 'onClick', null, handler, null)
  38. triggerEvent(child, 'click')
  39. expect(fn).not.toBeCalled()
  40. })
  41. test('it should support key modifiers and system modifiers', () => {
  42. const keyNames = ['ctrl', 'shift', 'meta', 'alt'] as const
  43. keyNames.forEach(keyName => {
  44. const el = document.createElement('div')
  45. const fn = vi.fn()
  46. // <div @keyup[keyName].esc="test"/>
  47. const nextValue = withKeys(withModifiers(fn, [keyName]), [
  48. 'esc',
  49. 'arrow-left',
  50. ])
  51. patchEvent(el, 'onKeyup', null, nextValue, null)
  52. triggerEvent(el, 'keyup', e => (e.key = 'a'))
  53. expect(fn).not.toBeCalled()
  54. triggerEvent(el, 'keyup', e => {
  55. e[`${keyName}Key`] = false
  56. e.key = 'esc'
  57. })
  58. expect(fn).not.toBeCalled()
  59. triggerEvent(el, 'keyup', e => {
  60. e[`${keyName}Key`] = true
  61. e.key = 'Escape'
  62. })
  63. expect(fn).toBeCalledTimes(1)
  64. triggerEvent(el, 'keyup', e => {
  65. e[`${keyName}Key`] = true
  66. e.key = 'ArrowLeft'
  67. })
  68. expect(fn).toBeCalledTimes(2)
  69. })
  70. })
  71. test('it should support "exact" modifier', () => {
  72. const el = document.createElement('div')
  73. // Case 1: <div @keyup.exact="test"/>
  74. const fn1 = vi.fn()
  75. const next1 = withModifiers(fn1, ['exact'])
  76. patchEvent(el, 'onKeyup', null, next1, null)
  77. triggerEvent(el, 'keyup')
  78. expect(fn1.mock.calls.length).toBe(1)
  79. triggerEvent(el, 'keyup', e => (e.ctrlKey = true))
  80. expect(fn1.mock.calls.length).toBe(1)
  81. // Case 2: <div @keyup.ctrl.a.exact="test"/>
  82. const fn2 = vi.fn()
  83. const next2 = withKeys(withModifiers(fn2, ['ctrl', 'exact']), ['a'])
  84. patchEvent(el, 'onKeyup', null, next2, null)
  85. triggerEvent(el, 'keyup', e => (e.key = 'a'))
  86. expect(fn2).not.toBeCalled()
  87. triggerEvent(el, 'keyup', e => {
  88. e.key = 'a'
  89. e.ctrlKey = true
  90. })
  91. expect(fn2.mock.calls.length).toBe(1)
  92. triggerEvent(el, 'keyup', e => {
  93. // should not trigger if has other system modifiers
  94. e.key = 'a'
  95. e.ctrlKey = true
  96. e.altKey = true
  97. })
  98. expect(fn2.mock.calls.length).toBe(1)
  99. })
  100. it('should support mouse modifiers', () => {
  101. const buttons = ['left', 'middle', 'right'] as const
  102. const buttonCodes = { left: 0, middle: 1, right: 2 }
  103. buttons.forEach(button => {
  104. const el = document.createElement('div')
  105. const fn = vi.fn()
  106. const handler = withModifiers(fn, [button])
  107. patchEvent(el, 'onMousedown', null, handler, null)
  108. buttons
  109. .filter(b => b !== button)
  110. .forEach(button => {
  111. triggerEvent(el, 'mousedown', e => (e.button = buttonCodes[button]))
  112. })
  113. expect(fn).not.toBeCalled()
  114. triggerEvent(el, 'mousedown', e => (e.button = buttonCodes[button]))
  115. expect(fn).toBeCalled()
  116. })
  117. })
  118. it('should handle multiple arguments when using modifiers', () => {
  119. const el = document.createElement('div')
  120. const fn = vi.fn()
  121. const handler = withModifiers(fn, ['ctrl'])
  122. const event = triggerEvent(el, 'click', e => (e.ctrlKey = true))
  123. handler(event, 'value', true)
  124. expect(fn).toBeCalledWith(event, 'value', true)
  125. })
  126. it('withKeys cache wrapped listener separately for different modifiers', () => {
  127. const el1 = document.createElement('button')
  128. const el2 = document.createElement('button')
  129. const fn = vi.fn()
  130. const handler1 = withKeys(fn, ['a'])
  131. const handler2 = withKeys(fn, ['b'])
  132. expect(handler1 === handler2).toBe(false)
  133. patchEvent(el1, 'onKeyup', null, handler1, null)
  134. patchEvent(el2, 'onKeyup', null, handler2, null)
  135. triggerEvent(el1, 'keyup', e => (e.key = 'a'))
  136. triggerEvent(el2, 'keyup', e => (e.key = 'b'))
  137. expect(fn).toBeCalledTimes(2)
  138. })
  139. it('withModifiers cache wrapped listener separately for different modifiers', () => {
  140. const el1 = document.createElement('button')
  141. const el2 = document.createElement('button')
  142. const fn = vi.fn()
  143. const handler1 = withModifiers(fn, ['ctrl'])
  144. const handler2 = withModifiers(fn, ['shift'])
  145. expect(handler1 === handler2).toBe(false)
  146. patchEvent(el1, 'onClick', null, handler1, null)
  147. patchEvent(el2, 'onClick', null, handler2, null)
  148. triggerEvent(el1, 'click', e => (e.ctrlKey = true))
  149. triggerEvent(el2, 'click', e => (e.shiftKey = true))
  150. expect(fn).toBeCalledTimes(2)
  151. })
  152. })