on.spec.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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. <input type="checkbox" ref="input" @click.prevent="foo">
  79. `,
  80. methods: { foo: spy2 }
  81. })
  82. vm.$refs.input.checked = false
  83. triggerEvent(vm.$refs.input, 'click')
  84. expect(spy2).toHaveBeenCalled()
  85. expect(vm.$refs.input.checked).toBe(false)
  86. })
  87. it('should support capture', () => {
  88. const callOrder = []
  89. vm = new Vue({
  90. el,
  91. template: `
  92. <div @click.capture="foo">
  93. <div @click="bar"></div>
  94. </div>
  95. `,
  96. methods: {
  97. foo () { callOrder.push(1) },
  98. bar () { callOrder.push(2) }
  99. }
  100. })
  101. triggerEvent(vm.$el.firstChild, 'click')
  102. expect(callOrder.toString()).toBe('1,2')
  103. })
  104. it('should support once', () => {
  105. vm = new Vue({
  106. el,
  107. template: `
  108. <div @click.once="foo">
  109. </div>
  110. `,
  111. methods: { foo: spy }
  112. })
  113. triggerEvent(vm.$el, 'click')
  114. expect(spy.calls.count()).toBe(1)
  115. triggerEvent(vm.$el, 'click')
  116. expect(spy.calls.count()).toBe(1) // should no longer trigger
  117. })
  118. // #4655
  119. it('should handle .once on multiple elements properly', () => {
  120. vm = new Vue({
  121. el,
  122. template: `
  123. <div>
  124. <button ref="one" @click.once="foo">one</button>
  125. <button ref="two" @click.once="foo">two</button>
  126. </div>
  127. `,
  128. methods: { foo: spy }
  129. })
  130. triggerEvent(vm.$refs.one, 'click')
  131. expect(spy.calls.count()).toBe(1)
  132. triggerEvent(vm.$refs.one, 'click')
  133. expect(spy.calls.count()).toBe(1)
  134. triggerEvent(vm.$refs.two, 'click')
  135. expect(spy.calls.count()).toBe(2)
  136. triggerEvent(vm.$refs.one, 'click')
  137. triggerEvent(vm.$refs.two, 'click')
  138. expect(spy.calls.count()).toBe(2)
  139. })
  140. it('should support capture and once', () => {
  141. const callOrder = []
  142. vm = new Vue({
  143. el,
  144. template: `
  145. <div @click.capture.once="foo">
  146. <div @click="bar"></div>
  147. </div>
  148. `,
  149. methods: {
  150. foo () { callOrder.push(1) },
  151. bar () { callOrder.push(2) }
  152. }
  153. })
  154. triggerEvent(vm.$el.firstChild, 'click')
  155. expect(callOrder.toString()).toBe('1,2')
  156. triggerEvent(vm.$el.firstChild, 'click')
  157. expect(callOrder.toString()).toBe('1,2,2')
  158. })
  159. // #4846
  160. it('should support once and other modifiers', () => {
  161. vm = new Vue({
  162. el,
  163. template: `<div @click.once.self="foo"><span/></div>`,
  164. methods: { foo: spy }
  165. })
  166. triggerEvent(vm.$el.firstChild, 'click')
  167. expect(spy).not.toHaveBeenCalled()
  168. triggerEvent(vm.$el, 'click')
  169. expect(spy).toHaveBeenCalled()
  170. triggerEvent(vm.$el, 'click')
  171. expect(spy.calls.count()).toBe(1)
  172. })
  173. it('should support keyCode', () => {
  174. vm = new Vue({
  175. el,
  176. template: `<input @keyup.enter="foo">`,
  177. methods: { foo: spy }
  178. })
  179. triggerEvent(vm.$el, 'keyup', e => {
  180. e.keyCode = 13
  181. })
  182. expect(spy).toHaveBeenCalled()
  183. })
  184. it('should support number keyCode', () => {
  185. vm = new Vue({
  186. el,
  187. template: `<input @keyup.13="foo">`,
  188. methods: { foo: spy }
  189. })
  190. triggerEvent(vm.$el, 'keyup', e => {
  191. e.keyCode = 13
  192. })
  193. expect(spy).toHaveBeenCalled()
  194. })
  195. it('should support mouse modifier', () => {
  196. const left = 0
  197. const middle = 1
  198. const right = 2
  199. const spyLeft = jasmine.createSpy()
  200. const spyMiddle = jasmine.createSpy()
  201. const spyRight = jasmine.createSpy()
  202. vm = new Vue({
  203. el,
  204. template: `
  205. <div>
  206. <div ref="left" @mousedown.left="foo">left</div>
  207. <div ref="right" @mousedown.right="foo1">right</div>
  208. <div ref="middle" @mousedown.middle="foo2">right</div>
  209. </div>
  210. `,
  211. methods: {
  212. foo: spyLeft,
  213. foo1: spyRight,
  214. foo2: spyMiddle
  215. }
  216. })
  217. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = right })
  218. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = middle })
  219. expect(spyLeft).not.toHaveBeenCalled()
  220. triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = left })
  221. expect(spyLeft).toHaveBeenCalled()
  222. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = left })
  223. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = middle })
  224. expect(spyRight).not.toHaveBeenCalled()
  225. triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = right })
  226. expect(spyRight).toHaveBeenCalled()
  227. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = left })
  228. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = right })
  229. expect(spyMiddle).not.toHaveBeenCalled()
  230. triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = middle })
  231. expect(spyMiddle).toHaveBeenCalled()
  232. })
  233. it('should support custom keyCode', () => {
  234. Vue.config.keyCodes.test = 1
  235. vm = new Vue({
  236. el,
  237. template: `<input @keyup.test="foo">`,
  238. methods: { foo: spy }
  239. })
  240. triggerEvent(vm.$el, 'keyup', e => {
  241. e.keyCode = 1
  242. })
  243. expect(spy).toHaveBeenCalled()
  244. Vue.config.keyCodes = Object.create(null)
  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. Vue.config.keyCodes = Object.create(null)
  267. })
  268. it('should bind to a child component', () => {
  269. Vue.component('bar', {
  270. template: '<span>Hello</span>'
  271. })
  272. vm = new Vue({
  273. el,
  274. template: '<bar @custom="foo"></bar>',
  275. methods: { foo: spy }
  276. })
  277. vm.$children[0].$emit('custom', 'foo', 'bar')
  278. expect(spy).toHaveBeenCalledWith('foo', 'bar')
  279. })
  280. it('should be able to bind native events for a child component', () => {
  281. Vue.component('bar', {
  282. template: '<span>Hello</span>'
  283. })
  284. vm = new Vue({
  285. el,
  286. template: '<bar @click.native="foo"></bar>',
  287. methods: { foo: spy }
  288. })
  289. vm.$children[0].$emit('click')
  290. expect(spy).not.toHaveBeenCalled()
  291. triggerEvent(vm.$children[0].$el, 'click')
  292. expect(spy).toHaveBeenCalled()
  293. })
  294. it('.once modifier should work with child components', () => {
  295. Vue.component('bar', {
  296. template: '<span>Hello</span>'
  297. })
  298. vm = new Vue({
  299. el,
  300. template: '<bar @custom.once="foo"></bar>',
  301. methods: { foo: spy }
  302. })
  303. vm.$children[0].$emit('custom')
  304. expect(spy.calls.count()).toBe(1)
  305. vm.$children[0].$emit('custom')
  306. expect(spy.calls.count()).toBe(1) // should not be called again
  307. })
  308. it('remove listener', done => {
  309. const spy2 = jasmine.createSpy('remove listener')
  310. vm = new Vue({
  311. el,
  312. methods: { foo: spy, bar: spy2 },
  313. data: {
  314. ok: true
  315. },
  316. render (h) {
  317. return this.ok
  318. ? h('input', { on: { click: this.foo }})
  319. : h('input', { on: { input: this.bar }})
  320. }
  321. })
  322. triggerEvent(vm.$el, 'click')
  323. expect(spy.calls.count()).toBe(1)
  324. expect(spy2.calls.count()).toBe(0)
  325. vm.ok = false
  326. waitForUpdate(() => {
  327. triggerEvent(vm.$el, 'click')
  328. expect(spy.calls.count()).toBe(1) // should no longer trigger
  329. triggerEvent(vm.$el, 'input')
  330. expect(spy2.calls.count()).toBe(1)
  331. }).then(done)
  332. })
  333. it('remove capturing listener', done => {
  334. const spy2 = jasmine.createSpy('remove listener')
  335. vm = new Vue({
  336. el,
  337. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  338. data: {
  339. ok: true
  340. },
  341. render (h) {
  342. return this.ok
  343. ? h('div', { on: { '!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  344. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  345. }
  346. })
  347. triggerEvent(vm.$el.firstChild, 'click')
  348. expect(spy.calls.count()).toBe(1)
  349. expect(spy2.calls.count()).toBe(0)
  350. vm.ok = false
  351. waitForUpdate(() => {
  352. triggerEvent(vm.$el.firstChild, 'click')
  353. expect(spy.calls.count()).toBe(1) // should no longer trigger
  354. triggerEvent(vm.$el, 'mouseOver')
  355. expect(spy2.calls.count()).toBe(1)
  356. }).then(done)
  357. })
  358. it('remove once listener', done => {
  359. const spy2 = jasmine.createSpy('remove listener')
  360. vm = new Vue({
  361. el,
  362. methods: { foo: spy, bar: spy2 },
  363. data: {
  364. ok: true
  365. },
  366. render (h) {
  367. return this.ok
  368. ? h('input', { on: { '~click': this.foo }})
  369. : h('input', { on: { input: this.bar }})
  370. }
  371. })
  372. triggerEvent(vm.$el, 'click')
  373. expect(spy.calls.count()).toBe(1)
  374. triggerEvent(vm.$el, 'click')
  375. expect(spy.calls.count()).toBe(1) // should no longer trigger
  376. expect(spy2.calls.count()).toBe(0)
  377. vm.ok = false
  378. waitForUpdate(() => {
  379. triggerEvent(vm.$el, 'click')
  380. expect(spy.calls.count()).toBe(1) // should no longer trigger
  381. triggerEvent(vm.$el, 'input')
  382. expect(spy2.calls.count()).toBe(1)
  383. }).then(done)
  384. })
  385. it('remove capturing and once listener', done => {
  386. const spy2 = jasmine.createSpy('remove listener')
  387. vm = new Vue({
  388. el,
  389. methods: { foo: spy, bar: spy2, stopped (ev) { ev.stopPropagation() } },
  390. data: {
  391. ok: true
  392. },
  393. render (h) {
  394. return this.ok
  395. ? h('div', { on: { '~!click': this.foo }}, [h('div', { on: { click: this.stopped }})])
  396. : h('div', { on: { mouseOver: this.bar }}, [h('div')])
  397. }
  398. })
  399. triggerEvent(vm.$el.firstChild, 'click')
  400. expect(spy.calls.count()).toBe(1)
  401. triggerEvent(vm.$el.firstChild, 'click')
  402. expect(spy.calls.count()).toBe(1) // should no longer trigger
  403. expect(spy2.calls.count()).toBe(0)
  404. vm.ok = false
  405. waitForUpdate(() => {
  406. triggerEvent(vm.$el.firstChild, 'click')
  407. expect(spy.calls.count()).toBe(1) // should no longer trigger
  408. triggerEvent(vm.$el, 'mouseOver')
  409. expect(spy2.calls.count()).toBe(1)
  410. }).then(done)
  411. })
  412. it('remove listener on child component', done => {
  413. const spy2 = jasmine.createSpy('remove listener')
  414. vm = new Vue({
  415. el,
  416. methods: { foo: spy, bar: spy2 },
  417. data: {
  418. ok: true
  419. },
  420. components: {
  421. test: {
  422. template: '<div></div>'
  423. }
  424. },
  425. render (h) {
  426. return this.ok
  427. ? h('test', { on: { foo: this.foo }})
  428. : h('test', { on: { bar: this.bar }})
  429. }
  430. })
  431. vm.$children[0].$emit('foo')
  432. expect(spy.calls.count()).toBe(1)
  433. expect(spy2.calls.count()).toBe(0)
  434. vm.ok = false
  435. waitForUpdate(() => {
  436. vm.$children[0].$emit('foo')
  437. expect(spy.calls.count()).toBe(1) // should no longer trigger
  438. vm.$children[0].$emit('bar')
  439. expect(spy2.calls.count()).toBe(1)
  440. }).then(done)
  441. })
  442. it('warn missing handlers', () => {
  443. vm = new Vue({
  444. el,
  445. data: { none: null },
  446. template: `<div @click="none"></div>`
  447. })
  448. expect(`Invalid handler for event "click": got null`).toHaveBeenWarned()
  449. expect(() => {
  450. triggerEvent(vm.$el, 'click')
  451. }).not.toThrow()
  452. })
  453. // Github Issue #5046
  454. it('should support keyboard modifier', () => {
  455. const spyLeft = jasmine.createSpy()
  456. const spyRight = jasmine.createSpy()
  457. const spyUp = jasmine.createSpy()
  458. const spyDown = jasmine.createSpy()
  459. vm = new Vue({
  460. el,
  461. template: `
  462. <div>
  463. <input ref="left" @keydown.left="foo"></input>
  464. <input ref="right" @keydown.right="foo1"></input>
  465. <input ref="up" @keydown.up="foo2"></input>
  466. <input ref="down" @keydown.down="foo3"></input>
  467. </div>
  468. `,
  469. methods: {
  470. foo: spyLeft,
  471. foo1: spyRight,
  472. foo2: spyUp,
  473. foo3: spyDown
  474. }
  475. })
  476. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 37 })
  477. triggerEvent(vm.$refs.left, 'keydown', e => { e.keyCode = 39 })
  478. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 39 })
  479. triggerEvent(vm.$refs.right, 'keydown', e => { e.keyCode = 38 })
  480. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 38 })
  481. triggerEvent(vm.$refs.up, 'keydown', e => { e.keyCode = 37 })
  482. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 40 })
  483. triggerEvent(vm.$refs.down, 'keydown', e => { e.keyCode = 39 })
  484. expect(spyLeft.calls.count()).toBe(1)
  485. expect(spyRight.calls.count()).toBe(1)
  486. expect(spyUp.calls.count()).toBe(1)
  487. expect(spyDown.calls.count()).toBe(1)
  488. })
  489. })