vOn.spec.ts 4.3 KB

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