on.spec.js 15 KB

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