import Vue from 'vue' import { supportsPassive } from 'core/util/env' describe('Directive v-on', () => { let vm, spy, el beforeEach(() => { vm = null spy = jasmine.createSpy() el = document.createElement('div') document.body.appendChild(el) }) afterEach(() => { if (vm) { document.body.removeChild(vm.$el) } }) it('should bind event to a method', () => { vm = new Vue({ el, template: '
', methods: { foo: spy } }) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) const args = spy.calls.allArgs() const event = args[0] && args[0][0] || {} expect(event.type).toBe('click') }) it('should bind event to a inline statement', () => { vm = new Vue({ el, template: '
', methods: { foo: spy } }) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) const args = spy.calls.allArgs() const firstArgs = args[0] expect(firstArgs.length).toBe(4) expect(firstArgs[0]).toBe(1) expect(firstArgs[1]).toBe(2) expect(firstArgs[2]).toBe(3) expect(firstArgs[3].type).toBe('click') }) it('should support inline function expression', () => { const spy = jasmine.createSpy() vm = new Vue({ el, template: `
`, methods: { log: spy } }).$mount() triggerEvent(vm.$el, 'click') expect(spy).toHaveBeenCalledWith('test') }) it('should support shorthand', () => { vm = new Vue({ el, template: '', methods: { foo: spy } }) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) }) it('should support stop propagation', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) const hash = window.location.hash triggerEvent(vm.$el, 'click') expect(window.location.hash).toBe(hash) }) it('should support prevent default', () => { vm = new Vue({ el, template: ` `, methods: { foo ($event) { spy($event.defaultPrevented) } } }) vm.$refs.input.checked = false triggerEvent(vm.$refs.input, 'click') expect(spy).toHaveBeenCalledWith(true) }) it('should support capture', () => { const callOrder = [] vm = new Vue({ el, template: `
`, methods: { foo () { callOrder.push(1) }, bar () { callOrder.push(2) } } }) triggerEvent(vm.$el.firstChild, 'click') expect(callOrder.toString()).toBe('1,2') }) it('should support once', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) // should no longer trigger }) // #4655 it('should handle .once on multiple elements properly', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$refs.one, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.one, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.two, 'click') expect(spy.calls.count()).toBe(2) triggerEvent(vm.$refs.one, 'click') triggerEvent(vm.$refs.two, 'click') expect(spy.calls.count()).toBe(2) }) it('should support capture and once', () => { const callOrder = [] vm = new Vue({ el, template: `
`, methods: { foo () { callOrder.push(1) }, bar () { callOrder.push(2) } } }) triggerEvent(vm.$el.firstChild, 'click') expect(callOrder.toString()).toBe('1,2') triggerEvent(vm.$el.firstChild, 'click') expect(callOrder.toString()).toBe('1,2,2') }) // #4846 it('should support once and other modifiers', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$el.firstChild, 'click') expect(spy).not.toHaveBeenCalled() triggerEvent(vm.$el, 'click') expect(spy).toHaveBeenCalled() triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) }) it('should support keyCode', () => { vm = new Vue({ el, template: ``, methods: { foo: spy } }) triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 13 }) expect(spy).toHaveBeenCalled() }) it('should support automatic key name inference', () => { vm = new Vue({ el, template: ``, methods: { foo: spy } }) triggerEvent(vm.$el, 'keyup', e => { e.key = 'ArrowRight' }) expect(spy).toHaveBeenCalled() }) // ctrl, shift, alt, meta it('should support system modifers', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$refs.ctrl, 'keyup') expect(spy.calls.count()).toBe(0) triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true }) expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.shift, 'keyup') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.shift, 'keyup', e => { e.shiftKey = true }) expect(spy.calls.count()).toBe(2) triggerEvent(vm.$refs.alt, 'keyup') expect(spy.calls.count()).toBe(2) triggerEvent(vm.$refs.alt, 'keyup', e => { e.altKey = true }) expect(spy.calls.count()).toBe(3) triggerEvent(vm.$refs.meta, 'keyup') expect(spy.calls.count()).toBe(3) triggerEvent(vm.$refs.meta, 'keyup', e => { e.metaKey = true }) expect(spy.calls.count()).toBe(4) }) it('should support exact modifier', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$refs.ctrl, 'keyup') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true }) expect(spy.calls.count()).toBe(1) // should not trigger if has other system modifiers triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true e.altKey = true }) expect(spy.calls.count()).toBe(1) }) it('should support system modifiers with exact', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$refs.ctrl, 'keyup') expect(spy.calls.count()).toBe(0) triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true }) expect(spy.calls.count()).toBe(1) // should not trigger if has other system modifiers triggerEvent(vm.$refs.ctrl, 'keyup', e => { e.ctrlKey = true e.altKey = true }) expect(spy.calls.count()).toBe(1) }) it('should support number keyCode', () => { vm = new Vue({ el, template: ``, methods: { foo: spy } }) triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 13 }) expect(spy).toHaveBeenCalled() }) it('should support mouse modifier', () => { const left = 0 const middle = 1 const right = 2 const spyLeft = jasmine.createSpy() const spyMiddle = jasmine.createSpy() const spyRight = jasmine.createSpy() vm = new Vue({ el, template: `
left
right
right
`, methods: { foo: spyLeft, foo1: spyRight, foo2: spyMiddle } }) triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = right }) triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = middle }) expect(spyLeft).not.toHaveBeenCalled() triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = left }) expect(spyLeft).toHaveBeenCalled() triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = left }) triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = middle }) expect(spyRight).not.toHaveBeenCalled() triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = right }) expect(spyRight).toHaveBeenCalled() triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = left }) triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = right }) expect(spyMiddle).not.toHaveBeenCalled() triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = middle }) expect(spyMiddle).toHaveBeenCalled() }) it('should support KeyboardEvent.key for built in aliases', () => { vm = new Vue({ el, template: `
`, methods: { foo: spy } }) triggerEvent(vm.$refs.enter, 'keyup', e => { e.key = 'Enter' }) expect(spy.calls.count()).toBe(1) triggerEvent(vm.$refs.space, 'keyup', e => { e.key = ' ' }) expect(spy.calls.count()).toBe(2) triggerEvent(vm.$refs.esc, 'keyup', e => { e.key = 'Escape' }) expect(spy.calls.count()).toBe(3) triggerEvent(vm.$refs.left, 'keyup', e => { e.key = 'ArrowLeft' }) expect(spy.calls.count()).toBe(4) triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Backspace' }) expect(spy.calls.count()).toBe(5) triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Delete' }) expect(spy.calls.count()).toBe(6) }) it('should support custom keyCode', () => { Vue.config.keyCodes.test = 1 vm = new Vue({ el, template: ``, methods: { foo: spy } }) triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 1 }) expect(spy).toHaveBeenCalled() Vue.config.keyCodes = Object.create(null) }) it('should override built-in keyCode', () => { Vue.config.keyCodes.up = [1, 87] vm = new Vue({ el, template: ``, methods: { foo: spy } }) triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 87 }) expect(spy).toHaveBeenCalled() triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 1 }) expect(spy).toHaveBeenCalledTimes(2) // should not affect built-in down keycode triggerEvent(vm.$el, 'keyup', e => { e.keyCode = 40 }) expect(spy).toHaveBeenCalledTimes(3) Vue.config.keyCodes = Object.create(null) }) it('should bind to a child component', () => { vm = new Vue({ el, template: '', methods: { foo: spy }, components: { bar: { template: 'Hello' } } }) vm.$children[0].$emit('custom', 'foo', 'bar') expect(spy).toHaveBeenCalledWith('foo', 'bar') }) it('should be able to bind native events for a child component', () => { vm = new Vue({ el, template: '', methods: { foo: spy }, components: { bar: { template: 'Hello' } } }) vm.$children[0].$emit('click') expect(spy).not.toHaveBeenCalled() triggerEvent(vm.$children[0].$el, 'click') expect(spy).toHaveBeenCalled() }) it('should throw a warning if native modifier is used on native HTML element', () => { vm = new Vue({ el, template: ` `, methods: { foo: spy }, }) triggerEvent(vm.$el, 'click') expect(`The .native modifier for v-on is only valid on components but it was used on `, created () { this.listeners = { click, mouseup } } }) triggerEvent(vm.$el, 'click') expect(click.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(0) triggerEvent(vm.$el, 'mouseup') expect(click.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(1) }) it('object syntax (no argument, mixed with normal listeners)', () => { const click1 = jasmine.createSpy('click1') const click2 = jasmine.createSpy('click2') const mouseup = jasmine.createSpy('mouseup') vm = new Vue({ el, template: ``, created () { this.listeners = { click: click1, mouseup } }, methods: { click2 } }) triggerEvent(vm.$el, 'click') expect(click1.calls.count()).toBe(1) expect(click2.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(0) triggerEvent(vm.$el, 'mouseup') expect(click1.calls.count()).toBe(1) expect(click2.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(1) }) it('object syntax (usage in HOC, mixed with native listeners)', () => { const click = jasmine.createSpy('click') const mouseup = jasmine.createSpy('mouseup') const mousedown = jasmine.createSpy('mousedown') vm = new Vue({ el, template: ` `, methods: { click, mouseup, mousedown }, components: { fooButton: { template: ` ` } } }) triggerEvent(vm.$el, 'click') expect(click.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(0) expect(mousedown.calls.count()).toBe(0) triggerEvent(vm.$el, 'mouseup') expect(click.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(1) expect(mousedown.calls.count()).toBe(0) triggerEvent(vm.$el, 'mousedown') expect(click.calls.count()).toBe(1) expect(mouseup.calls.count()).toBe(1) expect(mousedown.calls.count()).toBe(1) }) // #6805 (v-on="object" bind order problem) it('object syntax (no argument): should fire after high-priority listeners', done => { const MyCheckbox = { template: '', props: { value: false }, computed: { model: { get () { return this.value }, set (val) { this.$emit('input', val) } } } } vm = new Vue({ el, template: `
`, components: { MyCheckbox }, data: { check: false }, methods: { change () { expect(this.check).toBe(true) done() } } }) vm.$el.querySelector('input').click() }) it('warn object syntax with modifier', () => { new Vue({ template: `` }).$mount() expect(`v-on without argument does not support modifiers`).toHaveBeenWarned() }) it('warn object syntax with non-object value', () => { new Vue({ template: `` }).$mount() expect(`v-on without argument expects an Object value`).toHaveBeenWarned() }) it('should correctly remove once listener', done => { const vm = new Vue({ template: `
a b
`, data: { ok: true }, methods: { foo: spy } }).$mount() vm.ok = false waitForUpdate(() => { triggerEvent(vm.$el.childNodes[0], 'click') expect(spy.calls.count()).toBe(0) }).then(done) }) // #7628 it('handler should return the return value of inline function invocation', () => { let value new Vue({ template: ``, methods: { bar() { return 1 } }, components: { test: { created() { value = this.$listeners.foo() }, render(h) { return h('div') } } } }).$mount() expect(value).toBe(1) }) it('should not execute callback if modifiers are present', () => { vm = new Vue({ el, template: '', methods: { foo: spy } }) // simulating autocomplete event (Event object with type keyup but without keyCode) triggerEvent(vm.$el, 'keyup') expect(spy.calls.count()).toBe(0) }) describe('dynamic arguments', () => { it('basic', done => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { spy } }).$mount() triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) vm.key = 'mouseup' waitForUpdate(() => { triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$el, 'mouseup') expect(spy.calls.count()).toBe(2) // explicit null value vm.key = null }).then(() => { triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(2) triggerEvent(vm.$el, 'mouseup') expect(spy.calls.count()).toBe(2) }).then(done) }) it('shorthand', done => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { spy } }).$mount() triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) vm.key = 'mouseup' waitForUpdate(() => { triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$el, 'mouseup') expect(spy.calls.count()).toBe(2) }).then(done) }) it('with .middle modifier', () => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { spy } }).$mount() triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 }) expect(spy).not.toHaveBeenCalled() triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 }) expect(spy).toHaveBeenCalled() }) it('with .right modifier', () => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { spy } }).$mount() triggerEvent(vm.$el, 'contextmenu') expect(spy).toHaveBeenCalled() }) it('with .capture modifier', () => { const callOrder = [] const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { foo () { callOrder.push(1) }, bar () { callOrder.push(2) } } }).$mount() triggerEvent(vm.$el.firstChild, 'click') expect(callOrder.toString()).toBe('1,2') }) it('with .once modifier', () => { const vm = new Vue({ template: `
`, data: { key: 'click' }, methods: { foo: spy } }).$mount() triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) triggerEvent(vm.$el, 'click') expect(spy.calls.count()).toBe(1) // should no longer trigger }) }) })