on.spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. import Vue from 'vue'
  2. describe('Directive v-on', () => {
  3. let vm, spy, spy2, el
  4. beforeEach(() => {
  5. spy = jasmine.createSpy()
  6. spy2 = jasmine.createSpy()
  7. el = document.createElement('div')
  8. document.body.appendChild(el)
  9. })
  10. afterEach(() => {
  11. document.body.removeChild(vm.$el)
  12. })
  13. it('should bind event to a method', () => {
  14. vm = new Vue({
  15. el,
  16. template: '<div v-on:click="foo"></div>',
  17. methods: { foo: spy }
  18. })
  19. triggerEvent(vm.$el, 'click')
  20. expect(spy.calls.count()).toBe(1)
  21. const args = spy.calls.allArgs()
  22. const event = args[0] && args[0][0] || {}
  23. expect(event.type).toBe('click')
  24. })
  25. it('should bind event to a inline statement', () => {
  26. vm = new Vue({
  27. el,
  28. template: '<div v-on:click="foo(1,2,3,$event)"></div>',
  29. methods: { foo: spy }
  30. })
  31. triggerEvent(vm.$el, 'click')
  32. expect(spy.calls.count()).toBe(1)
  33. const args = spy.calls.allArgs()
  34. const firstArgs = args[0]
  35. expect(firstArgs.length).toBe(4)
  36. expect(firstArgs[0]).toBe(1)
  37. expect(firstArgs[1]).toBe(2)
  38. expect(firstArgs[2]).toBe(3)
  39. expect(firstArgs[3].type).toBe('click')
  40. })
  41. it('should support inline function expression', () => {
  42. const spy = jasmine.createSpy()
  43. vm = new Vue({
  44. el,
  45. template: `<div class="test" @click="function (e) { log(e.target.className) }"></div>`,
  46. methods: {
  47. log: spy
  48. }
  49. }).$mount()
  50. triggerEvent(vm.$el, 'click')
  51. expect(spy).toHaveBeenCalledWith('test')
  52. })
  53. it('should support shorthand', () => {
  54. vm = new Vue({
  55. el,
  56. template: '<a href="#test" @click.prevent="foo"></a>',
  57. methods: { foo: spy }
  58. })
  59. triggerEvent(vm.$el, 'click')
  60. expect(spy.calls.count()).toBe(1)
  61. })
  62. it('should support stop propagation', () => {
  63. vm = new Vue({
  64. el,
  65. template: `
  66. <div @click.stop="foo"></div>
  67. `,
  68. methods: { foo: spy }
  69. })
  70. const hash = window.location.hash
  71. triggerEvent(vm.$el, 'click')
  72. expect(window.location.hash).toBe(hash)
  73. })
  74. it('should support prevent default', () => {
  75. vm = new Vue({
  76. el,
  77. template: `
  78. <div @click="bar">
  79. <div @click.stop="foo"></div>
  80. </div>
  81. `,
  82. methods: { foo: spy, bar: spy2 }
  83. })
  84. triggerEvent(vm.$el.firstChild, 'click')
  85. expect(spy).toHaveBeenCalled()
  86. expect(spy2).not.toHaveBeenCalled()
  87. })
  88. it('should support capture', () => {
  89. const callOrder = []
  90. vm = new Vue({
  91. el,
  92. template: `
  93. <div @click.capture="foo">
  94. <div @click="bar"></div>
  95. </div>
  96. `,
  97. methods: {
  98. foo () { callOrder.push(1) },
  99. bar () { callOrder.push(2) }
  100. }
  101. })
  102. triggerEvent(vm.$el.firstChild, 'click')
  103. expect(callOrder.toString()).toBe('1,2')
  104. })
  105. it('should support once', () => {
  106. vm = new Vue({
  107. el,
  108. template: `
  109. <div @click.once="foo">
  110. </div>
  111. `,
  112. methods: { foo: spy }
  113. })
  114. triggerEvent(vm.$el, 'click')
  115. expect(spy.calls.count()).toBe(1)
  116. triggerEvent(vm.$el, 'click')
  117. expect(spy.calls.count()).toBe(1) // should no longer trigger
  118. })
  119. // #4655
  120. it('should handle .once on multiple elements properly', () => {
  121. vm = new Vue({
  122. el,
  123. template: `
  124. <div>
  125. <button ref="one" @click.once="foo">one</button>
  126. <button ref="two" @click.once="foo">two</button>
  127. </div>
  128. `,
  129. methods: { foo: spy }
  130. })
  131. triggerEvent(vm.$refs.one, 'click')
  132. expect(spy.calls.count()).toBe(1)
  133. triggerEvent(vm.$refs.one, 'click')
  134. expect(spy.calls.count()).toBe(1)
  135. triggerEvent(vm.$refs.two, 'click')
  136. expect(spy.calls.count()).toBe(2)
  137. triggerEvent(vm.$refs.one, 'click')
  138. triggerEvent(vm.$refs.two, 'click')
  139. expect(spy.calls.count()).toBe(2)
  140. })
  141. it('should support capture and once', () => {
  142. const callOrder = []
  143. vm = new Vue({
  144. el,
  145. template: `
  146. <div @click.capture.once="foo">
  147. <div @click="bar"></div>
  148. </div>
  149. `,
  150. methods: {
  151. foo () { callOrder.push(1) },
  152. bar () { callOrder.push(2) }
  153. }
  154. })
  155. triggerEvent(vm.$el.firstChild, 'click')
  156. expect(callOrder.toString()).toBe('1,2')
  157. triggerEvent(vm.$el.firstChild, 'click')
  158. expect(callOrder.toString()).toBe('1,2,2')
  159. })
  160. // #4846
  161. it('should support once and other modifiers', () => {
  162. vm = new Vue({
  163. el,
  164. template: `<div @click.once.self="foo"><span/></div>`,
  165. methods: { foo: spy }
  166. })
  167. triggerEvent(vm.$el.firstChild, 'click')
  168. expect(spy).not.toHaveBeenCalled()
  169. triggerEvent(vm.$el, 'click')
  170. expect(spy).toHaveBeenCalled()
  171. triggerEvent(vm.$el, 'click')
  172. expect(spy.calls.count()).toBe(1)
  173. })
  174. it('should support keyCode', () => {
  175. vm = new Vue({
  176. el,
  177. template: `<input @keyup.enter="foo">`,
  178. methods: { foo: spy }
  179. })
  180. triggerEvent(vm.$el, 'keyup', e => {
  181. e.keyCode = 13
  182. })
  183. expect(spy).toHaveBeenCalled()
  184. })
  185. it('should support number keyCode', () => {
  186. vm = new Vue({
  187. el,
  188. template: `<input @keyup.13="foo">`,
  189. methods: { foo: spy }
  190. })
  191. triggerEvent(vm.$el, 'keyup', e => {
  192. e.keyCode = 13
  193. })
  194. expect(spy).toHaveBeenCalled()
  195. })
  196. it('should support mouse modifier', () => {
  197. const left = 0
  198. const middle = 1
  199. const right = 2
  200. const spyLeft = jasmine.createSpy()
  201. const spyMiddle = jasmine.createSpy()
  202. const spyRight = jasmine.createSpy()
  203. vm = new Vue({
  204. el,
  205. template: `
  206. <div>
  207. <div ref="left" @mousedown.left="foo">left</div>
  208. <div ref="right" @mousedown.right="foo1">right</div>
  209. <div ref="middle" @mousedown.middle="foo2">right</div>
  210. </div>
  211. `,
  212. methods: {
  213. foo: spyLeft,
  214. foo1: spyRight,
  215. foo2: spyMiddle
  216. }
  217. })
  218. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = right })
  219. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = middle })
  220. expect(spyLeft).not.toHaveBeenCalled()
  221. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = left })
  222. expect(spyLeft).toHaveBeenCalled()
  223. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = left })
  224. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = middle })
  225. expect(spyRight).not.toHaveBeenCalled()
  226. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = right })
  227. expect(spyRight).toHaveBeenCalled()
  228. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = left })
  229. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = right })
  230. expect(spyMiddle).not.toHaveBeenCalled()
  231. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = middle })
  232. expect(spyMiddle).toHaveBeenCalled()
  233. })
  234. it('should support custom keyCode', () => {
  235. Vue.config.keyCodes.test = 1
  236. vm = new Vue({
  237. el,
  238. template: `<input @keyup.test="foo">`,
  239. methods: { foo: spy }
  240. })
  241. triggerEvent(vm.$el, 'keyup', e => {
  242. e.keyCode = 1
  243. })
  244. expect(spy).toHaveBeenCalled()
  245. })
  246. it('should override build-in keyCode', () => {
  247. Vue.config.keyCodes.up = [1, 87]
  248. vm = new Vue({
  249. el,
  250. template: `<input @keyup.up="foo" @keyup.down="foo">`,
  251. methods: { foo: spy }
  252. })
  253. triggerEvent(vm.$el, 'keyup', e => {
  254. e.keyCode = 87
  255. })
  256. expect(spy).toHaveBeenCalled()
  257. triggerEvent(vm.$el, 'keyup', e => {
  258. e.keyCode = 1
  259. })
  260. expect(spy).toHaveBeenCalledTimes(2)
  261. // should not affect build-in down keycode
  262. triggerEvent(vm.$el, 'keyup', e => {
  263. e.keyCode = 40
  264. })
  265. expect(spy).toHaveBeenCalledTimes(3)
  266. })
  267. it('should bind to a child component', () => {
  268. Vue.component('bar', {
  269. template: '<span>Hello</span>'
  270. })
  271. vm = new Vue({
  272. el,
  273. template: '<bar @custom="foo"></bar>',
  274. methods: { foo: spy }
  275. })
  276. vm.$children[0].$emit('custom', 'foo', 'bar')
  277. expect(spy).toHaveBeenCalledWith('foo', 'bar')
  278. })
  279. it('should be able to bind native events for a child component', () => {
  280. Vue.component('bar', {
  281. template: '<span>Hello</span>'
  282. })
  283. vm = new Vue({
  284. el,
  285. template: '<bar @click.native="foo"></bar>',
  286. methods: { foo: spy }
  287. })
  288. vm.$children[0].$emit('click')
  289. expect(spy).not.toHaveBeenCalled()
  290. triggerEvent(vm.$children[0].$el, 'click')
  291. expect(spy).toHaveBeenCalled()
  292. })
  293. it('.once modifier should work with child components', () => {
  294. Vue.component('bar', {
  295. template: '<span>Hello</span>'
  296. })
  297. vm = new Vue({
  298. el,
  299. template: '<bar @custom.once="foo"></bar>',
  300. methods: { foo: spy }
  301. })
  302. vm.$children[0].$emit('custom')
  303. expect(spy.calls.count()).toBe(1)
  304. vm.$children[0].$emit('custom')
  305. expect(spy.calls.count()).toBe(1) // should not be called again
  306. })
  307. it('remove listener', done => {
  308. const spy2 = jasmine.createSpy('remove listener')
  309. vm = new Vue({
  310. el,
  311. methods: { foo: spy, bar: spy2 },
  312. data: {
  313. ok: true
  314. },
  315. render (h) {
  316. return this.ok
  317. ? h('input', { on: { click: this.foo }})
  318. : h('input', { on: { input: this.bar }})
  319. }
  320. })
  321. triggerEvent(vm.$el, 'click')
  322. expect(spy.calls.count()).toBe(1)
  323. expect(spy2.calls.count()).toBe(0)
  324. vm.ok = false
  325. waitForUpdate(() => {
  326. triggerEvent(vm.$el, 'click')
  327. expect(spy.calls.count()).toBe(1) // should no longer trigger
  328. triggerEvent(vm.$el, 'input')
  329. expect(spy2.calls.count()).toBe(1)
  330. }).then(done)
  331. })
  332. it('remove capturing listener', done => {
  333. const spy2 = jasmine.createSpy('remove listener')
  334. vm = new Vue({
  335. el,
  336. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  337. data: {
  338. ok: true
  339. },
  340. render (h) {
  341. return this.ok
  342. ? h('div', { on: { '!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  343. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  344. }
  345. })
  346. triggerEvent(vm.$el.firstChild, 'click')
  347. expect(spy.calls.count()).toBe(1)
  348. expect(spy2.calls.count()).toBe(0)
  349. vm.ok = false
  350. waitForUpdate(() => {
  351. triggerEvent(vm.$el.firstChild, 'click')
  352. expect(spy.calls.count()).toBe(1) // should no longer trigger
  353. triggerEvent(vm.$el, 'mouseOver')
  354. expect(spy2.calls.count()).toBe(1)
  355. }).then(done)
  356. })
  357. it('remove once listener', done => {
  358. const spy2 = jasmine.createSpy('remove listener')
  359. vm = new Vue({
  360. el,
  361. methods: { foo: spy, bar: spy2 },
  362. data: {
  363. ok: true
  364. },
  365. render (h) {
  366. return this.ok
  367. ? h('input', { on: { '~click': this.foo }})
  368. : h('input', { on: { input: this.bar }})
  369. }
  370. })
  371. triggerEvent(vm.$el, 'click')
  372. expect(spy.calls.count()).toBe(1)
  373. triggerEvent(vm.$el, 'click')
  374. expect(spy.calls.count()).toBe(1) // should no longer trigger
  375. expect(spy2.calls.count()).toBe(0)
  376. vm.ok = false
  377. waitForUpdate(() => {
  378. triggerEvent(vm.$el, 'click')
  379. expect(spy.calls.count()).toBe(1) // should no longer trigger
  380. triggerEvent(vm.$el, 'input')
  381. expect(spy2.calls.count()).toBe(1)
  382. }).then(done)
  383. })
  384. it('remove capturing and once listener', done => {
  385. const spy2 = jasmine.createSpy('remove listener')
  386. vm = new Vue({
  387. el,
  388. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  389. data: {
  390. ok: true
  391. },
  392. render (h) {
  393. return this.ok
  394. ? h('div', { on: { '~!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  395. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  396. }
  397. })
  398. triggerEvent(vm.$el.firstChild, 'click')
  399. expect(spy.calls.count()).toBe(1)
  400. triggerEvent(vm.$el.firstChild, 'click')
  401. expect(spy.calls.count()).toBe(1) // should no longer trigger
  402. expect(spy2.calls.count()).toBe(0)
  403. vm.ok = false
  404. waitForUpdate(() => {
  405. triggerEvent(vm.$el.firstChild, 'click')
  406. expect(spy.calls.count()).toBe(1) // should no longer trigger
  407. triggerEvent(vm.$el, 'mouseOver')
  408. expect(spy2.calls.count()).toBe(1)
  409. }).then(done)
  410. })
  411. it('remove listener on child component', done => {
  412. const spy2 = jasmine.createSpy('remove listener')
  413. vm = new Vue({
  414. el,
  415. methods: { foo: spy, bar: spy2 },
  416. data: {
  417. ok: true
  418. },
  419. components: {
  420. test: {
  421. template: '<div></div>'
  422. }
  423. },
  424. render (h) {
  425. return this.ok
  426. ? h('test', { on: { foo: this.foo }})
  427. : h('test', { on: { bar: this.bar }})
  428. }
  429. })
  430. vm.$children[0].$emit('foo')
  431. expect(spy.calls.count()).toBe(1)
  432. expect(spy2.calls.count()).toBe(0)
  433. vm.ok = false
  434. waitForUpdate(() => {
  435. vm.$children[0].$emit('foo')
  436. expect(spy.calls.count()).toBe(1) // should no longer trigger
  437. vm.$children[0].$emit('bar')
  438. expect(spy2.calls.count()).toBe(1)
  439. }).then(done)
  440. })
  441. it('warn missing handlers', () => {
  442. vm = new Vue({
  443. el,
  444. data: { none: null },
  445. template: `<div @click="none"></div>`
  446. })
  447. expect(`Invalid handler for event "click": got null`).toHaveBeenWarned()
  448. expect(() => {
  449. triggerEvent(vm.$el, 'click')
  450. }).not.toThrow()
  451. })
  452. })