Тестирование анимаций и переходов в Vue-компонентах требует понимания
не только API Vue Test Utils, но и внутреннего механизма работы Vue с
transition и transition-group. Важной
особенностью является то, что настоящие анимации в DOM зачастую не нужны
для тестирования логики компонента — достаточно проверки правильного
изменения классов и состояния.
<transition>Компонент <transition> оборачивает элементы и
добавляет специальные CSS-классы в зависимости от состояния
анимации:
v-enter-from / v-enter-tov-leave-from / v-leave-tov-enter-active / v-leave-activeДля тестов важно проверять именно эти классы и их последовательность, а не саму анимацию.
Пример:
import { mount } from '@vue/test-utils'
import Fade from '@/components/Fade.vue'
test('должен применять классы перехода при монтировании', async () => {
const wrapper = mount(Fade, { props: { show: true } })
const el = wrapper.find('div')
expect(el.classes()).toContain('v-enter-from')
await wrapper.vm.$nextTick()
expect(el.classes()).toContain('v-enter-to')
expect(el.classes()).toContain('v-enter-active')
})
В этом примере проверяется, что при монтировании компонент получает правильные классы для анимации появления.
Vue автоматически эмитирует события before-enter,
enter, after-enter, before-leave,
leave, after-leave. Их можно использовать для
проверки логики компонента:
test('вызывает колбэки при завершении анимации', async () => {
const beforeEnter = jest.fn()
const afterEnter = jest.fn()
mount(Fade, {
props: { show: true },
attrs: {
onBeforeEnter: beforeEnter,
onAfterEnter: afterEnter
}
})
expect(beforeEnter).toHaveBeenCalled()
await new Promise(resolve => setTimeout(resolve, 0)) // эмуляция завершения анимации
expect(afterEnter).toHaveBeenCalled()
})
Даже без реальных анимаций можно проверять последовательность вызова этих событий и корректность работы логики, связанной с переходами.
stubVue Test Utils позволяет заменять <transition> на
простой stub, чтобы тесты выполнялись быстрее и без ожидания реальных
анимаций:
const wrapper = mount(Fade, {
global: {
stubs: {
transition: true
}
}
})
expect(wrapper.html()).toContain('<div>Контент</div>')
Такой подход полезен, когда важна проверка рендеринга и условной логики, а не визуальные эффекты.
<transition-group>Компонент <transition-group> используется для
анимации списков. Основная задача теста — проверить правильное
присвоение классов элементам при добавлении и удалении:
import ListWithTransition from '@/components/ListWithTransition.vue'
test('добавление и удаление элементов с правильными классами', async () => {
const wrapper = mount(ListWithTransition, {
props: { items: ['A', 'B'] }
})
wrapper.vm.items.push('C')
await wrapper.vm.$nextTick()
const items = wrapper.findAll('li')
expect(items[2].classes()).toContain('v-enter-from')
wrapper.vm.items.pop()
await wrapper.vm.$nextTick()
expect(items[1].classes()).toContain('v-leave-to')
})
Важно отслеживать не только добавление новых элементов, но и корректное применение классов при удалении, чтобы логика компонента, зависящая от событий анимации, работала стабильно.
Часто <transition> используется вместе с
v-if. В таких случаях тест проверяет, что элемент не
появляется сразу, а проходит через правильные классы:
const wrapper = mount({
template: `
<transition name="fade">
<div v-if="visible">Привет</div>
</transition>
`,
data() { return { visible: false } }
})
expect(wrapper.html()).toBe('')
wrapper.setData({ visible: true })
await wrapper.vm.$nextTick()
expect(wrapper.find('div').classes()).toContain('fade-enter-from')
Это гарантирует, что анимация запускается именно при изменении состояния, а не при инициализации компонента.
Иногда анимации влияют на выполнение колбэков после завершения. Для
таких сценариев полезно мокировать requestAnimationFrame
или таймеры с jest.useFakeTimers():
jest.useFakeTimers()
const wrapper = mount(Fade, { props: { show: true } })
jest.advanceTimersByTime(500) // эмуляция длительности анимации
expect(wrapper.emitted('after-enter')).toBeTruthy()
Этот подход позволяет контролировать длительность анимации и проверять вызовы событий без реального ожидания времени.
stub для ускорения тестов и упрощения
логики.$nextTick.<transition>, так и
<transition-group> для списков.Эти методы обеспечивают стабильность и предсказуемость тестов, позволяя покрывать как визуальные, так и логические аспекты анимаций в Vue.