import Vue from 'vue'
describe('Component', () => {
it('static', () => {
const vm = new Vue({
template: '',
components: {
test: {
data() {
return { a: 123 }
},
template: '{{a}}'
}
}
}).$mount()
expect(vm.$el.tagName).toBe('SPAN')
expect(vm.$el.innerHTML).toBe('123')
})
it('using component in restricted elements', () => {
const vm = new Vue({
template: '
',
components: {
test: {
data() {
return { a: 123 }
},
template: '| {{a}} |
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe(
''
)
})
it('"is" attribute', () => {
const vm = new Vue({
template: '',
components: {
test: {
data() {
return { a: 123 }
},
template: '| {{a}} |
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe(
''
)
})
it('inline-template', () => {
const vm = new Vue({
template: '{{a}}
',
data: {
a: 'parent'
},
components: {
test: {
data() {
return { a: 'child' }
}
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('child')
})
it('fragment instance warning', () => {
new Vue({
template: '',
components: {
test: {
data() {
return { a: 123, b: 234 }
},
template: '{{a}}
{{b}}
'
}
}
}).$mount()
expect(
'Component template should contain exactly one root element'
).toHaveBeenWarned()
})
it('dynamic', done => {
const vm = new Vue({
template: '',
data: {
view: 'view-a'
},
components: {
'view-a': {
template: 'foo {{view}}
',
data() {
return { view: 'a' }
}
},
'view-b': {
template: 'bar {{view}}
',
data() {
return { view: 'b' }
}
}
}
}).$mount()
expect(vm.$el.outerHTML).toBe('foo a
')
vm.view = 'view-b'
waitForUpdate(() => {
expect(vm.$el.outerHTML).toBe('bar b
')
vm.view = ''
})
.then(() => {
expect(vm.$el.nodeType).toBe(8)
expect(vm.$el.data).toBe('')
})
.then(done)
})
it('dynamic with props', done => {
const vm = new Vue({
template: '',
data: {
view: 'view-a'
},
components: {
'view-a': {
template: 'foo {{view}}
',
props: ['view']
},
'view-b': {
template: 'bar {{view}}
',
props: ['view']
}
}
}).$mount()
expect(vm.$el.outerHTML).toBe('foo view-a
')
vm.view = 'view-b'
waitForUpdate(() => {
expect(vm.$el.outerHTML).toBe('bar view-b
')
vm.view = ''
})
.then(() => {
expect(vm.$el.nodeType).toBe(8)
expect(vm.$el.data).toBe('')
})
.then(done)
})
it(':is using raw component constructor', () => {
const vm = new Vue({
template:
'' +
'' +
'' +
'
',
components: {
test: {
template: 'foo'
},
async: function (resolve) {
resolve({
template: 'bar'
})
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('foobar')
})
it('dynamic combined with v-for', done => {
const vm = new Vue({
template:
'' +
'' +
'
',
data: {
comps: [{ type: 'one' }, { type: 'two' }]
},
components: {
one: {
template: 'one'
},
two: {
template: 'two'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('onetwo')
vm.comps[1].type = 'one'
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe('oneone')
}).then(done)
})
it('dynamic elements with domProps', done => {
const vm = new Vue({
template: '',
data: {
view: 'input',
val: 'hello'
}
}).$mount()
expect(vm.$el.tagName).toBe('INPUT')
expect(vm.$el.value).toBe('hello')
vm.view = 'textarea'
vm.val += ' world'
waitForUpdate(() => {
expect(vm.$el.tagName).toBe('TEXTAREA')
expect(vm.$el.value).toBe('hello world')
vm.view = ''
}).then(done)
})
it('should compile parent template directives & content in parent scope', done => {
const vm = new Vue({
data: {
ok: false,
message: 'hello'
},
template: '{{message}}',
components: {
test: {
template: ' {{message}}
',
data() {
return {
message: 'world'
}
}
}
}
}).$mount()
expect(vm.$el.style.display).toBe('none')
expect(vm.$el.textContent).toBe('hello world')
vm.ok = true
vm.message = 'bye'
waitForUpdate(() => {
expect(vm.$el.style.display).toBe('')
expect(vm.$el.textContent).toBe('bye world')
}).then(done)
})
it('parent content + v-if', done => {
const vm = new Vue({
data: {
ok: false,
message: 'hello'
},
template: '{{message}}',
components: {
test: {
template: ' {{message}}
',
data() {
return {
message: 'world'
}
}
}
}
}).$mount()
expect(vm.$el.textContent).toBe('')
expect(vm.$children.length).toBe(0)
vm.ok = true
waitForUpdate(() => {
expect(vm.$children.length).toBe(1)
expect(vm.$el.textContent).toBe('hello world')
}).then(done)
})
it('props', () => {
const vm = new Vue({
data: {
list: [{ a: 1 }, { a: 2 }]
},
template: '',
components: {
test: {
template: '',
props: ['collection']
}
}
}).$mount()
expect(vm.$el.outerHTML).toBe('')
})
it('should warn when using camelCased props in in-DOM template', () => {
new Vue({
data: {
list: [{ a: 1 }, { a: 2 }]
},
template: '', // <-- simulate lowercased template
components: {
test: {
template:
'',
props: ['someCollection']
}
}
}).$mount()
expect(
'You should probably use "some-collection" instead of "someCollection".'
).toHaveBeenTipped()
})
it('should warn when using camelCased events in in-DOM template', () => {
new Vue({
template: '', // <-- simulate lowercased template
components: {
test: {
template: '',
created() {
this.$emit('fooBar')
}
}
}
}).$mount()
expect(
'You should probably use "foo-bar" instead of "fooBar".'
).toHaveBeenTipped()
})
it('not found component should not throw', () => {
expect(function () {
new Vue({
template: ''
})
}).not.toThrow()
})
it('properly update replaced higher-order component root node', done => {
const vm = new Vue({
data: {
color: 'red'
},
template: '',
components: {
test: {
data() {
return { tag: 'div' }
},
render(h) {
return h(this.tag, { class: 'test' }, 'hi')
}
}
}
}).$mount()
expect(vm.$el.tagName).toBe('DIV')
expect(vm.$el.id).toBe('foo')
expect(vm.$el.className).toBe('test red')
vm.color = 'green'
waitForUpdate(() => {
expect(vm.$el.tagName).toBe('DIV')
expect(vm.$el.id).toBe('foo')
expect(vm.$el.className).toBe('test green')
vm.$children[0].tag = 'p'
})
.then(() => {
expect(vm.$el.tagName).toBe('P')
expect(vm.$el.id).toBe('foo')
expect(vm.$el.className).toBe('test green')
vm.color = 'red'
})
.then(() => {
expect(vm.$el.tagName).toBe('P')
expect(vm.$el.id).toBe('foo')
expect(vm.$el.className).toBe('test red')
})
.then(done)
})
it('catch component render error and preserve previous vnode', done => {
const spy = vi.fn()
Vue.config.errorHandler = spy
const vm = new Vue({
data: {
a: {
b: 123
}
},
render(h) {
return h('div', [this.a.b])
}
}).$mount()
expect(vm.$el.textContent).toBe('123')
expect(spy).not.toHaveBeenCalled()
vm.a = null
waitForUpdate(() => {
expect(spy).toHaveBeenCalled()
expect(vm.$el.textContent).toBe('123') // should preserve rendered DOM
vm.a = { b: 234 }
})
.then(() => {
expect(vm.$el.textContent).toBe('234') // should be able to recover
Vue.config.errorHandler = undefined
})
.then(done)
})
it('relocates node without error', done => {
const el = document.createElement('div')
document.body.appendChild(el)
const target = document.createElement('div')
document.body.appendChild(target)
const Test = {
render(h) {
return h('div', { class: 'test' }, this.$slots.default)
},
mounted() {
target.appendChild(this.$el)
},
beforeDestroy() {
const parent = this.$el.parentNode
if (parent) {
parent.removeChild(this.$el)
}
}
}
const vm = new Vue({
data() {
return {
view: true
}
},
template: `Test
`,
components: {
test: Test
}
}).$mount(el)
expect(el.outerHTML).toBe('')
expect(target.outerHTML).toBe('')
vm.view = false
waitForUpdate(() => {
expect(el.outerHTML).toBe('')
expect(target.outerHTML).toBe('')
vm.$destroy()
}).then(done)
})
it('render vnode with '
}
}
}).$mount()
expect(vm.$el.nodeName).toBe('#comment')
expect(
'Templates should only be responsible for mapping the state'
).toHaveBeenWarned()
})
})