on.spec.js 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  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('should not throw a warning if native modifier is used on a dynamic component', () => {
  432. vm = new Vue({
  433. el,
  434. template: `
  435. <component is="div" @click.native="foo('native')" @click="foo('regular')"/>
  436. `,
  437. methods: { foo: spy },
  438. })
  439. triggerEvent(vm.$el, 'click')
  440. expect(`The .native modifier for v-on is only valid on components but it was used on <div>.`).not.toHaveBeenWarned()
  441. expect(spy.calls.allArgs()).toEqual([['regular']]); // Regular @click should work for dynamic components resolved to native HTML elements.
  442. })
  443. it('.once modifier should work with child components', () => {
  444. vm = new Vue({
  445. el,
  446. template: '<bar @custom.once="foo"></bar>',
  447. methods: { foo: spy },
  448. components: {
  449. bar: {
  450. template: '<span>Hello</span>'
  451. }
  452. }
  453. })
  454. vm.$children[0].$emit('custom')
  455. expect(spy.calls.count()).toBe(1)
  456. vm.$children[0].$emit('custom')
  457. expect(spy.calls.count()).toBe(1) // should not be called again
  458. })
  459. it('remove listener', done => {
  460. const spy2 = jasmine.createSpy('remove listener')
  461. vm = new Vue({
  462. el,
  463. methods: { foo: spy, bar: spy2 },
  464. data: {
  465. ok: true
  466. },
  467. render (h) {
  468. return this.ok
  469. ? h('input', { on: { click: this.foo }})
  470. : h('input', { on: { input: this.bar }})
  471. }
  472. })
  473. triggerEvent(vm.$el, 'click')
  474. expect(spy.calls.count()).toBe(1)
  475. expect(spy2.calls.count()).toBe(0)
  476. vm.ok = false
  477. waitForUpdate(() => {
  478. triggerEvent(vm.$el, 'click')
  479. expect(spy.calls.count()).toBe(1) // should no longer trigger
  480. triggerEvent(vm.$el, 'input')
  481. expect(spy2.calls.count()).toBe(1)
  482. }).then(done)
  483. })
  484. it('remove capturing listener', done => {
  485. const spy2 = jasmine.createSpy('remove listener')
  486. vm = new Vue({
  487. el,
  488. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  489. data: {
  490. ok: true
  491. },
  492. render (h) {
  493. return this.ok
  494. ? h('div', { on: { '!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  495. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  496. }
  497. })
  498. triggerEvent(vm.$el.firstChild, 'click')
  499. expect(spy.calls.count()).toBe(1)
  500. expect(spy2.calls.count()).toBe(0)
  501. vm.ok = false
  502. waitForUpdate(() => {
  503. triggerEvent(vm.$el.firstChild, 'click')
  504. expect(spy.calls.count()).toBe(1) // should no longer trigger
  505. triggerEvent(vm.$el, 'mouseOver')
  506. expect(spy2.calls.count()).toBe(1)
  507. }).then(done)
  508. })
  509. it('remove once listener', done => {
  510. const spy2 = jasmine.createSpy('remove listener')
  511. vm = new Vue({
  512. el,
  513. methods: { foo: spy, bar: spy2 },
  514. data: {
  515. ok: true
  516. },
  517. render (h) {
  518. return this.ok
  519. ? h('input', { on: { '~click': this.foo }})
  520. : h('input', { on: { input: this.bar }})
  521. }
  522. })
  523. triggerEvent(vm.$el, 'click')
  524. expect(spy.calls.count()).toBe(1)
  525. triggerEvent(vm.$el, 'click')
  526. expect(spy.calls.count()).toBe(1) // should no longer trigger
  527. expect(spy2.calls.count()).toBe(0)
  528. vm.ok = false
  529. waitForUpdate(() => {
  530. triggerEvent(vm.$el, 'click')
  531. expect(spy.calls.count()).toBe(1) // should no longer trigger
  532. triggerEvent(vm.$el, 'input')
  533. expect(spy2.calls.count()).toBe(1)
  534. }).then(done)
  535. })
  536. it('remove capturing and once listener', done => {
  537. const spy2 = jasmine.createSpy('remove listener')
  538. vm = new Vue({
  539. el,
  540. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  541. data: {
  542. ok: true
  543. },
  544. render (h) {
  545. return this.ok
  546. ? h('div', { on: { '~!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  547. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  548. }
  549. })
  550. triggerEvent(vm.$el.firstChild, 'click')
  551. expect(spy.calls.count()).toBe(1)
  552. triggerEvent(vm.$el.firstChild, 'click')
  553. expect(spy.calls.count()).toBe(1) // should no longer trigger
  554. expect(spy2.calls.count()).toBe(0)
  555. vm.ok = false
  556. waitForUpdate(() => {
  557. triggerEvent(vm.$el.firstChild, 'click')
  558. expect(spy.calls.count()).toBe(1) // should no longer trigger
  559. triggerEvent(vm.$el, 'mouseOver')
  560. expect(spy2.calls.count()).toBe(1)
  561. }).then(done)
  562. })
  563. it('remove listener on child component', done => {
  564. const spy2 = jasmine.createSpy('remove listener')
  565. vm = new Vue({
  566. el,
  567. methods: { foo: spy, bar: spy2 },
  568. data: {
  569. ok: true
  570. },
  571. components: {
  572. test: {
  573. template: '<div></div>'
  574. }
  575. },
  576. render (h) {
  577. return this.ok
  578. ? h('test', { on: { foo: this.foo }})
  579. : h('test', { on: { bar: this.bar }})
  580. }
  581. })
  582. vm.$children[0].$emit('foo')
  583. expect(spy.calls.count()).toBe(1)
  584. expect(spy2.calls.count()).toBe(0)
  585. vm.ok = false
  586. waitForUpdate(() => {
  587. vm.$children[0].$emit('foo')
  588. expect(spy.calls.count()).toBe(1) // should no longer trigger
  589. vm.$children[0].$emit('bar')
  590. expect(spy2.calls.count()).toBe(1)
  591. }).then(done)
  592. })
  593. it('warn missing handlers', () => {
  594. vm = new Vue({
  595. el,
  596. data: { none: null },
  597. template: `<div @click="none"></div>`
  598. })
  599. expect(`Invalid handler for event "click": got null`).toHaveBeenWarned()
  600. expect(() => {
  601. triggerEvent(vm.$el, 'click')
  602. }).not.toThrow()
  603. })
  604. // Github Issue #5046
  605. it('should support keyboard modifier for direction keys', () => {
  606. const spyLeft = jasmine.createSpy()
  607. const spyRight = jasmine.createSpy()
  608. const spyUp = jasmine.createSpy()
  609. const spyDown = jasmine.createSpy()
  610. vm = new Vue({
  611. el,
  612. template: `
  613. <div>
  614. <input ref="left" @keydown.left="foo"></input>
  615. <input ref="right" @keydown.right="foo1"></input>
  616. <input ref="up" @keydown.up="foo2"></input>
  617. <input ref="down" @keydown.down="foo3"></input>
  618. </div>
  619. `,
  620. methods: {
  621. foo: spyLeft,
  622. foo1: spyRight,
  623. foo2: spyUp,
  624. foo3: spyDown
  625. }
  626. })
  627. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 37 })
  628. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 39 })
  629. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 39 })
  630. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 38 })
  631. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 38 })
  632. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 37 })
  633. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 40 })
  634. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 39 })
  635. expect(spyLeft.calls.count()).toBe(1)
  636. expect(spyRight.calls.count()).toBe(1)
  637. expect(spyUp.calls.count()).toBe(1)
  638. expect(spyDown.calls.count()).toBe(1)
  639. })
  640. // This test case should only run when the test browser supports passive.
  641. if (supportsPassive) {
  642. it('should support passive', () => {
  643. vm = new Vue({
  644. el,
  645. template: `
  646. <div>
  647. <input type="checkbox" ref="normal" @click="foo"/>
  648. <input type="checkbox" ref="passive" @click.passive="foo"/>
  649. <input type="checkbox" ref="exclusive" @click.prevent.passive/>
  650. </div>
  651. `,
  652. methods: {
  653. foo (e) {
  654. e.preventDefault()
  655. }
  656. }
  657. })
  658. vm.$refs.normal.checked = false
  659. vm.$refs.passive.checked = false
  660. vm.$refs.exclusive.checked = false
  661. vm.$refs.normal.click()
  662. vm.$refs.passive.click()
  663. vm.$refs.exclusive.click()
  664. expect(vm.$refs.normal.checked).toBe(false)
  665. expect(vm.$refs.passive.checked).toBe(true)
  666. expect(vm.$refs.exclusive.checked).toBe(true)
  667. expect('passive and prevent can\'t be used together. Passive handler can\'t prevent default event.').toHaveBeenWarned()
  668. })
  669. }
  670. // GitHub Issues #5146
  671. it('should only prevent when match keycode', () => {
  672. let prevented = false
  673. vm = new Vue({
  674. el,
  675. template: `
  676. <input ref="input" @keydown.enter.prevent="foo">
  677. `,
  678. methods: {
  679. foo ($event) {
  680. prevented = $event.defaultPrevented
  681. }
  682. }
  683. })
  684. triggerEvent(vm.$refs.input, 'keydown', e => { e.keyCode = 32 })
  685. expect(prevented).toBe(false)
  686. triggerEvent(vm.$refs.input, 'keydown', e => { e.keyCode = 13 })
  687. expect(prevented).toBe(true)
  688. })
  689. it('should transform click.right to contextmenu', () => {
  690. const spy = jasmine.createSpy('click.right')
  691. const vm = new Vue({
  692. template: `<div @click.right="foo"></div>`,
  693. methods: { foo: spy }
  694. }).$mount()
  695. triggerEvent(vm.$el, 'contextmenu')
  696. expect(spy).toHaveBeenCalled()
  697. })
  698. it('should transform click.middle to mouseup', () => {
  699. const spy = jasmine.createSpy('click.middle')
  700. vm = new Vue({
  701. el,
  702. template: `<div @click.middle="foo"></div>`,
  703. methods: { foo: spy }
  704. })
  705. triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 })
  706. expect(spy).not.toHaveBeenCalled()
  707. triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 })
  708. expect(spy).toHaveBeenCalled()
  709. })
  710. it('object syntax (no argument)', () => {
  711. const click = jasmine.createSpy('click')
  712. const mouseup = jasmine.createSpy('mouseup')
  713. vm = new Vue({
  714. el,
  715. template: `<button v-on="listeners">foo</button>`,
  716. created () {
  717. this.listeners = {
  718. click,
  719. mouseup
  720. }
  721. }
  722. })
  723. triggerEvent(vm.$el, 'click')
  724. expect(click.calls.count()).toBe(1)
  725. expect(mouseup.calls.count()).toBe(0)
  726. triggerEvent(vm.$el, 'mouseup')
  727. expect(click.calls.count()).toBe(1)
  728. expect(mouseup.calls.count()).toBe(1)
  729. })
  730. it('object syntax (no argument, mixed with normal listeners)', () => {
  731. const click1 = jasmine.createSpy('click1')
  732. const click2 = jasmine.createSpy('click2')
  733. const mouseup = jasmine.createSpy('mouseup')
  734. vm = new Vue({
  735. el,
  736. template: `<button v-on="listeners" @click="click2">foo</button>`,
  737. created () {
  738. this.listeners = {
  739. click: click1,
  740. mouseup
  741. }
  742. },
  743. methods: {
  744. click2
  745. }
  746. })
  747. triggerEvent(vm.$el, 'click')
  748. expect(click1.calls.count()).toBe(1)
  749. expect(click2.calls.count()).toBe(1)
  750. expect(mouseup.calls.count()).toBe(0)
  751. triggerEvent(vm.$el, 'mouseup')
  752. expect(click1.calls.count()).toBe(1)
  753. expect(click2.calls.count()).toBe(1)
  754. expect(mouseup.calls.count()).toBe(1)
  755. })
  756. it('object syntax (usage in HOC, mixed with native listeners)', () => {
  757. const click = jasmine.createSpy('click')
  758. const mouseup = jasmine.createSpy('mouseup')
  759. const mousedown = jasmine.createSpy('mousedown')
  760. vm = new Vue({
  761. el,
  762. template: `
  763. <foo-button
  764. @click="click"
  765. @mousedown="mousedown"
  766. @mouseup.native="mouseup">
  767. </foo-button>
  768. `,
  769. methods: {
  770. click,
  771. mouseup,
  772. mousedown
  773. },
  774. components: {
  775. fooButton: {
  776. template: `
  777. <button v-on="$listeners"></button>
  778. `
  779. }
  780. }
  781. })
  782. triggerEvent(vm.$el, 'click')
  783. expect(click.calls.count()).toBe(1)
  784. expect(mouseup.calls.count()).toBe(0)
  785. expect(mousedown.calls.count()).toBe(0)
  786. triggerEvent(vm.$el, 'mouseup')
  787. expect(click.calls.count()).toBe(1)
  788. expect(mouseup.calls.count()).toBe(1)
  789. expect(mousedown.calls.count()).toBe(0)
  790. triggerEvent(vm.$el, 'mousedown')
  791. expect(click.calls.count()).toBe(1)
  792. expect(mouseup.calls.count()).toBe(1)
  793. expect(mousedown.calls.count()).toBe(1)
  794. })
  795. // #6805 (v-on="object" bind order problem)
  796. it('object syntax (no argument): should fire after high-priority listeners', done => {
  797. const MyCheckbox = {
  798. template: '<input type="checkbox" v-model="model" v-on="$listeners">',
  799. props: {
  800. value: false
  801. },
  802. computed: {
  803. model: {
  804. get () {
  805. return this.value
  806. },
  807. set (val) {
  808. this.$emit('input', val)
  809. }
  810. }
  811. }
  812. }
  813. vm = new Vue({
  814. el,
  815. template: `
  816. <div>
  817. <my-checkbox v-model="check" @change="change"></my-checkbox>
  818. </div>
  819. `,
  820. components: { MyCheckbox },
  821. data: {
  822. check: false
  823. },
  824. methods: {
  825. change () {
  826. expect(this.check).toBe(true)
  827. done()
  828. }
  829. }
  830. })
  831. vm.$el.querySelector('input').click()
  832. })
  833. it('warn object syntax with modifier', () => {
  834. new Vue({
  835. template: `<button v-on.self="{}"></button>`
  836. }).$mount()
  837. expect(`v-on without argument does not support modifiers`).toHaveBeenWarned()
  838. })
  839. it('warn object syntax with non-object value', () => {
  840. new Vue({
  841. template: `<button v-on="123"></button>`
  842. }).$mount()
  843. expect(`v-on without argument expects an Object value`).toHaveBeenWarned()
  844. })
  845. it('should correctly remove once listener', done => {
  846. const vm = new Vue({
  847. template: `
  848. <div>
  849. <span v-if="ok" @click.once="foo">
  850. a
  851. </span>
  852. <span v-else a="a">
  853. b
  854. </span>
  855. </div>
  856. `,
  857. data: {
  858. ok: true
  859. },
  860. methods: {
  861. foo: spy
  862. }
  863. }).$mount()
  864. vm.ok = false
  865. waitForUpdate(() => {
  866. triggerEvent(vm.$el.childNodes[0], 'click')
  867. expect(spy.calls.count()).toBe(0)
  868. }).then(done)
  869. })
  870. // #7628
  871. it('handler should return the return value of inline function invocation', () => {
  872. let value
  873. new Vue({
  874. template: `<test @foo="bar()"></test>`,
  875. methods: {
  876. bar() {
  877. return 1
  878. }
  879. },
  880. components: {
  881. test: {
  882. created() {
  883. value = this.$listeners.foo()
  884. },
  885. render(h) {
  886. return h('div')
  887. }
  888. }
  889. }
  890. }).$mount()
  891. expect(value).toBe(1)
  892. })
  893. it('should not execute callback if modifiers are present', () => {
  894. vm = new Vue({
  895. el,
  896. template: '<input @keyup.?="foo">',
  897. methods: { foo: spy }
  898. })
  899. // simulating autocomplete event (Event object with type keyup but without keyCode)
  900. triggerEvent(vm.$el, 'keyup')
  901. expect(spy.calls.count()).toBe(0)
  902. })
  903. describe('dynamic arguments', () => {
  904. it('basic', done => {
  905. const spy = jasmine.createSpy()
  906. const vm = new Vue({
  907. template: `<div v-on:[key]="spy"></div>`,
  908. data: {
  909. key: 'click'
  910. },
  911. methods: {
  912. spy
  913. }
  914. }).$mount()
  915. triggerEvent(vm.$el, 'click')
  916. expect(spy.calls.count()).toBe(1)
  917. vm.key = 'mouseup'
  918. waitForUpdate(() => {
  919. triggerEvent(vm.$el, 'click')
  920. expect(spy.calls.count()).toBe(1)
  921. triggerEvent(vm.$el, 'mouseup')
  922. expect(spy.calls.count()).toBe(2)
  923. // explicit null value
  924. vm.key = null
  925. }).then(() => {
  926. triggerEvent(vm.$el, 'click')
  927. expect(spy.calls.count()).toBe(2)
  928. triggerEvent(vm.$el, 'mouseup')
  929. expect(spy.calls.count()).toBe(2)
  930. }).then(done)
  931. })
  932. it('shorthand', done => {
  933. const spy = jasmine.createSpy()
  934. const vm = new Vue({
  935. template: `<div @[key]="spy"></div>`,
  936. data: {
  937. key: 'click'
  938. },
  939. methods: {
  940. spy
  941. }
  942. }).$mount()
  943. triggerEvent(vm.$el, 'click')
  944. expect(spy.calls.count()).toBe(1)
  945. vm.key = 'mouseup'
  946. waitForUpdate(() => {
  947. triggerEvent(vm.$el, 'click')
  948. expect(spy.calls.count()).toBe(1)
  949. triggerEvent(vm.$el, 'mouseup')
  950. expect(spy.calls.count()).toBe(2)
  951. }).then(done)
  952. })
  953. it('with .middle modifier', () => {
  954. const spy = jasmine.createSpy()
  955. const vm = new Vue({
  956. template: `<div @[key].middle="spy"></div>`,
  957. data: {
  958. key: 'click'
  959. },
  960. methods: {
  961. spy
  962. }
  963. }).$mount()
  964. triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 })
  965. expect(spy).not.toHaveBeenCalled()
  966. triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 })
  967. expect(spy).toHaveBeenCalled()
  968. })
  969. it('with .right modifier', () => {
  970. const spy = jasmine.createSpy()
  971. const vm = new Vue({
  972. template: `<div @[key].right="spy"></div>`,
  973. data: {
  974. key: 'click'
  975. },
  976. methods: {
  977. spy
  978. }
  979. }).$mount()
  980. triggerEvent(vm.$el, 'contextmenu')
  981. expect(spy).toHaveBeenCalled()
  982. })
  983. it('with .capture modifier', () => {
  984. const callOrder = []
  985. const vm = new Vue({
  986. template: `
  987. <div @[key].capture="foo">
  988. <div @[key]="bar"></div>
  989. </div>
  990. `,
  991. data: {
  992. key: 'click'
  993. },
  994. methods: {
  995. foo () { callOrder.push(1) },
  996. bar () { callOrder.push(2) }
  997. }
  998. }).$mount()
  999. triggerEvent(vm.$el.firstChild, 'click')
  1000. expect(callOrder.toString()).toBe('1,2')
  1001. })
  1002. it('with .once modifier', () => {
  1003. const vm = new Vue({
  1004. template: `<div @[key].once="foo"></div>`,
  1005. data: { key: 'click' },
  1006. methods: { foo: spy }
  1007. }).$mount()
  1008. triggerEvent(vm.$el, 'click')
  1009. expect(spy.calls.count()).toBe(1)
  1010. triggerEvent(vm.$el, 'click')
  1011. expect(spy.calls.count()).toBe(1) // should no longer trigger
  1012. })
  1013. })
  1014. })