import Vue from 'vue'
describe('Component slot', () => {
let vm, child
function mount (options) {
vm = new Vue({
data: {
msg: 'parent message'
},
template: `
${options.parentContent || ''}
`,
components: {
test: {
template: options.childTemplate,
data () {
return {
msg: 'child message'
}
}
}
}
}).$mount()
child = vm.$children[0]
}
it('no content', () => {
mount({
childTemplate: '
'
})
expect(child.$el.childNodes.length).toBe(0)
})
it('default content', done => {
mount({
childTemplate: '
',
parentContent: '{{ msg }}
'
})
expect(child.$el.tagName).toBe('DIV')
expect(child.$el.children[0].tagName).toBe('P')
expect(child.$el.children[0].textContent).toBe('parent message')
vm.msg = 'changed'
waitForUpdate(() => {
expect(child.$el.children[0].textContent).toBe('changed')
}).then(done)
})
it('fallback content', () => {
mount({
childTemplate: ''
})
expect(child.$el.children[0].tagName).toBe('P')
expect(child.$el.textContent).toBe('child message')
})
it('fallback content with multiple named slots', () => {
mount({
childTemplate: `
`,
parentContent: 'slot b
'
})
expect(child.$el.childNodes.length).toBe(2)
expect(child.$el.firstChild.textContent).toBe('fallback a')
expect(child.$el.lastChild.textContent).toBe('slot b')
})
it('fallback content with mixed named/unamed slots', () => {
mount({
childTemplate: `
`,
parentContent: 'slot b
'
})
expect(child.$el.childNodes.length).toBe(2)
expect(child.$el.firstChild.textContent).toBe('fallback a')
expect(child.$el.lastChild.textContent).toBe('slot b')
})
it('selector matching multiple elements', () => {
mount({
childTemplate: '
',
parentContent: '1
2
'
})
expect(child.$el.innerHTML).toBe('1
2
')
})
it('default content should only render parts not selected', () => {
mount({
childTemplate: `
`,
parentContent: 'foo
1
2
'
})
expect(child.$el.innerHTML).toBe('1
foo
2
')
})
it('name should only match children', function () {
mount({
childTemplate: `
fallback a
fallback b
fallback c
`,
parentContent: `
'select b
'nested b
'nested c
`
})
expect(child.$el.childNodes.length).toBe(3)
expect(child.$el.firstChild.textContent).toBe('fallback a')
expect(child.$el.childNodes[1].textContent).toBe('select b')
expect(child.$el.lastChild.textContent).toBe('fallback c')
})
it('should accept expressions in slot attribute and slot names', () => {
mount({
childTemplate: `
`,
parentContent: `one
two
`
})
expect(child.$el.innerHTML).toBe('two
')
})
it('slot inside v-if', done => {
const vm = new Vue({
data: {
a: 1,
b: 2,
show: true
},
template: '{{b}}
{{a}}
',
components: {
test: {
props: ['show'],
template: '
'
}
}
}).$mount()
expect(vm.$el.textContent).toBe('12')
vm.a = 2
waitForUpdate(() => {
expect(vm.$el.textContent).toBe('22')
vm.show = false
}).then(() => {
expect(vm.$el.textContent).toBe('')
vm.show = true
vm.a = 3
}).then(() => {
expect(vm.$el.textContent).toBe('32')
}).then(done)
})
it('slot inside v-for', () => {
mount({
childTemplate: '
',
parentContent: '{{ i - 1 }}
'
})
expect(child.$el.innerHTML).toBe('0
1
2
')
})
it('nested slots', done => {
const vm = new Vue({
template: '{{ msg }}
',
data: {
msg: 'foo'
},
components: {
test: {
template: '
'
},
test2: {
template: '
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('')
vm.msg = 'bar'
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe('')
}).then(done)
})
it('v-if on inserted content', done => {
const vm = new Vue({
template: '{{ msg }}
',
data: {
ok: true,
msg: 'hi'
},
components: {
test: {
template: 'fallback
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('hi
')
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe('fallback')
vm.ok = true
vm.msg = 'bye'
}).then(() => {
expect(vm.$el.innerHTML).toBe('bye
')
}).then(done)
})
it('template slot', function () {
const vm = new Vue({
template: 'hello',
components: {
test: {
template: ' world
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('hello world')
})
it('combined with v-for', () => {
const vm = new Vue({
template: '{{ i }}
',
components: {
test: {
template: '
'
}
}
}).$mount()
expect(vm.$el.innerHTML).toBe('1
2
3
')
})
it('inside template v-if', () => {
mount({
childTemplate: `
`,
parentContent: 'foo'
})
expect(child.$el.innerHTML).toBe('foo')
})
it('default slot should use fallback content if has only whitespace', () => {
Vue.config.preserveWhitespace = true
mount({
childTemplate: `
first slot
this is the default slot
second named slot
`,
parentContent: `1
2
`
})
expect(child.$el.innerHTML).toBe(
'1
this is the default slot
2
'
)
Vue.config.preserveWhitespace = false
})
it('programmatic access to $slots', () => {
const vm = new Vue({
template: 'A
C
B
',
components: {
test: {
render () {
expect(this.$slots.a.length).toBe(1)
expect(this.$slots.a[0].tag).toBe('p')
expect(this.$slots.a[0].children.length).toBe(1)
expect(this.$slots.a[0].children[0].text).toBe('A')
expect(this.$slots.b.length).toBe(1)
expect(this.$slots.b[0].tag).toBe('p')
expect(this.$slots.b[0].children.length).toBe(1)
expect(this.$slots.b[0].children[0].text).toBe('B')
expect(this.$slots.default.length).toBe(1)
expect(this.$slots.default[0].tag).toBe('div')
expect(this.$slots.default[0].children.length).toBe(1)
expect(this.$slots.default[0].children[0].text).toBe('C')
return this.$slots.default[0]
}
}
}
}).$mount()
expect(vm.$el.tagName).toBe('DIV')
expect(vm.$el.textContent).toBe('C')
})
it('warn if user directly returns array', () => {
new Vue({
template: '',
components: {
test: {
render () {
return this.$slots.default
}
}
}
}).$mount()
expect('Render function should return a single root node').toHaveBeenWarned()
})
})