Аргументы и модификаторы директив

Аргументы директив в Vue используются для передачи дополнительного контекста директиве. Классические примеры — v-on:click, v-bind:class, а также пользовательские директивы с аргументами (v-focus:delay).

В Vue Test Utils аргументы директив не тестируются напрямую как синтаксис шаблона, а проверяются через поведение компонента или через хуки пользовательской директивы, куда эти аргументы попадают.

Проверка стандартных директив с аргументами

Для встроенных директив (v-on, v-bind) аргумент отражается в итоговом DOM.

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

const wrapper = mount(Component)

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

В данном случае аргумент click директивы v-on проверяется через вызов обработчика и изменение состояния.

Пример с v-bind:class:

<template>
  <div :class="status"></div>
</template>
const wrapper = mount(Component, {
  data() {
    return { status: 'active' }
  }
})

expect(wrapper.find('div').classes()).toContain('active')

Аргумент class проверяется через итоговое значение DOM-атрибутов.

Динамические аргументы

Vue поддерживает динамические аргументы:

<div v-bind:[attrName]="value"></div>

Тестирование сводится к проверке итогового атрибута:

const wrapper = mount(Component, {
  data() {
    return {
      attrName: 'data-test',
      value: 'example'
    }
  }
})

expect(wrapper.find('div').attributes('data-test')).toBe('example')

Пользовательские директивы с аргументами

Пользовательские директивы получают аргумент в объекте binding.

app.directive('example', {
  mounted(el, binding) {
    // binding.arg
  }
})

В тестах Vue Test Utils позволяет регистрировать директивы через global.directives.

const directiveMock = {
  mounted: vi.fn()
}

const wrapper = mount(Component, {
  global: {
    directives: {
      example: directiveMock
    }
  }
})

Если директива используется как:

<div v-example:color="'red'"></div>

Аргумент проверяется через вызов хука:

expect(directiveMock.mounted).toHaveBeenCalled()

const binding = directiveMock.mounted.mock.calls[0][1]
expect(binding.arg).toBe('color')
expect(binding.value).toBe('red')

Модификаторы директив

Модификаторы — это булевые флаги, добавляемые к директиве через точку:

<input v-model.lazy.trim />

Во всех директивах модификаторы попадают в binding.modifiers.

Проверка модификаторов пользовательских директив

<div v-example.once.prevent></div>
const directiveMock = {
  mounted: vi.fn()
}

mount(Component, {
  global: {
    directives: {
      example: directiveMock
    }
  }
})

const binding = directiveMock.mounted.mock.calls[0][1]

expect(binding.modifiers.once).toBe(true)
expect(binding.modifiers.prevent).toBe(true)

Модификаторы отсутствуют в DOM, поэтому тестирование всегда осуществляется через объект binding.

Модификаторы встроенных директив

v-model

Модификаторы lazy, trim, number влияют на момент и формат обновления значения.

<input v-model.trim="text" />
const wrapper = mount(Component)

const input = wrapper.find('input')
await input.setValue('  hello  ')

expect(wrapper.vm.text).toBe('hello')

Для lazy:

<input v-model.lazy="text" />
await input.setValue('test')
expect(wrapper.vm.text).toBe('')

await input.trigger('change')
expect(wrapper.vm.text).toBe('test')

v-on

Модификаторы prevent, stop, once, capture влияют на поведение событий.

<button @click.prevent="onClick"></button>
const onCl ick = vi.fn()

const wrapper = mount(Component, {
  methods: { onClick }
})

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

expect(onClick).toHaveBeenCalled()

Для once:

await button.trigger('click')
await button.trigger('click')

expect(onClick).toHaveBeenCalledTimes(1)

Vue Test Utils эмулирует поведение браузера, поэтому эффект модификаторов проявляется без дополнительной настройки.

Комбинация аргументов и модификаторов

Пользовательская директива может одновременно использовать аргументы и модификаторы:

<div v-tooltip:top.delay></div>

Тестирование:

const directiveMock = {
  mounted: vi.fn()
}

mount(Component, {
  global: {
    directives: {
      tooltip: directiveMock
    }
  }
})

const binding = directiveMock.mounted.mock.calls[0][1]

expect(binding.arg).toBe('top')
expect(binding.modifiers.delay).toBe(true)

Обновление аргументов и модификаторов

При изменении аргументов или значений директивы вызывается хук updated.

const directiveMock = {
  mounted: vi.fn(),
  updated: vi.fn()
}

const wrapper = mount(Component, {
  global: {
    directives: {
      example: directiveMock
    }
  }
})

await wrapper.setData({ value: 'new' })

expect(directiveMock.updated).toHaveBeenCalled()

В updated аргументы и модификаторы остаются доступны через binding.

Изоляция директив в тестах

При тестировании компонентов с директивами логика директивы часто не важна. В таких случаях директивы подменяются заглушками.

mount(Component, {
  global: {
    directives: {
      tooltip: {}
    }
  }
})

Это позволяет сосредоточиться на проверке аргументов, модификаторов и их влияния на состояние компонента, не затрагивая реальную реализацию директивы.

Ключевые особенности тестирования директив

  • аргументы директив проверяются через binding.arg или итоговое DOM-состояние
  • модификаторы всегда находятся в binding.modifiers
  • стандартные директивы тестируются через поведение компонента
  • пользовательские директивы удобно тестировать через global.directives
  • комбинации аргументов и модификаторов не требуют специальных настроек

Тестирование директив в Vue Test Utils строится вокруг наблюдаемого эффекта и жизненного цикла директивы, а не вокруг синтаксиса шаблона.