import Vue from 'vue' describe('Directive v-bind', () => { it('normal attr', done => { const vm = new Vue({ template: '
hello
', data: { foo: 'ok' } }).$mount() expect(vm.$el.firstChild.getAttribute('test')).toBe('ok') vm.foo = 'again' waitForUpdate(() => { expect(vm.$el.firstChild.getAttribute('test')).toBe('again') vm.foo = null }).then(() => { expect(vm.$el.firstChild.hasAttribute('test')).toBe(false) vm.foo = false }).then(() => { expect(vm.$el.firstChild.hasAttribute('test')).toBe(false) vm.foo = true }).then(() => { expect(vm.$el.firstChild.getAttribute('test')).toBe('true') vm.foo = 0 }).then(() => { expect(vm.$el.firstChild.getAttribute('test')).toBe('0') }).then(done) }) it('should set property for input value', done => { const vm = new Vue({ template: `
`, data: { foo: 'ok', bar: false } }).$mount() expect(vm.$el.firstChild.value).toBe('ok') expect(vm.$el.lastChild.checked).toBe(false) vm.bar = true waitForUpdate(() => { expect(vm.$el.lastChild.checked).toBe(true) }).then(done) }) it('xlink', done => { const vm = new Vue({ template: '', data: { foo: 'ok' } }).$mount() const xlinkNS = 'http://www.w3.org/1999/xlink' expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('ok') vm.foo = 'again' waitForUpdate(() => { expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('again') vm.foo = null }).then(() => { expect(vm.$el.firstChild.hasAttributeNS(xlinkNS, 'special')).toBe(false) vm.foo = true }).then(() => { expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('true') }).then(done) }) it('enumerated attr', done => { const vm = new Vue({ template: '
hello
', data: { foo: true } }).$mount() expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('true') vm.foo = 'plaintext-only' // allow special values waitForUpdate(() => { expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('plaintext-only') vm.foo = null }).then(() => { expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false') vm.foo = '' }).then(() => { expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('true') vm.foo = false }).then(() => { expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false') vm.foo = 'false' }).then(() => { expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false') }).then(done) }) it('boolean attr', done => { const vm = new Vue({ template: '
hello
', data: { foo: true } }).$mount() expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled') vm.foo = 'again' waitForUpdate(() => { expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled') vm.foo = null }).then(() => { expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(false) vm.foo = '' }).then(() => { expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(true) }).then(done) }) it('.prop modifier', () => { const vm = new Vue({ template: '
', data: { foo: 'hello', bar: 'qux' } }).$mount() expect(vm.$el.children[0].textContent).toBe('hello') expect(vm.$el.children[1].innerHTML).toBe('qux') }) it('.prop modifier with normal attribute binding', () => { const vm = new Vue({ template: '', data: { some: 'hello', id: false } }).$mount() expect(vm.$el.some).toBe('hello') expect(vm.$el.getAttribute('id')).toBe(null) }) if (process.env.VBIND_PROP_SHORTHAND) { it('.prop modifier shorthand', () => { const vm = new Vue({ template: '
', data: { foo: 'hello', bar: 'qux' } }).$mount() expect(vm.$el.children[0].textContent).toBe('hello') expect(vm.$el.children[1].innerHTML).toBe('qux') }) } it('.camel modifier', () => { const vm = new Vue({ template: '', data: { viewBox: '0 0 1 1' } }).$mount() expect(vm.$el.getAttribute('viewBox')).toBe('0 0 1 1') }) it('.sync modifier', done => { const vm = new Vue({ template: ``, data: { bar: 1 }, components: { test: { props: ['fooBar'], template: `
{{ fooBar }}
` } } }).$mount() document.body.appendChild(vm.$el) expect(vm.$el.textContent).toBe('1') triggerEvent(vm.$el, 'click') waitForUpdate(() => { expect(vm.$el.textContent).toBe('2') document.body.removeChild(vm.$el) }).then(done) }) it('.sync modifier with kebab case event', done => { const vm = new Vue({ template: ``, data: { bar: 1 }, components: { test: { props: ['fooBar'], template: `
{{ fooBar }}
`, methods: { update () { this.$emit('update:foo-bar', 2) } } } } }).$mount() expect(vm.$el.textContent).toBe('1') vm.$refs.test.update() waitForUpdate(() => { expect(vm.$el.textContent).toBe('2') }).then(done) }) it('bind object', done => { const vm = new Vue({ template: '', data: { test: { id: 'test', class: 'ok', value: 'hello' } } }).$mount() expect(vm.$el.getAttribute('id')).toBe('test') expect(vm.$el.getAttribute('class')).toBe('ok') expect(vm.$el.value).toBe('hello') vm.test.id = 'hi' vm.test.value = 'bye' waitForUpdate(() => { expect(vm.$el.getAttribute('id')).toBe('hi') expect(vm.$el.getAttribute('class')).toBe('ok') expect(vm.$el.value).toBe('bye') }).then(done) }) it('bind object with explicit overrides', () => { const vm = new Vue({ template: ``, components: { test: { template: '
', props: ['dataFoo', 'dataBar'] } }, data: { test: { dataFoo: 'hi', dataBar: 'bye' } } }).$mount() expect(vm.$el.getAttribute('data-foo')).toBe('foo') expect(vm.$el.getAttribute('data-bar')).toBe('bar') }) it('.sync modifier with bind object', done => { const vm = new Vue({ template: ``, data: { test: { fooBar: 1 } }, components: { test: { props: ['fooBar'], template: `
{{ fooBar }}
`, methods: { handleUpdate () { this.$emit('update:fooBar', 2) } } } } }).$mount() document.body.appendChild(vm.$el) expect(vm.$el.textContent).toBe('1') triggerEvent(vm.$el, 'click') waitForUpdate(() => { expect(vm.$el.textContent).toBe('2') vm.test.fooBar = 3 }).then(() => { expect(vm.$el.textContent).toBe('3') document.body.removeChild(vm.$el) }).then(done) }) it('bind object with overwrite', done => { const vm = new Vue({ template: '', data: { test: { id: 'test', class: 'ok', value: 'hello' } } }).$mount() expect(vm.$el.getAttribute('id')).toBe('foo') expect(vm.$el.getAttribute('class')).toBe('hello') expect(vm.$el.value).toBe('hello') vm.test.id = 'hi' vm.test.value = 'bye' waitForUpdate(() => { expect(vm.$el.getAttribute('id')).toBe('foo') expect(vm.$el.getAttribute('class')).toBe('bye') expect(vm.$el.value).toBe('bye') }).then(done) }) it('bind object with class/style', done => { const vm = new Vue({ template: '', data: { test: { id: 'test', class: ['b', 'c'], style: { fontSize: '12px' } } } }).$mount() expect(vm.$el.id).toBe('test') expect(vm.$el.className).toBe('a b c') expect(vm.$el.style.color).toBe('red') expect(vm.$el.style.fontSize).toBe('12px') vm.test.id = 'hi' vm.test.class = ['d'] vm.test.style = { fontSize: '14px' } waitForUpdate(() => { expect(vm.$el.id).toBe('hi') expect(vm.$el.className).toBe('a d') expect(vm.$el.style.color).toBe('red') expect(vm.$el.style.fontSize).toBe('14px') }).then(done) }) it('bind object as prop', done => { const vm = new Vue({ template: '', data: { test: { id: 'test', className: 'ok', value: 'hello' } } }).$mount() expect(vm.$el.id).toBe('test') expect(vm.$el.className).toBe('ok') expect(vm.$el.value).toBe('hello') vm.test.id = 'hi' vm.test.className = 'okay' vm.test.value = 'bye' waitForUpdate(() => { expect(vm.$el.id).toBe('hi') expect(vm.$el.className).toBe('okay') expect(vm.$el.value).toBe('bye') }).then(done) }) it('bind array', done => { const vm = new Vue({ template: '', data: { test: [ { id: 'test', class: 'ok' }, { value: 'hello' } ] } }).$mount() expect(vm.$el.getAttribute('id')).toBe('test') expect(vm.$el.getAttribute('class')).toBe('ok') expect(vm.$el.value).toBe('hello') vm.test[0].id = 'hi' vm.test[1].value = 'bye' waitForUpdate(() => { expect(vm.$el.getAttribute('id')).toBe('hi') expect(vm.$el.getAttribute('class')).toBe('ok') expect(vm.$el.value).toBe('bye') }).then(done) }) it('warn expect object', () => { new Vue({ template: '', data: { test: 1 } }).$mount() expect('v-bind without argument expects an Object or Array value').toHaveBeenWarned() }) it('set value for option element', () => { const vm = new Vue({ template: '', data: { val: 'val' } }).$mount() // check value attribute expect(vm.$el.options[0].getAttribute('value')).toBe('val') }) // a vdom patch edge case where the user has several un-keyed elements of the // same tag next to each other, and toggling them. it('properly update for toggling un-keyed children', done => { const vm = new Vue({ template: `
`, data: { ok: true } }).$mount() expect(vm.$el.children[0].id).toBe('a') expect(vm.$el.children[0].getAttribute('data-test')).toBe('1') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].id).toBe('b') expect(vm.$el.children[0].getAttribute('data-test')).toBe(null) }).then(done) }) describe('bind object with special attribute', () => { function makeInstance (options) { return new Vue({ template: `
${options.parentTemp}
`, data: { attrs: { [options.attr]: options.value } }, components: { comp: { template: options.childTemp } } }).$mount() } it('key', () => { const vm = makeInstance({ attr: 'key', value: 'test', parentTemp: '
' }) expect(vm._vnode.children[0].key).toBe('test') }) it('ref', () => { const vm = makeInstance({ attr: 'ref', value: 'test', parentTemp: '
' }) expect(vm.$refs.test).toBe(vm.$el.firstChild) }) it('slot', () => { const vm = makeInstance({ attr: 'slot', value: 'test', parentTemp: '123', childTemp: '
slot:
' }) expect(vm.$el.innerHTML).toBe('
slot:123
') }) it('is', () => { const vm = makeInstance({ attr: 'is', value: 'comp', parentTemp: '', childTemp: '
comp
' }) expect(vm.$el.innerHTML).toBe('
comp
') }) }) describe('dynamic arguments', () => { it('basic', done => { const vm = new Vue({ template: `
`, data: { key: 'id', value: 'hello' } }).$mount() expect(vm.$el.id).toBe('hello') vm.key = 'class' waitForUpdate(() => { expect(vm.$el.id).toBe('') expect(vm.$el.className).toBe('hello') // explicit null value vm.key = null }).then(() => { expect(vm.$el.className).toBe('') expect(vm.$el.id).toBe('') vm.key = undefined }).then(() => { expect(`Invalid value for dynamic directive argument`).toHaveBeenWarned() }).then(done) }) it('shorthand', done => { const vm = new Vue({ template: `
`, data: { key: 'id', value: 'hello' } }).$mount() expect(vm.$el.id).toBe('hello') vm.key = 'class' waitForUpdate(() => { expect(vm.$el.className).toBe('hello') }).then(done) }) it('with .prop modifier', done => { const vm = new Vue({ template: `
`, data: { key: 'id', value: 'hello' } }).$mount() expect(vm.$el.id).toBe('hello') vm.key = 'textContent' waitForUpdate(() => { expect(vm.$el.textContent).toBe('hello') }).then(done) }) if (process.env.VBIND_PROP_SHORTHAND) { it('.prop shorthand', done => { const vm = new Vue({ template: `
`, data: { key: 'id', value: 'hello' } }).$mount() expect(vm.$el.id).toBe('hello') vm.key = 'textContent' waitForUpdate(() => { expect(vm.$el.textContent).toBe('hello') }).then(done) }) } it('handle class and style', () => { const vm = new Vue({ template: `
`, data: { key: 'class', value: ['hello', 'world'], key2: 'style', value2: { color: 'red' } } }).$mount() expect(vm.$el.className).toBe('hello world') expect(vm.$el.style.color).toBe('red') }) it('handle shouldUseProp', done => { const vm = new Vue({ template: ``, data: { key: 'value', value: 'foo' } }).$mount() expect(vm.$el.value).toBe('foo') vm.value = 'bar' waitForUpdate(() => { expect(vm.$el.value).toBe('bar') }).then(done) }) it('with .sync modifier', done => { const vm = new Vue({ template: ``, data: { key: 'foo', value: 'bar' }, components: { foo: { props: ['foo'], template: `
{{ foo }}
` } } }).$mount() expect(vm.$el.textContent).toBe('bar') vm.$refs.child.$emit('update:foo', 'baz') waitForUpdate(() => { expect(vm.value).toBe('baz') expect(vm.$el.textContent).toBe('baz') }).then(done) }) }) })