on.spec.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. import Vue from 'vue'
  2. import { supportsPassive } from 'core/util/env'
  3. describe('Directive v-on', () => {
  4. let vm, spy, el
  5. beforeEach(() => {
  6. vm = null
  7. spy = jasmine.createSpy()
  8. el = document.createElement('div')
  9. document.body.appendChild(el)
  10. })
  11. afterEach(() => {
  12. if (vm) {
  13. document.body.removeChild(vm.$el)
  14. }
  15. })
  16. it('should bind event to a method', () => {
  17. vm = new Vue({
  18. el,
  19. template: '<div v-on:click="foo"></div>',
  20. methods: { foo: spy }
  21. })
  22. triggerEvent(vm.$el, 'click')
  23. expect(spy.calls.count()).toBe(1)
  24. const args = spy.calls.allArgs()
  25. const event = args[0] && args[0][0] || {}
  26. expect(event.type).toBe('click')
  27. })
  28. it('should bind event to a inline statement', () => {
  29. vm = new Vue({
  30. el,
  31. template: '<div v-on:click="foo(1,2,3,$event)"></div>',
  32. methods: { foo: spy }
  33. })
  34. triggerEvent(vm.$el, 'click')
  35. expect(spy.calls.count()).toBe(1)
  36. const args = spy.calls.allArgs()
  37. const firstArgs = args[0]
  38. expect(firstArgs.length).toBe(4)
  39. expect(firstArgs[0]).toBe(1)
  40. expect(firstArgs[1]).toBe(2)
  41. expect(firstArgs[2]).toBe(3)
  42. expect(firstArgs[3].type).toBe('click')
  43. })
  44. it('should support inline function expression', () => {
  45. const spy = jasmine.createSpy()
  46. vm = new Vue({
  47. el,
  48. template: `<div class="test" @click="function (e) { log(e.target.className) }"></div>`,
  49. methods: {
  50. log: spy
  51. }
  52. }).$mount()
  53. triggerEvent(vm.$el, 'click')
  54. expect(spy).toHaveBeenCalledWith('test')
  55. })
  56. it('should support shorthand', () => {
  57. vm = new Vue({
  58. el,
  59. template: '<a href="#test" @click.prevent="foo"></a>',
  60. methods: { foo: spy }
  61. })
  62. triggerEvent(vm.$el, 'click')
  63. expect(spy.calls.count()).toBe(1)
  64. })
  65. it('should support stop propagation', () => {
  66. vm = new Vue({
  67. el,
  68. template: `
  69. <div @click.stop="foo"></div>
  70. `,
  71. methods: { foo: spy }
  72. })
  73. const hash = window.location.hash
  74. triggerEvent(vm.$el, 'click')
  75. expect(window.location.hash).toBe(hash)
  76. })
  77. it('should support prevent default', () => {
  78. vm = new Vue({
  79. el,
  80. template: `
  81. <input type="checkbox" ref="input" @click.prevent="foo">
  82. `,
  83. methods: {
  84. foo ($event) {
  85. spy($event.defaultPrevented)
  86. }
  87. }
  88. })
  89. vm.$refs.input.checked = false
  90. triggerEvent(vm.$refs.input, 'click')
  91. expect(spy).toHaveBeenCalledWith(true)
  92. })
  93. it('should support capture', () => {
  94. const callOrder = []
  95. vm = new Vue({
  96. el,
  97. template: `
  98. <div @click.capture="foo">
  99. <div @click="bar"></div>
  100. </div>
  101. `,
  102. methods: {
  103. foo () { callOrder.push(1) },
  104. bar () { callOrder.push(2) }
  105. }
  106. })
  107. triggerEvent(vm.$el.firstChild, 'click')
  108. expect(callOrder.toString()).toBe('1,2')
  109. })
  110. it('should support once', () => {
  111. vm = new Vue({
  112. el,
  113. template: `
  114. <div @click.once="foo">
  115. </div>
  116. `,
  117. methods: { foo: spy }
  118. })
  119. triggerEvent(vm.$el, 'click')
  120. expect(spy.calls.count()).toBe(1)
  121. triggerEvent(vm.$el, 'click')
  122. expect(spy.calls.count()).toBe(1) // should no longer trigger
  123. })
  124. // #4655
  125. it('should handle .once on multiple elements properly', () => {
  126. vm = new Vue({
  127. el,
  128. template: `
  129. <div>
  130. <button ref="one" @click.once="foo">one</button>
  131. <button ref="two" @click.once="foo">two</button>
  132. </div>
  133. `,
  134. methods: { foo: spy }
  135. })
  136. triggerEvent(vm.$refs.one, 'click')
  137. expect(spy.calls.count()).toBe(1)
  138. triggerEvent(vm.$refs.one, 'click')
  139. expect(spy.calls.count()).toBe(1)
  140. triggerEvent(vm.$refs.two, 'click')
  141. expect(spy.calls.count()).toBe(2)
  142. triggerEvent(vm.$refs.one, 'click')
  143. triggerEvent(vm.$refs.two, 'click')
  144. expect(spy.calls.count()).toBe(2)
  145. })
  146. it('should support capture and once', () => {
  147. const callOrder = []
  148. vm = new Vue({
  149. el,
  150. template: `
  151. <div @click.capture.once="foo">
  152. <div @click="bar"></div>
  153. </div>
  154. `,
  155. methods: {
  156. foo () { callOrder.push(1) },
  157. bar () { callOrder.push(2) }
  158. }
  159. })
  160. triggerEvent(vm.$el.firstChild, 'click')
  161. expect(callOrder.toString()).toBe('1,2')
  162. triggerEvent(vm.$el.firstChild, 'click')
  163. expect(callOrder.toString()).toBe('1,2,2')
  164. })
  165. // #4846
  166. it('should support once and other modifiers', () => {
  167. vm = new Vue({
  168. el,
  169. template: `<div @click.once.self="foo"><span/></div>`,
  170. methods: { foo: spy }
  171. })
  172. triggerEvent(vm.$el.firstChild, 'click')
  173. expect(spy).not.toHaveBeenCalled()
  174. triggerEvent(vm.$el, 'click')
  175. expect(spy).toHaveBeenCalled()
  176. triggerEvent(vm.$el, 'click')
  177. expect(spy.calls.count()).toBe(1)
  178. })
  179. it('should support keyCode', () => {
  180. vm = new Vue({
  181. el,
  182. template: `<input @keyup.enter="foo">`,
  183. methods: { foo: spy }
  184. })
  185. triggerEvent(vm.$el, 'keyup', e => {
  186. e.keyCode = 13
  187. })
  188. expect(spy).toHaveBeenCalled()
  189. })
  190. it('should support automatic key name inference', () => {
  191. vm = new Vue({
  192. el,
  193. template: `<input @keyup.arrow-right="foo">`,
  194. methods: { foo: spy }
  195. })
  196. triggerEvent(vm.$el, 'keyup', e => {
  197. e.key = 'ArrowRight'
  198. })
  199. expect(spy).toHaveBeenCalled()
  200. })
  201. // ctrl, shift, alt, meta
  202. it('should support system modifers', () => {
  203. vm = new Vue({
  204. el,
  205. template: `
  206. <div>
  207. <input ref="ctrl" @keyup.ctrl="foo">
  208. <input ref="shift" @keyup.shift="foo">
  209. <input ref="alt" @keyup.alt="foo">
  210. <input ref="meta" @keyup.meta="foo">
  211. </div>
  212. `,
  213. methods: { foo: spy }
  214. })
  215. triggerEvent(vm.$refs.ctrl, 'keyup')
  216. expect(spy.calls.count()).toBe(0)
  217. triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true })
  218. expect(spy.calls.count()).toBe(1)
  219. triggerEvent(vm.$refs.shift, 'keyup')
  220. expect(spy.calls.count()).toBe(1)
  221. triggerEvent(vm.$refs.shift, 'keyup', e => { e.shiftKey = true })
  222. expect(spy.calls.count()).toBe(2)
  223. triggerEvent(vm.$refs.alt, 'keyup')
  224. expect(spy.calls.count()).toBe(2)
  225. triggerEvent(vm.$refs.alt, 'keyup', e => { e.altKey = true })
  226. expect(spy.calls.count()).toBe(3)
  227. triggerEvent(vm.$refs.meta, 'keyup')
  228. expect(spy.calls.count()).toBe(3)
  229. triggerEvent(vm.$refs.meta, 'keyup', e => { e.metaKey = true })
  230. expect(spy.calls.count()).toBe(4)
  231. })
  232. it('should support exact modifier', () => {
  233. vm = new Vue({
  234. el,
  235. template: `
  236. <div>
  237. <input ref="ctrl" @keyup.exact="foo">
  238. </div>
  239. `,
  240. methods: { foo: spy }
  241. })
  242. triggerEvent(vm.$refs.ctrl, 'keyup')
  243. expect(spy.calls.count()).toBe(1)
  244. triggerEvent(vm.$refs.ctrl, 'keyup', e => {
  245. e.ctrlKey = true
  246. })
  247. expect(spy.calls.count()).toBe(1)
  248. // should not trigger if has other system modifiers
  249. triggerEvent(vm.$refs.ctrl, 'keyup', e => {
  250. e.ctrlKey = true
  251. e.altKey = true
  252. })
  253. expect(spy.calls.count()).toBe(1)
  254. })
  255. it('should support system modifiers with exact', () => {
  256. vm = new Vue({
  257. el,
  258. template: `
  259. <div>
  260. <input ref="ctrl" @keyup.ctrl.exact="foo">
  261. </div>
  262. `,
  263. methods: { foo: spy }
  264. })
  265. triggerEvent(vm.$refs.ctrl, 'keyup')
  266. expect(spy.calls.count()).toBe(0)
  267. triggerEvent(vm.$refs.ctrl, 'keyup', e => {
  268. e.ctrlKey = true
  269. })
  270. expect(spy.calls.count()).toBe(1)
  271. // should not trigger if has other system modifiers
  272. triggerEvent(vm.$refs.ctrl, 'keyup', e => {
  273. e.ctrlKey = true
  274. e.altKey = true
  275. })
  276. expect(spy.calls.count()).toBe(1)
  277. })
  278. it('should support number keyCode', () => {
  279. vm = new Vue({
  280. el,
  281. template: `<input @keyup.13="foo">`,
  282. methods: { foo: spy }
  283. })
  284. triggerEvent(vm.$el, 'keyup', e => {
  285. e.keyCode = 13
  286. })
  287. expect(spy).toHaveBeenCalled()
  288. })
  289. it('should support mouse modifier', () => {
  290. const left = 0
  291. const middle = 1
  292. const right = 2
  293. const spyLeft = jasmine.createSpy()
  294. const spyMiddle = jasmine.createSpy()
  295. const spyRight = jasmine.createSpy()
  296. vm = new Vue({
  297. el,
  298. template: `
  299. <div>
  300. <div ref="left" @mousedown.left="foo">left</div>
  301. <div ref="right" @mousedown.right="foo1">right</div>
  302. <div ref="middle" @mousedown.middle="foo2">right</div>
  303. </div>
  304. `,
  305. methods: {
  306. foo: spyLeft,
  307. foo1: spyRight,
  308. foo2: spyMiddle
  309. }
  310. })
  311. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = right })
  312. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = middle })
  313. expect(spyLeft).not.toHaveBeenCalled()
  314. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = left })
  315. expect(spyLeft).toHaveBeenCalled()
  316. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = left })
  317. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = middle })
  318. expect(spyRight).not.toHaveBeenCalled()
  319. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = right })
  320. expect(spyRight).toHaveBeenCalled()
  321. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = left })
  322. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = right })
  323. expect(spyMiddle).not.toHaveBeenCalled()
  324. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = middle })
  325. expect(spyMiddle).toHaveBeenCalled()
  326. })
  327. it('should support KeyboardEvent.key for built in aliases', () => {
  328. vm = new Vue({
  329. el,
  330. template: `
  331. <div>
  332. <input ref="enter" @keyup.enter="foo">
  333. <input ref="space" @keyup.space="foo">
  334. <input ref="esc" @keyup.esc="foo">
  335. <input ref="left" @keyup.left="foo">
  336. <input ref="delete" @keyup.delete="foo">
  337. </div>
  338. `,
  339. methods: { foo: spy }
  340. })
  341. triggerEvent(vm.$refs.enter, 'keyup', e => { e.key = 'Enter' })
  342. expect(spy.calls.count()).toBe(1)
  343. triggerEvent(vm.$refs.space, 'keyup', e => { e.key = ' ' })
  344. expect(spy.calls.count()).toBe(2)
  345. triggerEvent(vm.$refs.esc, 'keyup', e => { e.key = 'Escape' })
  346. expect(spy.calls.count()).toBe(3)
  347. triggerEvent(vm.$refs.left, 'keyup', e => { e.key = 'ArrowLeft' })
  348. expect(spy.calls.count()).toBe(4)
  349. triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Backspace' })
  350. expect(spy.calls.count()).toBe(5)
  351. triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Delete' })
  352. expect(spy.calls.count()).toBe(6)
  353. })
  354. it('should support custom keyCode', () => {
  355. Vue.config.keyCodes.test = 1
  356. vm = new Vue({
  357. el,
  358. template: `<input @keyup.test="foo">`,
  359. methods: { foo: spy }
  360. })
  361. triggerEvent(vm.$el, 'keyup', e => {
  362. e.keyCode = 1
  363. })
  364. expect(spy).toHaveBeenCalled()
  365. Vue.config.keyCodes = Object.create(null)
  366. })
  367. it('should override built-in keyCode', () => {
  368. Vue.config.keyCodes.up = [1, 87]
  369. vm = new Vue({
  370. el,
  371. template: `<input @keyup.up="foo" @keyup.down="foo">`,
  372. methods: { foo: spy }
  373. })
  374. triggerEvent(vm.$el, 'keyup', e => {
  375. e.keyCode = 87
  376. })
  377. expect(spy).toHaveBeenCalled()
  378. triggerEvent(vm.$el, 'keyup', e => {
  379. e.keyCode = 1
  380. })
  381. expect(spy).toHaveBeenCalledTimes(2)
  382. // should not affect built-in down keycode
  383. triggerEvent(vm.$el, 'keyup', e => {
  384. e.keyCode = 40
  385. })
  386. expect(spy).toHaveBeenCalledTimes(3)
  387. Vue.config.keyCodes = Object.create(null)
  388. })
  389. it('should bind to a child component', () => {
  390. vm = new Vue({
  391. el,
  392. template: '<bar @custom="foo"></bar>',
  393. methods: { foo: spy },
  394. components: {
  395. bar: {
  396. template: '<span>Hello</span>'
  397. }
  398. }
  399. })
  400. vm.$children[0].$emit('custom', 'foo', 'bar')
  401. expect(spy).toHaveBeenCalledWith('foo', 'bar')
  402. })
  403. it('should be able to bind native events for a child component', () => {
  404. vm = new Vue({
  405. el,
  406. template: '<bar @click.native="foo"></bar>',
  407. methods: { foo: spy },
  408. components: {
  409. bar: {
  410. template: '<span>Hello</span>'
  411. }
  412. }
  413. })
  414. vm.$children[0].$emit('click')
  415. expect(spy).not.toHaveBeenCalled()
  416. triggerEvent(vm.$children[0].$el, 'click')
  417. expect(spy).toHaveBeenCalled()
  418. })
  419. it('should throw a warning if native modifier is used on native HTML element', () => {
  420. vm = new Vue({
  421. el,
  422. template: `
  423. <button @click.native="foo"></button>
  424. `,
  425. methods: { foo: spy },
  426. })
  427. triggerEvent(vm.$el, 'click')
  428. expect(`The .native modifier for v-on is only valid on components but it was used on <button>.`).toHaveBeenWarned()
  429. expect(spy.calls.count()).toBe(0)
  430. })
  431. it('.once modifier should work with child components', () => {
  432. vm = new Vue({
  433. el,
  434. template: '<bar @custom.once="foo"></bar>',
  435. methods: { foo: spy },
  436. components: {
  437. bar: {
  438. template: '<span>Hello</span>'
  439. }
  440. }
  441. })
  442. vm.$children[0].$emit('custom')
  443. expect(spy.calls.count()).toBe(1)
  444. vm.$children[0].$emit('custom')
  445. expect(spy.calls.count()).toBe(1) // should not be called again
  446. })
  447. it('remove listener', done => {
  448. const spy2 = jasmine.createSpy('remove listener')
  449. vm = new Vue({
  450. el,
  451. methods: { foo: spy, bar: spy2 },
  452. data: {
  453. ok: true
  454. },
  455. render (h) {
  456. return this.ok
  457. ? h('input', { on: { click: this.foo }})
  458. : h('input', { on: { input: this.bar }})
  459. }
  460. })
  461. triggerEvent(vm.$el, 'click')
  462. expect(spy.calls.count()).toBe(1)
  463. expect(spy2.calls.count()).toBe(0)
  464. vm.ok = false
  465. waitForUpdate(() => {
  466. triggerEvent(vm.$el, 'click')
  467. expect(spy.calls.count()).toBe(1) // should no longer trigger
  468. triggerEvent(vm.$el, 'input')
  469. expect(spy2.calls.count()).toBe(1)
  470. }).then(done)
  471. })
  472. it('remove capturing listener', done => {
  473. const spy2 = jasmine.createSpy('remove listener')
  474. vm = new Vue({
  475. el,
  476. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  477. data: {
  478. ok: true
  479. },
  480. render (h) {
  481. return this.ok
  482. ? h('div', { on: { '!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  483. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  484. }
  485. })
  486. triggerEvent(vm.$el.firstChild, 'click')
  487. expect(spy.calls.count()).toBe(1)
  488. expect(spy2.calls.count()).toBe(0)
  489. vm.ok = false
  490. waitForUpdate(() => {
  491. triggerEvent(vm.$el.firstChild, 'click')
  492. expect(spy.calls.count()).toBe(1) // should no longer trigger
  493. triggerEvent(vm.$el, 'mouseOver')
  494. expect(spy2.calls.count()).toBe(1)
  495. }).then(done)
  496. })
  497. it('remove once listener', done => {
  498. const spy2 = jasmine.createSpy('remove listener')
  499. vm = new Vue({
  500. el,
  501. methods: { foo: spy, bar: spy2 },
  502. data: {
  503. ok: true
  504. },
  505. render (h) {
  506. return this.ok
  507. ? h('input', { on: { '~click': this.foo }})
  508. : h('input', { on: { input: this.bar }})
  509. }
  510. })
  511. triggerEvent(vm.$el, 'click')
  512. expect(spy.calls.count()).toBe(1)
  513. triggerEvent(vm.$el, 'click')
  514. expect(spy.calls.count()).toBe(1) // should no longer trigger
  515. expect(spy2.calls.count()).toBe(0)
  516. vm.ok = false
  517. waitForUpdate(() => {
  518. triggerEvent(vm.$el, 'click')
  519. expect(spy.calls.count()).toBe(1) // should no longer trigger
  520. triggerEvent(vm.$el, 'input')
  521. expect(spy2.calls.count()).toBe(1)
  522. }).then(done)
  523. })
  524. it('remove capturing and once listener', done => {
  525. const spy2 = jasmine.createSpy('remove listener')
  526. vm = new Vue({
  527. el,
  528. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  529. data: {
  530. ok: true
  531. },
  532. render (h) {
  533. return this.ok
  534. ? h('div', { on: { '~!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  535. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  536. }
  537. })
  538. triggerEvent(vm.$el.firstChild, 'click')
  539. expect(spy.calls.count()).toBe(1)
  540. triggerEvent(vm.$el.firstChild, 'click')
  541. expect(spy.calls.count()).toBe(1) // should no longer trigger
  542. expect(spy2.calls.count()).toBe(0)
  543. vm.ok = false
  544. waitForUpdate(() => {
  545. triggerEvent(vm.$el.firstChild, 'click')
  546. expect(spy.calls.count()).toBe(1) // should no longer trigger
  547. triggerEvent(vm.$el, 'mouseOver')
  548. expect(spy2.calls.count()).toBe(1)
  549. }).then(done)
  550. })
  551. it('remove listener on child component', done => {
  552. const spy2 = jasmine.createSpy('remove listener')
  553. vm = new Vue({
  554. el,
  555. methods: { foo: spy, bar: spy2 },
  556. data: {
  557. ok: true
  558. },
  559. components: {
  560. test: {
  561. template: '<div></div>'
  562. }
  563. },
  564. render (h) {
  565. return this.ok
  566. ? h('test', { on: { foo: this.foo }})
  567. : h('test', { on: { bar: this.bar }})
  568. }
  569. })
  570. vm.$children[0].$emit('foo')
  571. expect(spy.calls.count()).toBe(1)
  572. expect(spy2.calls.count()).toBe(0)
  573. vm.ok = false
  574. waitForUpdate(() => {
  575. vm.$children[0].$emit('foo')
  576. expect(spy.calls.count()).toBe(1) // should no longer trigger
  577. vm.$children[0].$emit('bar')
  578. expect(spy2.calls.count()).toBe(1)
  579. }).then(done)
  580. })
  581. it('warn missing handlers', () => {
  582. vm = new Vue({
  583. el,
  584. data: { none: null },
  585. template: `<div @click="none"></div>`
  586. })
  587. expect(`Invalid handler for event "click": got null`).toHaveBeenWarned()
  588. expect(() => {
  589. triggerEvent(vm.$el, 'click')
  590. }).not.toThrow()
  591. })
  592. // Github Issue #5046
  593. it('should support keyboard modifier for direction keys', () => {
  594. const spyLeft = jasmine.createSpy()
  595. const spyRight = jasmine.createSpy()
  596. const spyUp = jasmine.createSpy()
  597. const spyDown = jasmine.createSpy()
  598. vm = new Vue({
  599. el,
  600. template: `
  601. <div>
  602. <input ref="left" @keydown.left="foo"></input>
  603. <input ref="right" @keydown.right="foo1"></input>
  604. <input ref="up" @keydown.up="foo2"></input>
  605. <input ref="down" @keydown.down="foo3"></input>
  606. </div>
  607. `,
  608. methods: {
  609. foo: spyLeft,
  610. foo1: spyRight,
  611. foo2: spyUp,
  612. foo3: spyDown
  613. }
  614. })
  615. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 37 })
  616. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 39 })
  617. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 39 })
  618. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 38 })
  619. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 38 })
  620. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 37 })
  621. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 40 })
  622. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 39 })
  623. expect(spyLeft.calls.count()).toBe(1)
  624. expect(spyRight.calls.count()).toBe(1)
  625. expect(spyUp.calls.count()).toBe(1)
  626. expect(spyDown.calls.count()).toBe(1)
  627. })
  628. // This test case should only run when the test browser supports passive.
  629. if (supportsPassive) {
  630. it('should support passive', () => {
  631. vm = new Vue({
  632. el,
  633. template: `
  634. <div>
  635. <input type="checkbox" ref="normal" @click="foo"/>
  636. <input type="checkbox" ref="passive" @click.passive="foo"/>
  637. <input type="checkbox" ref="exclusive" @click.prevent.passive/>
  638. </div>
  639. `,
  640. methods: {
  641. foo (e) {
  642. e.preventDefault()
  643. }
  644. }
  645. })
  646. vm.$refs.normal.checked = false
  647. vm.$refs.passive.checked = false
  648. vm.$refs.exclusive.checked = false
  649. vm.$refs.normal.click()
  650. vm.$refs.passive.click()
  651. vm.$refs.exclusive.click()
  652. expect(vm.$refs.normal.checked).toBe(false)
  653. expect(vm.$refs.passive.checked).toBe(true)
  654. expect(vm.$refs.exclusive.checked).toBe(true)
  655. expect('passive and prevent can\'t be used together. Passive handler can\'t prevent default event.').toHaveBeenWarned()
  656. })
  657. }
  658. // GitHub Issues #5146
  659. it('should only prevent when match keycode', () => {
  660. let prevented = false
  661. vm = new Vue({
  662. el,
  663. template: `
  664. <input ref="input" @keydown.enter.prevent="foo">
  665. `,
  666. methods: {
  667. foo ($event) {
  668. prevented = $event.defaultPrevented
  669. }
  670. }
  671. })
  672. triggerEvent(vm.$refs.input, 'keydown', e => { e.keyCode = 32 })
  673. expect(prevented).toBe(false)
  674. triggerEvent(vm.$refs.input, 'keydown', e => { e.keyCode = 13 })
  675. expect(prevented).toBe(true)
  676. })
  677. it('should transform click.right to contextmenu', () => {
  678. const spy = jasmine.createSpy('click.right')
  679. const vm = new Vue({
  680. template: `<div @click.right="foo"></div>`,
  681. methods: { foo: spy }
  682. }).$mount()
  683. triggerEvent(vm.$el, 'contextmenu')
  684. expect(spy).toHaveBeenCalled()
  685. })
  686. it('should transform click.middle to mouseup', () => {
  687. const spy = jasmine.createSpy('click.middle')
  688. vm = new Vue({
  689. el,
  690. template: `<div @click.middle="foo"></div>`,
  691. methods: { foo: spy }
  692. })
  693. triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 })
  694. expect(spy).not.toHaveBeenCalled()
  695. triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 })
  696. expect(spy).toHaveBeenCalled()
  697. })
  698. it('object syntax (no argument)', () => {
  699. const click = jasmine.createSpy('click')
  700. const mouseup = jasmine.createSpy('mouseup')
  701. vm = new Vue({
  702. el,
  703. template: `<button v-on="listeners">foo</button>`,
  704. created () {
  705. this.listeners = {
  706. click,
  707. mouseup
  708. }
  709. }
  710. })
  711. triggerEvent(vm.$el, 'click')
  712. expect(click.calls.count()).toBe(1)
  713. expect(mouseup.calls.count()).toBe(0)
  714. triggerEvent(vm.$el, 'mouseup')
  715. expect(click.calls.count()).toBe(1)
  716. expect(mouseup.calls.count()).toBe(1)
  717. })
  718. it('object syntax (no argument, mixed with normal listeners)', () => {
  719. const click1 = jasmine.createSpy('click1')
  720. const click2 = jasmine.createSpy('click2')
  721. const mouseup = jasmine.createSpy('mouseup')
  722. vm = new Vue({
  723. el,
  724. template: `<button v-on="listeners" @click="click2">foo</button>`,
  725. created () {
  726. this.listeners = {
  727. click: click1,
  728. mouseup
  729. }
  730. },
  731. methods: {
  732. click2
  733. }
  734. })
  735. triggerEvent(vm.$el, 'click')
  736. expect(click1.calls.count()).toBe(1)
  737. expect(click2.calls.count()).toBe(1)
  738. expect(mouseup.calls.count()).toBe(0)
  739. triggerEvent(vm.$el, 'mouseup')
  740. expect(click1.calls.count()).toBe(1)
  741. expect(click2.calls.count()).toBe(1)
  742. expect(mouseup.calls.count()).toBe(1)
  743. })
  744. it('object syntax (usage in HOC, mixed with native listeners)', () => {
  745. const click = jasmine.createSpy('click')
  746. const mouseup = jasmine.createSpy('mouseup')
  747. const mousedown = jasmine.createSpy('mousedown')
  748. vm = new Vue({
  749. el,
  750. template: `
  751. <foo-button
  752. @click="click"
  753. @mousedown="mousedown"
  754. @mouseup.native="mouseup">
  755. </foo-button>
  756. `,
  757. methods: {
  758. click,
  759. mouseup,
  760. mousedown
  761. },
  762. components: {
  763. fooButton: {
  764. template: `
  765. <button v-on="$listeners"></button>
  766. `
  767. }
  768. }
  769. })
  770. triggerEvent(vm.$el, 'click')
  771. expect(click.calls.count()).toBe(1)
  772. expect(mouseup.calls.count()).toBe(0)
  773. expect(mousedown.calls.count()).toBe(0)
  774. triggerEvent(vm.$el, 'mouseup')
  775. expect(click.calls.count()).toBe(1)
  776. expect(mouseup.calls.count()).toBe(1)
  777. expect(mousedown.calls.count()).toBe(0)
  778. triggerEvent(vm.$el, 'mousedown')
  779. expect(click.calls.count()).toBe(1)
  780. expect(mouseup.calls.count()).toBe(1)
  781. expect(mousedown.calls.count()).toBe(1)
  782. })
  783. // #6805 (v-on="object" bind order problem)
  784. it('object syntax (no argument): should fire after high-priority listeners', done => {
  785. const MyCheckbox = {
  786. template: '<input type="checkbox" v-model="model" v-on="$listeners">',
  787. props: {
  788. value: false
  789. },
  790. computed: {
  791. model: {
  792. get () {
  793. return this.value
  794. },
  795. set (val) {
  796. this.$emit('input', val)
  797. }
  798. }
  799. }
  800. }
  801. vm = new Vue({
  802. el,
  803. template: `
  804. <div>
  805. <my-checkbox v-model="check" @change="change"></my-checkbox>
  806. </div>
  807. `,
  808. components: { MyCheckbox },
  809. data: {
  810. check: false
  811. },
  812. methods: {
  813. change () {
  814. expect(this.check).toBe(true)
  815. done()
  816. }
  817. }
  818. })
  819. vm.$el.querySelector('input').click()
  820. })
  821. it('warn object syntax with modifier', () => {
  822. new Vue({
  823. template: `<button v-on.self="{}"></button>`
  824. }).$mount()
  825. expect(`v-on without argument does not support modifiers`).toHaveBeenWarned()
  826. })
  827. it('warn object syntax with non-object value', () => {
  828. new Vue({
  829. template: `<button v-on="123"></button>`
  830. }).$mount()
  831. expect(`v-on without argument expects an Object value`).toHaveBeenWarned()
  832. })
  833. it('should correctly remove once listener', done => {
  834. const vm = new Vue({
  835. template: `
  836. <div>
  837. <span v-if="ok" @click.once="foo">
  838. a
  839. </span>
  840. <span v-else a="a">
  841. b
  842. </span>
  843. </div>
  844. `,
  845. data: {
  846. ok: true
  847. },
  848. methods: {
  849. foo: spy
  850. }
  851. }).$mount()
  852. vm.ok = false
  853. waitForUpdate(() => {
  854. triggerEvent(vm.$el.childNodes[0], 'click')
  855. expect(spy.calls.count()).toBe(0)
  856. }).then(done)
  857. })
  858. // #7628
  859. it('handler should return the return value of inline function invocation', () => {
  860. let value
  861. new Vue({
  862. template: `<test @foo="bar()"></test>`,
  863. methods: {
  864. bar() {
  865. return 1
  866. }
  867. },
  868. components: {
  869. test: {
  870. created() {
  871. value = this.$listeners.foo()
  872. },
  873. render(h) {
  874. return h('div')
  875. }
  876. }
  877. }
  878. }).$mount()
  879. expect(value).toBe(1)
  880. })
  881. describe('dynamic arguments', () => {
  882. it('basic', done => {
  883. const spy = jasmine.createSpy()
  884. const vm = new Vue({
  885. template: `<div v-on:[key]="spy"></div>`,
  886. data: {
  887. key: 'click'
  888. },
  889. methods: {
  890. spy
  891. }
  892. }).$mount()
  893. triggerEvent(vm.$el, 'click')
  894. expect(spy.calls.count()).toBe(1)
  895. vm.key = 'mouseup'
  896. waitForUpdate(() => {
  897. triggerEvent(vm.$el, 'click')
  898. expect(spy.calls.count()).toBe(1)
  899. triggerEvent(vm.$el, 'mouseup')
  900. expect(spy.calls.count()).toBe(2)
  901. // explicit null value
  902. vm.key = null
  903. }).then(() => {
  904. triggerEvent(vm.$el, 'click')
  905. expect(spy.calls.count()).toBe(2)
  906. triggerEvent(vm.$el, 'mouseup')
  907. expect(spy.calls.count()).toBe(2)
  908. }).then(done)
  909. })
  910. it('shorthand', done => {
  911. const spy = jasmine.createSpy()
  912. const vm = new Vue({
  913. template: `<div @[key]="spy"></div>`,
  914. data: {
  915. key: 'click'
  916. },
  917. methods: {
  918. spy
  919. }
  920. }).$mount()
  921. triggerEvent(vm.$el, 'click')
  922. expect(spy.calls.count()).toBe(1)
  923. vm.key = 'mouseup'
  924. waitForUpdate(() => {
  925. triggerEvent(vm.$el, 'click')
  926. expect(spy.calls.count()).toBe(1)
  927. triggerEvent(vm.$el, 'mouseup')
  928. expect(spy.calls.count()).toBe(2)
  929. }).then(done)
  930. })
  931. it('with .middle modifier', () => {
  932. const spy = jasmine.createSpy()
  933. const vm = new Vue({
  934. template: `<div @[key].middle="spy"></div>`,
  935. data: {
  936. key: 'click'
  937. },
  938. methods: {
  939. spy
  940. }
  941. }).$mount()
  942. triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 })
  943. expect(spy).not.toHaveBeenCalled()
  944. triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 })
  945. expect(spy).toHaveBeenCalled()
  946. })
  947. it('with .right modifier', () => {
  948. const spy = jasmine.createSpy()
  949. const vm = new Vue({
  950. template: `<div @[key].right="spy"></div>`,
  951. data: {
  952. key: 'click'
  953. },
  954. methods: {
  955. spy
  956. }
  957. }).$mount()
  958. triggerEvent(vm.$el, 'contextmenu')
  959. expect(spy).toHaveBeenCalled()
  960. })
  961. it('with .capture modifier', () => {
  962. const callOrder = []
  963. const vm = new Vue({
  964. template: `
  965. <div @[key].capture="foo">
  966. <div @[key]="bar"></div>
  967. </div>
  968. `,
  969. data: {
  970. key: 'click'
  971. },
  972. methods: {
  973. foo () { callOrder.push(1) },
  974. bar () { callOrder.push(2) }
  975. }
  976. }).$mount()
  977. triggerEvent(vm.$el.firstChild, 'click')
  978. expect(callOrder.toString()).toBe('1,2')
  979. })
  980. it('with .once modifier', () => {
  981. const vm = new Vue({
  982. template: `<div @[key].once="foo"></div>`,
  983. data: { key: 'click' },
  984. methods: { foo: spy }
  985. }).$mount()
  986. triggerEvent(vm.$el, 'click')
  987. expect(spy.calls.count()).toBe(1)
  988. triggerEvent(vm.$el, 'click')
  989. expect(spy.calls.count()).toBe(1) // should no longer trigger
  990. })
  991. })
  992. })