vOn.spec.ts 4.3 KB

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