Обработка глобальных событий

В Vue.js глобальные события часто используются для взаимодействия компонентов между собой, особенно при работе с событийной шиной (event bus) или через родительские/дочерние компоненты. Для корректного тестирования таких взаимодействий в Vue Test Utils предусмотрены инструменты для имитации, прослушивания и проверки глобальных событий.


Регистрация глобальных событий

Vue Test Utils позволяет подключать глобальные плагины, миксины и компоненты через объект global при создании экземпляра компонента:

import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'

const wrapper = mount(MyComponent, {
  global: {
    plugins: [myEventBusPlugin],
    mixins: [myGlobalMixin]
  }
})
  • plugins — позволяют подключить сторонние или собственные шины событий.
  • mixins — глобальные миксины, содержащие обработку событий.
  • Глобальные компоненты или директивы также регистрируются через global.components и global.directives.

Это важно для тестирования, если компонент ожидает глобальные обработчики событий или методы, которые обычно доступны во Vue-приложении.


Прослушивание событий через $emit и $on

Для проверки реакций на события используется комбинация методов Vue Test Utils и нативных Vue-свойств:

wrapper.vm.$emit('custom-event', { payload: 123 })
expect(wrapper.emitted('custom-event')).toBeTruthy()
expect(wrapper.emitted('custom-event')[0]).toEqual([{ payload: 123 }])
  • Метод $emit позволяет программно вызвать событие на экземпляре компонента.
  • Метод emitted() возвращает объект всех вызванных событий, что позволяет проверять наличие события, количество вызовов и переданные данные.

Для глобальных событий через шину (event bus) может применяться мок:

const eventBus = {
  $on: jest.fn(),
  $emit: jest.fn(),
  $off: jest.fn()
}

const wrapper = mount(MyComponent, {
  global: {
    provide: {
      eventBus
    }
  }
})

wrapper.vm.$root.$emit('global-event', 42)
expect(eventBus.$emit).toHaveBeenCalledWith('global-event', 42)
  • Использование мока позволяет отслеживать вызовы глобальных событий без создания реального экземпляра шины.
  • provide/inject применяется для передачи глобальных зависимостей и обработки событий между компонентами.

Асинхронные глобальные события

В Vue 3 события часто вызываются в асинхронных обработчиках:

await wrapper.vm.$nextTick()
wrapper.vm.$emit('async-event', 'data')
await wrapper.vm.$nextTick()

expect(wrapper.emitted('async-event')[0]).toEqual(['data'])
  • $nextTick() гарантирует, что Vue завершил реактивные обновления перед проверкой.
  • Особенно важно при тестировании событий, инициируемых изменениями состояния (watch, computed).

Тестирование событий на DOM-элементах

Не все глобальные события проходят через $emit. События DOM (например, click, input) обрабатываются через trigger:

const button = wrapper.find('button')
await button.trigger('click')

expect(wrapper.emitted('button-click')).toBeTruthy()
  • trigger имитирует нативное событие на элементе.
  • Позволяет проверять, что глобальные обработчики событий, привязанные через директивы (v-on) или плагины, корректно реагируют.

Проверка удаления слушателей событий

Правильное тестирование глобальных событий включает проверку их отписки при размонтировании компонента:

const wrapper = mount(MyComponent, {
  global: {
    provide: {
      eventBus
    }
  }
})

wrapper.unmount()
expect(eventBus.$off).toHaveBeenCalled()
  • Удаление слушателей предотвращает утечки памяти и нежелательные побочные эффекты в приложении.
  • В Vue Test Utils unmount() вызывает метод beforeUnmount и завершает жизненный цикл компонента.

Рекомендации при тестировании глобальных событий

  1. Использовать моки для глобальных штук, чтобы тесты оставались изолированными.
  2. Проверять не только факт вызова события, но и переданные данные и количество вызовов.
  3. Асинхронные события тестировать с $nextTick() или flushPromises при промисах.
  4. Следить за отпиской слушателей при размонтировании компонента.
  5. Избегать прямой зависимости от реальной шины событий — моки упрощают поддержку и читаемость тестов.

Пример комплексного теста глобальных событий

import { mount } from '@vue/test-utils'
import ComponentA from '@/components/ComponentA.vue'

const mockBus = {
  $on: jest.fn(),
  $emit: jest.fn(),
  $off: jest.fn()
}

describe('ComponentA - глобальные события', () => {
  let wrapper

  beforeEach(() => {
    wrapper = mount(ComponentA, {
      global: { provide: { eventBus: mockBus } }
    })
  })

  afterEach(() => {
    wrapper.unmount()
  })

  test('вызывает глобальное событие при действии', async () => {
    await wrapper.find('button').trigger('click')
    expect(mockBus.$emit).toHaveBeenCalledWith('global-event', expect.anything())
  })

  test('подписка и отписка от событий', () => {
    expect(mockBus.$on).toHaveBeenCalledWith('another-event', expect.any(Function))
    wrapper.unmount()
    expect(mockBus.$off).toHaveBeenCalled()
  })
})
  • Пример демонстрирует тестирование подписки, эмита, реакции на DOM-события и корректного завершения жизненного цикла.
  • Такой подход обеспечивает надежность тестов и изоляцию от глобального состояния приложения.