Библиотека выбора элементов в интерфейсе работает поверх DOM и активно взаимодействует с событиями, состоянием элементов и динамическим рендерингом. Это формирует специфические требования к тестированию: значительная часть логики не является чистыми функциями, а зависит от окружения браузера.
Ключевые сложности:
Покрытие кода в таких условиях требует комбинирования unit, integration и end-to-end подходов.
На практике используется инструмент экосистемы Istanbul:
nyc — CLI-обёртка для Istanbul;
встроенные средства покрытия в Jest;
поддержка в Vitest через c8;
отчёты форматов:
Пример базовой конфигурации Jest с покрытием:
export default {
testEnvironment: "jsdom",
collectCoverage: true,
coverageDirectory: "coverage",
coverageReporters: ["text", "lcov", "html"],
collectCoverageFrom: [
"src/**/*.js",
"!src/**/*.d.ts",
"!src/**/*.config.js"
]
};
Важный аспект: для DOM-библиотек обязательно использовать
jsdom или аналогичную среду, иначе покрытие будет формально
высоким, но тесты не будут отражать реальное поведение.
Для эффективного покрытия важно разделить функциональность на уровни:
1. Ядро (core logic)
2. DOM-слой
3. Событийная система
4. Плагины
Каждый слой требует отдельного подхода к тестированию, иначе покрытие становится формальным и не отражает качество.
Unit-тесты должны изолировать бизнес-логику от DOM.
Пример тестирования фильтрации:
import TomSelect from "tom-select";
test("filters options correctly", () => {
const instance = new TomSelect(document.createElement("select"), {
options: [
{ value: "1", text: "Apple" },
{ value: "2", text: "Banana" },
{ value: "3", text: "Orange" }
],
create: false
});
const result = instance.search("ap");
expect(result.items.length).toBe(1);
expect(result.items[0].text).toBe("Apple");
});
Здесь важно:
Интеграционные тесты проверяют взаимодействие ядра и интерфейса.
Пример с jsdom:
import TomSelect from "tom-select";
beforeEach(() => {
document.body.innerHTML = `
<select id="test">
<option value="1">Apple</option>
<option value="2">Banana</option>
</select>
`;
});
test("opens dropdown on focus", () => {
const el = document.querySelector("#test");
const select = new TomSelect(el, {});
select.focus();
select.open();
const dropdown = document.querySelector(".ts-dropdown");
expect(dropdown).not.toBeNull();
});
Интеграционный уровень обязателен, поскольку значительная часть логики реализована через DOM-состояние.
События — ключевая часть поведения селекта.
Типовые сценарии:
Пример:
test("triggers item_add event", () => {
const el = document.createElement("select");
const select = new TomSelect(el, {});
const handler = jest.fn();
select.on("item_add", handler);
select.addItem("1");
expect(handler).toHaveBeenCalledWith("1");
});
Здесь важно проверять:
Многие конфигурации используют загрузку данных через API.
Проблемы тестирования:
Решение — использование mock fetch:
global.fetch = jest.fn(() =>
Promise.resolve({
json: () =>
Promise.resolve([
{ value: "1", text: "Apple" },
{ value: "2", text: "Banana" }
])
})
);
Тест:
test("loads remote options", async () => {
const el = document.createElement("select");
const select = new TomSelect(el, {
load: (query, callback) => {
fetch("/api?q=" + query)
.then(res => res.json())
.then(callback);
}
});
await select.load("a");
expect(select.options["1"]).toBeDefined();
});
Асинхронные тесты обязательно должны учитывать:
await;Плагины часто содержат отдельные ветки логики, которые легко пропустить.
Стратегия:
Пример:
test("plugin creates remove button", () => {
const el = document.createElement("select");
const select = new TomSelect(el, {
plugins: ["remove_button"]
});
select.addItem("1");
const button = document.querySelector(".remove");
expect(button).not.toBeNull();
});
Основные метрики:
Команда для запуска:
npx jest --coverage
Критический момент: высокий процент покрытия не гарантирует качество тестов.
Типичная проблема:
Для устойчивого проекта задаются минимальные пороги:
coverageThreshold: {
global: {
statements: 80,
branches: 70,
functions: 80,
lines: 80
}
}
Порог ветвлений особенно важен для библиотек с большим количеством условий (например, обработка input/selection states).
Подход, который даёт стабильное качество тестов: