Интеграционное тестирование для компонентов на базе Tom Sel ect требует проверки не отдельных функций, а поведения системы в связке: DOM, событийная модель, асинхронная загрузка данных, плагины и взаимодействие с внешними источниками данных. В отличие от юнит-тестов, здесь важно воспроизводить реальные сценарии использования: ввод текста, открытие списка, выбор элементов, очистка состояния, повторная инициализация.
Интеграционные тесты для Tom Select обычно выполняются в среде,
имитирующей браузер. Чаще всего используются Jest с
jsdom, Vitest или браузерные раннеры вроде
Playwright.
Ключевой момент — корректная эмуляция DOM, поскольку Tom Select активно манипулирует элементами:
/**
* Базовая HTML-структура для теста
*/
document.body.innerHTML = `
<select id="select">
<option value="1">One</option>
<option value="2">Two</option>
</select>
`;
Инициализация компонента:
import TomSelect fr om "tom-select";
const el = document.querySelector("#select");
const instance = new TomSelect(el, {
create: false,
persist: false
});
Важно учитывать, что Tom Sel ect создаёт собственную DOM-структуру
поверх исходного <select>, поэтому тесты должны
опираться на новые элементы, а не на оригинальный тег.
Интеграционный тест должен подтверждать, что компонент корректно преобразует исходный элемент:
test("создаёт контейнер Tom Select", () => {
const wrapper = document.querySelector(".tom-select");
expect(wrapper).not.toBeNull();
});
Типичные проверки:
.tom-select<select>expect(el.style.display).toBe("none");
Основной сценарий — выбор опции через интерфейс:
const control = document.querySelector(".ts-control");
control.click();
После открытия списка важно дождаться рендера:
const items = document.querySelectorAll(".option");
expect(items.length).toBeGreaterThan(0);
Выбор элемента:
items[0].click();
expect(instance.getValue()).toBe("1");
Проверка синхронизации состояния:
<select>Одной из ключевых особенностей интеграционного тестирования Tom
Select является проверка remote-загрузки данных через
load:
const instance = new TomSelect(el, {
load: (query, callback) => {
setTimeout(() => {
callback([
{ value: "10", text: "Ten" },
{ value: "20", text: "Twenty" }
]);
}, 50);
}
});
Тест должен учитывать задержку:
control.focus();
control.value = "te";
control.dispatchEvent(new Event("input"));
await new Promise(r => setTimeout(r, 100));
const options = document.querySelectorAll(".option");
expect(options.length).toBe(2);
Ключевой аспект — контроль таймингов и предотвращение флаки-тестов.
При интеграции с API часто используется fetch. В тестах
необходимо подменять сетевой слой:
global.fetch = jest.fn(() =>
Promise.resolve({
json: () =>
Promise.resolve([
{ value: "1", text: "Alpha" },
{ value: "2", text: "Beta" }
])
})
);
Конфигурация Tom Select:
const instance = new TomSelect(el, {
load: async (query, callback) => {
const res = await fetch("/api/items?q=" + query);
const data = await res.json();
callback(data);
}
});
Проверка результата:
control.focus();
control.value = "a";
control.dispatchEvent(new Event("input"));
await flushPromises();
expect(fetch).toHaveBeenCalled();
flushPromises используется для ожидания всех
микротасков:
function flushPromises() {
return new Promise(resolve => setImmediate(resolve));
}
Tom Select активно использует события: onChange,
onDropdownOpen, onItemAdd.
Пример интеграционного теста:
const onCha nge = jest.fn();
const instance = new TomSelect(el, {
onChange
});
Имитация выбора:
instance.addItem("1");
expect(onChange).toHaveBeenCalledWith("1");
Дополнительно проверяется порядок вызовов и корректность аргументов.
Для maxItems > 1 тестируется добавление нескольких
элементов:
const instance = new TomSelect(el, {
maxItems: 3
});
instance.addItem("1");
instance.addItem("2");
instance.addItem("3");
Проверки:
expect(instance.items.length).toBe(3);
expect(instance.getValue()).toBe("1,2,3");
Также важно тестировать ограничение:
instance.addItem("4");
expect(instance.items.length).toBe(3);
Интеграционные тесты должны учитывать жизненный цикл компонента.
Очистка:
instance.destroy();
Проверки после уничтожения:
<select>expect(document.querySelector(".tom-select")).toBeNull();
Повторная инициализация:
const instance2 = new TomSelect(el, {});
expect(instance2).toBeDefined();
Tom Select поддерживает плагины, влияющие на поведение интерфейса. Интеграционные тесты обязаны учитывать их влияние на DOM.
Пример с remove_button:
const instance = new TomSelect(el, {
plugins: ["remove_button"]
});
Проверка кнопки удаления:
instance.addItem("1");
const removeBtn = document.querySelector(".remove");
expect(removeBtn).not.toBeNull();
Имитация удаления:
removeBtn.click();
expect(instance.items.length).toBe(0);
Поиск внутри Tom Select — один из наиболее чувствительных сценариев:
control.focus();
control.value = "one";
control.dispatchEvent(new Event("input"));
Проверка отфильтрованных элементов:
const visibleOptions = Array.fr om(document.querySelectorAll(".option"))
.filter(o => o.style.display !== "none");
expect(visibleOptions.length).toBe(1);
Важно учитывать debounce-логику, если она включена в конфигурации.
Асинхронные операции внутри Tom Sel ect могут приводить к race conditions. Для стабилизации тестов используется контроль времени:
jest.useFakeTimers();
control.value = "a";
control.dispatchEvent(new Event("input"));
jest.runAllTimers();
Это позволяет детерминированно проверять результат загрузки и фильтрации.
<select>Критически важный аспект интеграции — корректное отражение состояния:
instance.addItem("2");
expect(el.value).toBe("2");
При множественном выборе:
expect(Array.fr om(el.selectedOptions).map(o => o.value))
.toEqual(["1", "2"]);
Интеграционные тесты часто включают эмуляцию клавиш:
control.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowDown" }));
control.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
Проверяется:
Поведение Tom Select чувствительно к таймингам и DOM-рефлоу, поэтому стабильность тестов достигается через:
document.body после каждого тестаsetTimeout в логике
тестовafterEach(() => {
document.body.innerHTML = "";
jest.clearAllMocks();
});
Реальные интеграционные тесты должны моделировать цепочки действий:
control.value = "a";
control.dispatchEvent(new Event("input"));
await flushPromises();
document.querySelectorAll(".option")[0].click();
instance.addItem("2");
instance.removeItem("1");
Проверка итогового состояния:
expect(instance.getValue()).toBe("2");
Такие сценарии выявляют скрытые проблемы взаимодействия внутренних модулей Tom Select, особенно при комбинировании плагинов, асинхронных загрузок и пользовательских обработчиков событий.