Автоматизированное тестирование библиотек форматирования чисел требует учета специфики работы с DOM, событийной моделью браузера и преобразованиями строкового представления чисел. AutoNumeric как библиотека, модифицирующая поведение input-элементов, требует многоуровневого подхода: модульные тесты, интеграционные сценарии и проверка взаимодействия с реальным DOM.
Тестирование AutoNumeric целесообразно разделять на три слоя:
Проверяется изолированная логика:
Модульный уровень не требует DOM и выполняется в Node.js среде.
Проверяется взаимодействие:
input, keydown,
paste)Используется jsdom.
Проверяется поведение в браузерной среде:
Jest используется благодаря:
Пример базовой конфигурации:
module.exports = {
testEnvironment: "jsdom",
setupFilesAfterEnv: ["<rootDir>/tests/setup.js"],
testMatch: ["**/__tests__/**/*.test.js"]
};
При интеграционных тестах часто используется
@testing-library/dom для имитации пользовательского
ввода.
import { getByRole } from "@testing-library/dom";
import AutoNumeric from "autonumeric";
test("форматирование при вводе числа", () => {
document.body.innerHTML = `<input id="price" />`;
const input = document.getElementById("price");
new AutoNumeric(input, {
decimalPlaces: 2
});
input.value = "1234.5";
input.dispatchEvent(new Event("input"));
expect(input.value).toBe("1,234.50");
});
Ключевая логика AutoNumeric связана с форматированием строк:
import { format } from "../src/core/format";
test("разделение тысяч", () => {
expect(format("1000000")).toBe("1,000,000");
});
test("округление до фиксированного количества знаков", () => {
expect(format("12.3456", { decimalPlaces: 2 })).toBe("12.35");
});
AutoNumeric поддерживает большое количество опций, поэтому тестирование конфигураций выполняется как параметризованные тесты.
describe("конфигурации формата", () => {
const cases = [
[{ decimalCharacter: "," }, "12,5", "12,5"],
[{ digitGroupSeparator: " " }, "1000", "1 000"],
[{ currencySymbol: "$" }, "10", "$10"]
];
test.each(cases)(
"конфиг %o корректно форматирует %s -> %s",
(options, input, expected) => {
expect(format(input, options)).toBe(expected);
}
);
});
Работа AutoNumeric напрямую зависит от событий браузера:
test("обработка ввода символов", () => {
document.body.innerHTML = `<input id="num" />`;
const input = document.getElementById("num");
new AutoNumeric(input, { decimalPlaces: 0 });
input.value = "1";
input.dispatchEvent(new Event("input"));
input.value = "12";
input.dispatchEvent(new Event("input"));
expect(input.value).toBe("12");
});
Некоторые символы должны блокироваться:
test("блокировка недопустимых символов", () => {
document.body.innerHTML = `<input id="num" />`;
const input = document.getElementById("num");
new AutoNumeric(input);
const event = new KeyboardEvent("keydown", { key: "a", cancelable: true });
const prevented = !input.dispatchEvent(event);
expect(prevented).toBe(false);
});
Вставка текста — один из самых проблемных сценариев:
test("обработка вставки некорректного значения", () => {
document.body.innerHTML = `<input id="num" />`;
const input = document.getElementById("num");
new AutoNumeric(input);
const clipboardEvent = new ClipboardEvent("paste", {
clipboardData: new DataTransfer()
});
clipboardEvent.clipboardData.setData("text", "12abc34");
input.dispatchEvent(clipboardEvent);
expect(input.value).toBe("12,34");
});
Snapshot применяется для сложных конфигураций:
test("snapshot форматирования валюты", () => {
const result = format("1234567.89", {
currencySymbol: "€",
decimalPlaces: 2,
digitGroupSeparator: "."
});
expect(result).toMatchSnapshot();
});
Snapshot полезен при регрессионной проверке визуального формата.
AutoNumeric хранит внутреннюю модель числа отдельно от DOM.
test("получение числового значения", () => {
document.body.innerHTML = `<input id="num" />`;
const input = document.getElementById("num");
const an = new AutoNumeric(input, {});
an.set("1234.56");
expect(an.getNumber()).toBe(1234.56);
});
test("пустое значение не ломает форматирование", () => {
expect(format("")).toBe("");
});
test("отрицательные значения", () => {
expect(format("-0.5")).toBe("-0.50");
});
test("обработка больших чисел", () => {
expect(format("1000000000000")).toBe("1,000,000,000,000");
});
Для стабильных тестов используется изоляция окружения:
beforeEach(() => {
document.body.innerHTML = "";
jest.clearAllMocks();
});
В сложных случаях мокируются:
window.getSelectiondocument.execCommandinput.selectionStart / selectionEndДля проверки деградации производительности используется измерение времени выполнения:
test("форматирование 10000 значений", () => {
const start = performance.now();
for (let i = 0; i < 10000; i++) {
format(String(i));
}
const end = performance.now();
expect(end - start).toBeLessThan(200);
});
Автоматические тесты обычно запускаются в pipeline:
Типичная команда:
jest --coverage --runInBand
Порог покрытия:
Регрессионные тесты фиксируют поведение:
tests/
unit/
format.test.js
parse.test.js
integration/
input-events.test.js
paste.test.js
fixtures/
numbers.json
setup.js
Флаки-тесты возникают из-за асинхронности и DOM-событий. Для их устранения применяются:
jest.useFakeTimers();
Тестируются разные локали:
test("европейский формат чисел", () => {
expect(format("1234.56", {
digitGroupSeparator: ".",
decimalCharacter: ","
})).toBe("1.234,56");
});
test("изменение конфигурации на лету", () => {
const input = document.createElement("input");
document.body.appendChild(input);
const an = new AutoNumeric(input, { decimalPlaces: 2 });
an.update({ decimalPlaces: 0 });
an.set("123.45");
expect(input.value).toBe("123");
});
test("игнорирование мусорного ввода", () => {
expect(format("abc---12..34")).toBe("12.34");
});
Генерация случайных тестов:
function randomNumber() {
return (Math.random() * 1e6).toFixed(4);
}
test("рандомизированное форматирование", () => {
for (let i = 0; i < 1000; i++) {
const num = randomNumber();
expect(() => format(num)).not.toThrow();
}
});
test("submit формы с AutoNumeric", () => {
document.body.innerHTML = `
<form>
<input id="amount" />
</form>
`;
const input = document.getElementById("amount");
new AutoNumeric(input);
input.value = "1000";
input.form.dispatchEvent(new Event("submit"));
expect(input.value).toBeDefined();
});