Мокирование пользовательских действий

При тестировании компонентов, использующих AutoNumeric, ключевой сложностью становится необходимость имитации реального поведения пользователя в браузере. Библиотека работает на стыке DOM-событий, форматирования строкового представления числа и внутреннего состояния поля ввода, поэтому простого вызова value = ... недостаточно для воспроизведения реальных сценариев.

Мокирование пользовательских действий в данном контексте означает генерацию последовательности событий, которые браузер обычно формирует при взаимодействии с полем ввода: клавиатурный ввод, вставка из буфера обмена, потеря/получение фокуса, программные изменения значения, а также события композиции текста.

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


Модель событий, с которыми работает AutoNumeric

AutoNumeric опирается на стандартные DOM-события:

  • keydown / keyup
  • keypress (в устаревших сценариях)
  • input
  • beforeinput
  • paste
  • focus / blur
  • compositionstart / compositionupdate / compositionend

При тестировании важно учитывать, что внутренние алгоритмы библиотеки часто завязаны на input и beforeinput, а остальные события используются для контекстной синхронизации состояния.

Мокирование должно учитывать не только факт вызова события, но и его свойства:

  • event.target.value
  • event.data (для InputEvent)
  • event.inputType (например, insertText, deleteContentBackward)
  • event.clipboardData (при вставке)
  • клавишные коды (key, code, keyCode в старых тестах)

Базовая стратегия генерации DOM-событий

В тестовой среде чаще всего используется программное создание событий через dispatchEvent.

Пример базового подхода:

const input = document.createElement('input');
document.body.appendChild(input);

const event = new InputEvent('input', {
  bubbles: true,
  cancelable: true,
  data: '1234',
  inputType: 'insertText'
});

input.value = '1234';
input.dispatchEvent(event);

Однако для AutoNumeric такой упрощённый вариант часто недостаточен, поскольку библиотека может перехватывать ввод до изменения value или ожидать согласованности между inputType и фактическим содержимым поля.


Эмуляция клавиатурного ввода

Клавиатурный ввод остаётся одним из наиболее сложных сценариев мокирования, так как включает несколько последовательных событий.

Типичная последовательность:

  1. keydown
  2. (опционально) beforeinput
  3. изменение value
  4. input
  5. keyup

Пример имитации:

function simulateKey(input, key) {
  input.dispatchEvent(new KeyboardEvent('keydown', {
    key,
    bubbles: true
  }));

  input.value += key;

  input.dispatchEvent(new InputEvent('input', {
    data: key,
    inputType: 'insertText',
    bubbles: true
  }));

  input.dispatchEvent(new KeyboardEvent('keyup', {
    key,
    bubbles: true
  }));
}

Для AutoNumeric важно, что изменение value вручную часто должно происходить до или синхронно с input, иначе внутренняя логика форматирования может не сработать корректно.


Мокирование удаления символов

Удаление символов требует иной семантики событий:

input.dispatchEvent(new KeyboardEvent('keydown', {
  key: 'Backspace',
  bubbles: true
}));

input.value = input.value.slice(0, -1);

input.dispatchEvent(new InputEvent('input', {
  inputType: 'deleteContentBackward',
  data: null,
  bubbles: true
}));

AutoNumeric использует тип удаления для корректного пересчёта форматированного значения, особенно при работе с разделителями тысяч и десятичными знаками.


Имитация вставки из буфера обмена

Вставка данных является отдельным сценарием, так как включает ClipboardEvent.

const pasteEvent = new ClipboardEvent('paste', {
  bubbles: true,
  clipboardData: new DataTransfer()
});

pasteEvent.clipboardData.setData('text/plain', '12,345.67');

input.dispatchEvent(pasteEvent);

После события paste AutoNumeric обычно инициирует переразбор строки, поэтому тестовая среда должна либо:

  • позволить библиотеке самостоятельно изменить value
  • либо вручную синхронизировать значение после события

Фокус и потеря фокуса

Сценарии focus и blur критичны для проверки форматирования при выходе из поля.

AutoNumeric часто переключает отображение между “сырым” и “форматированным” состоянием именно на этих событиях.

input.dispatchEvent(new FocusEvent('focus', {
  bubbles: true
}));

input.value = '1234.5';

input.dispatchEvent(new FocusEvent('blur', {
  bubbles: true
}));

При blur часто активируется финальное форматирование, включая округление, добавление разделителей и приведение к заданной локали.


Имитация композиционного ввода (IME)

Для языков с составным вводом (например, азиатские раскладки) используются события композиции:

input.dispatchEvent(new CompositionEvent('compositionstart', {
  bubbles: true
}));

input.value = '123';

input.dispatchEvent(new CompositionEvent('compositionend', {
  data: '123',
  bubbles: true
}));

input.dispatchEvent(new InputEvent('input', {
  data: '123',
  inputType: 'insertCompositionText',
  bubbles: true
}));

AutoNumeric может временно отключать форматирование во время композиции, чтобы не мешать вводу символов, поэтому корректная последовательность событий критична.


Мокирование через Jest и jsdom

В средах вроде Jest используется jsdom, который не полностью воспроизводит поведение браузера. Это требует дополнительного контроля над состоянием DOM.

Часто применяется явное переопределение value и ручной вызов событий:

input.value = '1000';
input.dispatchEvent(new Event('input', { bubbles: true }));

Для более точного контроля может использоваться обёртка:

function setValueAndTrigger(input, value) {
  input.value = value;
  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));
}

AutoNumeric реагирует на change реже, но в некоторых конфигурациях это событие используется для синхронизации внешнего состояния.


Проверка форматирования после событий

Ключевая часть мокирования — валидация результата после цепочки событий.

Типовой сценарий:

simulateKey(input, '1');
simulateKey(input, '0');
simulateKey(input, '0');
simulateKey(input, '0');

expect(input.value).toBe('1,000');

Однако в AutoNumeric значение может зависеть от конфигурации:

  • разделитель тысяч
  • символ валюты
  • количество знаков после запятой
  • режим округления

Поэтому тестовые сценарии часто фиксируют конфигурацию заранее, чтобы исключить вариативность.


Мокирование программных изменений значения

AutoNumeric предоставляет API для установки значений без пользовательского ввода. В тестах важно различать:

  • изменение через DOM (input.value = ...)
  • изменение через API библиотеки
  • реакцию на внешние setValue вызовы

Пример:

autoNumericInstance.set('2500.75');

После этого DOM-события могут не генерироваться автоматически, поэтому тесты, проверяющие реакцию UI, должны явно инициировать input или blur.


Сложные сценарии: комбинированные события

Реалистичное мокирование часто включает смешанные цепочки:

  1. фокус на поле
  2. ввод числа
  3. вставка значения
  4. удаление части строки
  5. потеря фокуса

Пример последовательности:

input.dispatchEvent(new FocusEvent('focus', { bubbles: true }));

simulateKey(input, '1');
simulateKey(input, '0');

input.dispatchEvent(new ClipboardEvent('paste', {
  bubbles: true,
  clipboardData: new DataTransfer()
}));

input.value = '10abc0';

input.dispatchEvent(new InputEvent('input', {
  inputType: 'insertFromPaste',
  data: '10abc0',
  bubbles: true
}));

input.dispatchEvent(new FocusEvent('blur', { bubbles: true }));

AutoNumeric в таких сценариях обязан:

  • отфильтровать нечисловые символы
  • восстановить корректный формат
  • сохранить позиционную целостность значения

Контроль состояния курсора

Отдельным аспектом тестирования становится selectionStart и selectionEnd.

input.value = '12345';
input.setSelectionRange(2, 2);

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


Ошибки при мокировании и типовые причины нестабильности тестов

Наиболее частые проблемы:

  • несоответствие value и InputEvent.data
  • отсутствие bubbles: true
  • неправильный порядок событий
  • игнорирование composition-состояний
  • смешивание KeyboardEvent и InputEvent без синхронизации

Особенно критичным является рассинхрон между ручным изменением value и генерацией input события, что приводит к расхождению внутреннего состояния AutoNumeric.


Стабилизация тестовой среды

Для уменьшения флаки-тестов применяется стандартизация:

  • фиксированные хелперы для ввода
  • централизованные функции генерации событий
  • изоляция каждого сценария в новом DOM-элементе
  • явная очистка состояния AutoNumeric между тестами

Такая структура позволяет воспроизводить поведение библиотеки максимально близко к реальному браузерному взаимодействию без зависимости от особенностей jsdom или тестового раннера