componentEmits.spec.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Note: emits and listener fallthrough is tested in
  2. // ./rendererAttrsFallthrough.spec.ts.
  3. import { render, defineComponent, h, nodeOps } from '@vue/runtime-test'
  4. import { isEmitListener } from '../src/componentEmits'
  5. describe('component: emit', () => {
  6. test('trigger handlers', () => {
  7. const Foo = defineComponent({
  8. render() {},
  9. created() {
  10. // the `emit` function is bound on component instances
  11. this.$emit('foo')
  12. this.$emit('bar')
  13. this.$emit('!baz')
  14. }
  15. })
  16. const onfoo = jest.fn()
  17. const onBar = jest.fn()
  18. const onBaz = jest.fn()
  19. const Comp = () => h(Foo, { onfoo, onBar, ['on!baz']: onBaz })
  20. render(h(Comp), nodeOps.createElement('div'))
  21. expect(onfoo).not.toHaveBeenCalled()
  22. // only capitalized or special chars are considered event listeners
  23. expect(onBar).toHaveBeenCalled()
  24. expect(onBaz).toHaveBeenCalled()
  25. })
  26. // for v-model:foo-bar usage in DOM templates
  27. test('trigger hyphenated events for update:xxx events', () => {
  28. const Foo = defineComponent({
  29. render() {},
  30. created() {
  31. this.$emit('update:fooProp')
  32. this.$emit('update:barProp')
  33. }
  34. })
  35. const fooSpy = jest.fn()
  36. const barSpy = jest.fn()
  37. const Comp = () =>
  38. h(Foo, {
  39. 'onUpdate:fooProp': fooSpy,
  40. 'onUpdate:bar-prop': barSpy
  41. })
  42. render(h(Comp), nodeOps.createElement('div'))
  43. expect(fooSpy).toHaveBeenCalled()
  44. expect(barSpy).toHaveBeenCalled()
  45. })
  46. test('should trigger array of listeners', async () => {
  47. const Child = defineComponent({
  48. setup(_, { emit }) {
  49. emit('foo', 1)
  50. return () => h('div')
  51. }
  52. })
  53. const fn1 = jest.fn()
  54. const fn2 = jest.fn()
  55. const App = {
  56. setup() {
  57. return () =>
  58. h(Child, {
  59. onFoo: [fn1, fn2]
  60. })
  61. }
  62. }
  63. render(h(App), nodeOps.createElement('div'))
  64. expect(fn1).toHaveBeenCalledTimes(1)
  65. expect(fn1).toHaveBeenCalledWith(1)
  66. expect(fn2).toHaveBeenCalledTimes(1)
  67. expect(fn1).toHaveBeenCalledWith(1)
  68. })
  69. test('warning for undeclared event (array)', () => {
  70. const Foo = defineComponent({
  71. emits: ['foo'],
  72. render() {},
  73. created() {
  74. // @ts-ignore
  75. this.$emit('bar')
  76. }
  77. })
  78. render(h(Foo), nodeOps.createElement('div'))
  79. expect(
  80. `Component emitted event "bar" but it is neither declared`
  81. ).toHaveBeenWarned()
  82. })
  83. test('warning for undeclared event (object)', () => {
  84. const Foo = defineComponent({
  85. emits: {
  86. foo: null
  87. },
  88. render() {},
  89. created() {
  90. // @ts-ignore
  91. this.$emit('bar')
  92. }
  93. })
  94. render(h(Foo), nodeOps.createElement('div'))
  95. expect(
  96. `Component emitted event "bar" but it is neither declared`
  97. ).toHaveBeenWarned()
  98. })
  99. test('should not warn if has equivalent onXXX prop', () => {
  100. const Foo = defineComponent({
  101. props: ['onFoo'],
  102. emits: [],
  103. render() {},
  104. created() {
  105. // @ts-ignore
  106. this.$emit('foo')
  107. }
  108. })
  109. render(h(Foo), nodeOps.createElement('div'))
  110. expect(
  111. `Component emitted event "bar" but it is neither declared`
  112. ).not.toHaveBeenWarned()
  113. })
  114. test('validator warning', () => {
  115. const Foo = defineComponent({
  116. emits: {
  117. foo: (arg: number) => arg > 0
  118. },
  119. render() {},
  120. created() {
  121. this.$emit('foo', -1)
  122. }
  123. })
  124. render(h(Foo), nodeOps.createElement('div'))
  125. expect(`event validation failed for event "foo"`).toHaveBeenWarned()
  126. })
  127. test('merging from mixins', () => {
  128. const mixin = {
  129. emits: {
  130. foo: (arg: number) => arg > 0
  131. }
  132. }
  133. const Foo = defineComponent({
  134. mixins: [mixin],
  135. render() {},
  136. created() {
  137. this.$emit('foo', -1)
  138. }
  139. })
  140. render(h(Foo), nodeOps.createElement('div'))
  141. expect(`event validation failed for event "foo"`).toHaveBeenWarned()
  142. })
  143. test('.once', () => {
  144. const Foo = defineComponent({
  145. render() {},
  146. emits: {
  147. foo: null
  148. },
  149. created() {
  150. this.$emit('foo')
  151. this.$emit('foo')
  152. }
  153. })
  154. const fn = jest.fn()
  155. render(
  156. h(Foo, {
  157. onFooOnce: fn
  158. }),
  159. nodeOps.createElement('div')
  160. )
  161. expect(fn).toHaveBeenCalledTimes(1)
  162. })
  163. test('isEmitListener', () => {
  164. const options = { click: null }
  165. expect(isEmitListener(options, 'onClick')).toBe(true)
  166. expect(isEmitListener(options, 'onclick')).toBe(false)
  167. expect(isEmitListener(options, 'onBlick')).toBe(false)
  168. // .once listeners
  169. expect(isEmitListener(options, 'onClickOnce')).toBe(true)
  170. expect(isEmitListener(options, 'onclickOnce')).toBe(false)
  171. })
  172. })