Slots
Vue Test Utils provides some useful features for testing components using slots.
A Simple Example
You might have a generic <layout> component that uses a default slot to render some content. For example:
const Layout = { template: ` <div> <h1>Welcome!</h1> <main> <slot /> </main> <footer> Thanks for visiting. </footer> </div> ` }You might want to write a test to ensure the default slot content is rendered. VTU provides the slots mounting option for this purpose:
test('layout default slot', () => { const wrapper = mount(Layout, { slots: { default: 'Main Content' } }) expect(wrapper.html()).toContain('Main Content') })It passes! In this example, we are passing some text content to the default slot. If you want to be even more specific, and verify the default slot content is rendered inside <main>, you could change the assertion:
test('layout default slot', () => { const wrapper = mount(Layout, { slots: { default: 'Main Content' } }) expect(wrapper.find('main').text()).toContain('Main Content') })Named Slots
You may have more complex <layout> component with some named slots. For example:
const Layout = { template: ` <div> <header> <slot name="header" /> </header> <main> <slot name="main" /> </main> <footer> <slot name="footer" /> </footer> </div> ` }VTU also supports this. You can write a test as follows. Note that in this example we are passing HTML instead of text content to the slots.
test('layout full page layout', () => { const wrapper = mount(Layout, { slots: { header: '<div>Header</div>', main: '<div>Main Content</div>', footer: '<div>Footer</div>' } }) expect(wrapper.html()).toContain('<div>Header</div>') expect(wrapper.html()).toContain('<div>Main Content</div>') expect(wrapper.html()).toContain('<div>Footer</div>') })Multiple Slots
You can pass an array of slots, too:
test('layout full page layout', () => { const wrapper = mount(Layout, { slots: { default: [ '<div id="one">One</div>', '<div id="two">Two</div>' ] } }) expect(wrapper.find('#one').exists()).toBe(true) expect(wrapper.find('#two').exists()).toBe(true) })Advanced Usage
You can also pass a render function, an object with template or even an SFC imported from a vue file to a slot mounting option:
import { h } from 'vue' import Header from './Header.vue' test('layout full page layout', () => { const wrapper = mount(Layout, { slots: { header: Header, main: h('div', 'Main Content'), sidebar: { template: '<div>Sidebar</div>' }, footer: '<div>Footer</div>' } }) expect(wrapper.html()).toContain('<div>Header</div>') expect(wrapper.html()).toContain('<div>Main Content</div>') expect(wrapper.html()).toContain('<div>Footer</div>') })Refer to the tests for more examples and use cases.
Scoped Slots
Scoped slots and bindings are also supported.
const ComponentWithSlots = { template: ` <div class="scoped"> <slot name="scoped" v-bind="{ msg }" /> </div> `, data() { return { msg: 'world' } } } test('scoped slots', () => { const wrapper = mount(ComponentWithSlots, { slots: { scoped: `<template #scoped="scope"> Hello {{ scope.msg }} </template> ` } }) expect(wrapper.html()).toContain('Hello world') })When using string templates for slot content, if not explicitly defined using a wrapping <template #scoped="scopeVar"> tag, slot scope becomes available as a params object when the slot is evaluated.
test('scoped slots', () => { const wrapper = mount(ComponentWithSlots, { slots: { scoped: `Hello {{ params.msg }}` // no wrapping template tag provided, slot scope exposed as "params" } }) expect(wrapper.html()).toContain('Hello world') })Conclusion
- Use the
slotsmounting option to test components using<slot>are rendering content correctly. - Content can either be a string, a render function or an imported SFC.
- Use
defaultfor the default slot, and the correct name for a named slots. - scoped slots and the
#shorthand is also supported.