Scoped slots в Vue позволяют передавать данные из дочернего компонента в шаблон родителя через слот, обеспечивая гибкость в отображении. В тестировании компонентов с Vue Test Utils понимание и правильная обработка scoped slots является критически важным для точной проверки поведения компонентов.
Scoped slot — это слот, который предоставляет объект с данными (props) родителю. В компоненте это выглядит так:
<template>
<div>
<slot :user="userData"></slot>
</div>
</template>
<script>
export default {
data() {
return {
userData: { name: 'Alice', age: 30 }
};
}
};
</script>
В родительском компоненте можно использовать эти данные так:
<Child>
<template #default="{ user }">
<p>{{ user.name }} - {{ user.age }}</p>
</template>
</Child>
При тестировании необходимо учитывать, что Vue Test Utils по умолчанию не рендерит содержимое слотов. Чтобы проверять отображение данных, слот нужно явно монтировать с передачей пропсов.
Для тестов используется метод mount или
shallowMount из Vue Test Utils, где слот передается через
опцию slots. При этом scoped slot передается в виде
функции, которая возвращает шаблон или компонент:
import { mount } from '@vue/test-utils';
import Child from '@/components/Child.vue';
test('Scoped slot получает данные', () => {
const wrapper = mount(Child, {
slots: {
default: `<template #default="{ user }">
<span class="username">{{ user.name }}</span>
</template>`
}
});
expect(wrapper.find('.username').text()).toBe('Alice');
});
Важный момент: использование строки в слоте автоматически
компилируется Vue и позволяет использовать объект props,
переданный дочерним компонентом.
Можно передавать scoped slot как функцию, что особенно полезно для динамических значений:
const wrapper = mount(Child, {
slots: {
default: props => `<span class="username">${props.user.name}</span>`
}
});
expect(wrapper.find('.username').text()).toBe('Alice');
Такой подход обеспечивает более точный контроль над передаваемыми props и позволяет проверять поведение компонента при различных данных.
Если scoped slot зависит от реактивных данных, можно тестировать обновления следующим образом:
import { nextTick } from 'vue';
test('Scoped slot реагирует на изменения данных', async () => {
const wrapper = mount(Child, {
slots: {
default: props => `<span class="username">${props.user.name}</span>`
}
});
wrapper.vm.userData.name = 'Bob';
await nextTick();
expect(wrapper.find('.username').text()).toBe('Bob');
});
Использование nextTick() важно для того, чтобы дождаться
обновления DOM после изменения реактивных данных.
Scoped slots могут передавать несколько значений, которые также легко тестировать:
<slot :user="userData" :status="status"></slot>
Тестирование такого слота:
const wrapper = mount(Child, {
slots: {
default: ({ user, status }) => `
<span class="username">${user.name}</span>
<span class="status">${status}</span>
`
}
});
expect(wrapper.find('.username').text()).toBe('Alice');
expect(wrapper.find('.status').text()).toBe('active');
Scoped slots часто используются внутри компонентов с циклической структурой, например, списки:
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item"></slot>
</li>
</ul>
</template>
Для тестирования необходимо обеспечить корректную генерацию слота для каждого элемента:
const items = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
const wrapper = mount(ListComponent, {
props: { items },
slots: {
default: ({ item }) => `<span class="item-name">${item.name}</span>`
}
});
const renderedItems = wrapper.findAll('.item-name').map(n => n.text());
expect(renderedItems).toEqual(['A', 'B']);
shallowMount слоты работают так же,
но дочерние компоненты могут быть замоканы, что влияет на передаваемые
props..text()
родителя без явного монтирования слота.Scoped slots в Vue Test Utils — это инструмент, который требует внимательности при монтировании и проверке. Корректное использование слотов обеспечивает точное тестирование реактивных и динамических компонентов, что критично для надежного покрытия тестами.