Юнит-тесты для форматирования

Юнит-тестирование форматирования числовых значений в браузерных и Node.js-библиотеках требует особого подхода, поскольку поведение часто зависит от локали, DOM-среды, событий ввода и внутренних правил округления. В контексте AutoNumeric ключевой задачей становится проверка корректности преобразования входных данных в отформатированные строки и обратно, включая сценарии частичного ввода, удаления символов, вставки и программного изменения значений.

Форматирование чисел нельзя тестировать как чистую функцию в классическом виде, поскольку библиотека работает с состоянием DOM-элемента и перехватывает пользовательские события. Это приводит к необходимости разделения тестов на несколько уровней:

  • чистые юнит-тесты функций форматирования;
  • тесты логики округления и нормализации;
  • DOM-тесты поведения инпута;
  • интеграционные тесты событий ввода.

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

  • разделитель тысяч (digitGroupSeparator);
  • десятичный символ (decimalCharacter);
  • количество знаков после запятой (decimalPlaces);
  • режим округления (roundingMethod);
  • валютные символы (currencySymbol).

Подготовка тестового окружения

Для тестирования AutoNumeric в Node.js чаще всего используется связка Jest и JSDOM. JSDOM эмулирует браузерный DOM, позволяя создавать input-элементы и симулировать пользовательский ввод.

Типовая конфигурация тестового окружения включает:

  • Jest как тест-раннер;
  • JSDOM как DOM-эмулятор;
  • вспомогательные утилиты для симуляции событий (input, keydown, blur);
  • фиктивные таймеры при необходимости тестирования debounce-логики.

Важно учитывать, что некоторые браузерные API могут отсутствовать в JSDOM, поэтому требуется их частичное мокирование.

Пример базовой инициализации тестового инпута:

document.body.innerHTML = `<input id="price" type="text">`;
const input = document.getElementById("price");

После этого к элементу применяется AutoNumeric-конфигурация:

import AutoNumeric from "autonumeric";

const anElement = new AutoNumeric(input, {
  digitGroupSeparator: " ",
  decimalCharacter: ",",
  decimalPlaces: 2
});

Тестирование базового форматирования

Первый уровень проверки — корректное преобразование входного числа в строковое представление.

Проверяется:

  • корректное добавление разделителей групп;
  • правильная установка десятичной части;
  • сохранение знака числа.

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

test("форматирует число с разделением тысяч", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    digitGroupSeparator: " ",
    decimalCharacter: ".",
    decimalPlaces: 2
  });

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

  expect(input.value).toBe("1 000 000.50");
});

В этом сценарии проверяется не только форматирование, но и реакция на событие input, которое активирует внутренний парсер AutoNumeric.

Проверка округления

Округление является одной из наиболее чувствительных частей логики. Разные режимы могут давать разные результаты даже при одинаковом входном значении.

Основные режимы:

  • HALF_UP;
  • HALF_EVEN;
  • CEILING;
  • FLOOR;
  • UP/DOWN.

Тестирование требует проверки пограничных значений:

test("округление HALF_UP до двух знаков", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    decimalPlaces: 2,
    roundingMethod: "HALF_UP",
    decimalCharacter: "."
  });

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

  expect(input.value).toBe("10.56");
});

Особое внимание уделяется значениям вида x.5, x.555, x.554, поскольку именно они выявляют ошибки в алгоритме округления.

Тестирование частичного ввода

AutoNumeric работает в режиме live-форматирования, поэтому пользователь может вводить число поэтапно:

  • ввод 1
  • ввод 10
  • ввод 1000
  • ввод 1000000

На каждом этапе значение должно оставаться валидным и корректно форматироваться.

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

test("постепенный ввод числа форматируется корректно", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    digitGroupSeparator: ",",
    decimalPlaces: 0
  });

  input.value = "1";
  input.dispatchEvent(new Event("input", { bubbles: true }));
  expect(input.value).toBe("1");

  input.value = "1000";
  input.dispatchEvent(new Event("input", { bubbles: true }));
  expect(input.value).toBe("1,000");

  input.value = "1000000";
  input.dispatchEvent(new Event("input", { bubbles: true }));
  expect(input.value).toBe("1,000,000");
});

Здесь проверяется устойчивость форматирования к промежуточным состояниям.

Тестирование удаления символов

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

Проверяется:

  • корректное удаление цифр;
  • обновление группировки;
  • отсутствие «ломаных» строк.

Пример:

test("удаление символов сохраняет корректный формат", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    digitGroupSeparator: " ",
    decimalPlaces: 0
  });

  input.value = "1 000 000";
  input.dispatchEvent(new Event("input", { bubbles: true }));

  input.value = "100 000";
  input.dispatchEvent(new Event("input", { bubbles: true }));

  expect(input.value).toBe("100 000");
});

Тестирование отрицательных значений

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

Ключевые сценарии:

  • ввод отрицательного числа;
  • удаление цифр при отрицательном значении;
  • переключение знака.
test("форматирование отрицательного числа", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    digitGroupSeparator: ",",
    decimalPlaces: 2
  });

  input.value = "-12345.6";
  input.dispatchEvent(new Event("input", { bubbles: true }));

  expect(input.value).toBe("-12,345.60");
});

Тестирование валютного форматирования

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

  • позиция символа (prefix/suffix);
  • пробел между символом и числом;
  • сохранение символа при редактировании.
test("форматирование с валютным символом", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    currencySymbol: "$",
    currencySymbolPlacement: "p",
    digitGroupSeparator: ",",
    decimalPlaces: 2
  });

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

  expect(input.value).toBe("$2,500.00");
});

Тестирование локализации

Локализация влияет на:

  • десятичный разделитель;
  • групповой разделитель;
  • правила отображения отрицательных чисел.

Типичный тест локали:

test("локаль с запятой как десятичным разделителем", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    decimalCharacter: ",",
    digitGroupSeparator: ".",
    decimalPlaces: 2
  });

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

  expect(input.value).toBe("1.234,56");
});

Мокирование событий и поведения DOM

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

  • keydown для проверки ограничений ввода;
  • keypress для устаревших сценариев;
  • blur для финального форматирования;
  • focus для перехода в режим редактирования.

Пример теста blur-форматирования:

test("переформатирование при потере фокуса", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    decimalPlaces: 2
  });

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

  input.dispatchEvent(new Event("blur"));

  expect(input.value).toBe("12.30");
});

Проверка программного API

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

  • set();
  • get();
  • clear();
  • setUnformatted().

Пример:

test("set и get работают корректно", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  const anElement = new AutoNumeric(input, {
    decimalPlaces: 2
  });

  anElement.set(9999.9);

  expect(anElement.get()).toBe(9999.9);
  expect(input.value).toBe("9,999.90");
});

Граничные значения и числовые артефакты

Особое внимание уделяется:

  • очень большим числам;
  • числам с плавающей точкой;
  • экспоненциальной записи;
  • NaN и undefined;
  • пустым строкам.

Пример:

test("обработка NaN и пустого значения", () => {
  document.body.innerHTML = `<input id="price">`;
  const input = document.getElementById("price");

  new AutoNumeric(input, {
    decimalPlaces: 2
  });

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

  expect(input.value).toBe("");
});