import Vue from 'vue'
describe('vdom patch: edge cases', () => {
// exposed by #3406
// When a static vnode is inside v-for, it's possible for the same vnode
// to be used in multiple places, and its element will be replaced. This
// causes patch errors when node ops depend on the vnode's element position.
it('should handle static vnodes by key', done => {
const vm = new Vue({
data: {
ok: true
},
template: `
`
}).$mount()
expect(vm.$el.textContent).toBe('abdabd')
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.textContent).toBe('bcdbcd')
}).then(done)
})
// #3533
// a static node ( ) is reused in createElm, which changes its elm reference
// and is inserted into a different parent.
// later when patching the next element a DOM insertion uses it as the
// reference node, causing a parent mismatch.
it('should handle static node edge case when it\'s reused AND used as a reference node for insertion', done => {
const vm = new Vue({
data: {
ok: true
},
template: `
`
}).$mount()
expect(vm.$el.querySelector('.c').textContent).toBe('1')
expect(vm.$el.querySelector('.d').textContent).toBe('2')
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.querySelector('.c').textContent).toBe('1')
expect(vm.$el.querySelector('.d').textContent).toBe('2')
}).then(done)
})
it('should synchronize vm\' vnode', done => {
const comp = {
data: () => ({ swap: true }),
render (h) {
return this.swap
? h('a', 'atag')
: h('span', 'span')
}
}
const wrapper = {
render: h => h('comp'),
components: { comp }
}
const vm = new Vue({
render (h) {
const children = [
h('wrapper'),
h('div', 'row')
]
if (this.swap) {
children.reverse()
}
return h('div', children)
},
data: () => ({ swap: false }),
components: { wrapper }
}).$mount()
expect(vm.$el.innerHTML).toBe('atag row
')
const wrapperVm = vm.$children[0]
const compVm = wrapperVm.$children[0]
vm.swap = true
waitForUpdate(() => {
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
expect(vm.$el.innerHTML).toBe('row
atag ')
vm.swap = false
})
.then(() => {
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
expect(vm.$el.innerHTML).toBe('atag row
')
compVm.swap = false
})
.then(() => {
expect(vm.$el.innerHTML).toBe('span row
')
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
vm.swap = true
})
.then(() => {
expect(vm.$el.innerHTML).toBe('row
span ')
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
vm.swap = true
})
.then(done)
})
// #4530
it('should not reset value when patching between dynamic/static bindings', done => {
const vm = new Vue({
data: { ok: true },
template: `
`
}).$mount()
expect(vm.$el.children[0].value).toBe('a')
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.children[0].value).toBe('b')
vm.ok = true
}).then(() => {
expect(vm.$el.children[0].value).toBe('a')
}).then(done)
})
// #6313
it('should not replace node when switching between text-like inputs', done => {
const vm = new Vue({
data: { show: false },
template: `
`
}).$mount()
const node = vm.$el.children[0]
expect(vm.$el.children[0].type).toBe('password')
vm.$el.children[0].value = 'test'
vm.show = true
waitForUpdate(() => {
expect(vm.$el.children[0]).toBe(node)
expect(vm.$el.children[0].value).toBe('test')
expect(vm.$el.children[0].type).toBe('text')
vm.show = false
}).then(() => {
expect(vm.$el.children[0]).toBe(node)
expect(vm.$el.children[0].value).toBe('test')
expect(vm.$el.children[0].type).toBe('password')
}).then(done)
})
it('should properly patch nested HOC when root element is replaced', done => {
const vm = new Vue({
template: ` `,
components: {
foo: {
template: ` `,
components: {
bar: {
template: `
`,
data () {
return { ok: true }
}
}
}
}
}
}).$mount()
expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('DIV')
expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`)
vm.$refs.foo.$refs.bar.ok = false
waitForUpdate(() => {
expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('SPAN')
expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`)
}).then(done)
})
// #6790
it('should not render undefined for empty nested arrays', () => {
const vm = new Vue({
template: `
`,
data: { emptyArr: [] }
}).$mount()
expect(vm.$el.textContent).toBe('')
})
// #6803
it('backwards compat with checkbox code generated before 2.4', () => {
const spy = jasmine.createSpy()
const vm = new Vue({
data: {
label: 'foobar',
name: 'foobar'
},
computed: {
value: {
get () {
return 1
},
set: spy
}
},
render (h) {
const _vm = this
return h('div', {},
[h('input', {
directives: [{
name: 'model',
rawName: 'v-model',
value: (_vm.value),
expression: 'value'
}],
attrs: {
'type': 'radio',
'name': _vm.name
},
domProps: {
'value': _vm.label,
'checked': _vm._q(_vm.value, _vm.label)
},
on: {
'__c': function ($event) {
_vm.value = _vm.label
}
}
})])
}
}).$mount()
document.body.appendChild(vm.$el)
vm.$el.children[0].click()
expect(spy).toHaveBeenCalled()
})
})