E2E тестирование

При E2E тестировании интерфейсов, использующих Tom Select, основное внимание сосредоточено на поведении компонента в реальном браузере: вводе текста, открытии выпадающего списка, выборе элементов, очистке значений и взаимодействии с внешними источниками данных. В отличие от unit- и integration-тестов, здесь проверяется полный путь пользователя через DOM, события и асинхронные операции, включая рендеринг и обновление состояния.

Tom Select строится вокруг преобразования стандартного <select> элемента в управляемый JavaScript-компонент с кастомным DOM. После инициализации исходный элемент скрывается, а на его месте появляется структура, включающая контейнер, поле ввода, dropdown и элементы выбранных значений.

Ключевые DOM-сущности:

  • контейнер .tomselect как корневой узел компонента
  • поле ввода .ts-control input
  • список опций .ts-dropdown
  • элементы выбора .item
  • кнопка удаления выбранного значения .remove

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

Особенности асинхронного поведения

В E2E сценариях важно учитывать, что Tom Select может работать в нескольких режимах:

  • локальные опции (синхронная фильтрация)
  • удалённые источники данных (AJAX / fetch)
  • режимы с debounce ввода
  • lazy rendering списка

Асинхронность влияет на стабильность тестов, поскольку DOM обновляется не мгновенно. Поэтому ключевым аспектом становится ожидание состояний интерфейса, а не фиксированных таймингов.

В современных тестовых раннерах, таких как Cypress и Playwright, используется ожидание появления элементов:

  • появление .ts-dropdown
  • наличие определённого количества .option
  • изменение текста в выбранных значениях

Инициализация Tom Select в тестовом окружении

В E2E сценариях компонент обычно подключается через тестовую HTML-страницу:

<select id="country-select">
  <option value="kz">Kazakhstan</option>
  <option value="de">Germany</option>
  <option value="jp">Japan</option>
</select>

<script>
  new TomSelect("#country-select", {
    create: false,
    searchField: ["text"]
  });
</script>

В тестах важно, чтобы страница загружалась в состоянии, идентичном production-среде: стили подключены, скрипты загружены, а DOM уже инициализирован.

Базовые E2E сценарии выбора значения

Наиболее частый сценарий — открытие dropdown и выбор элемента. В Cypress это выглядит через последовательность действий:

cy.get('.tomselect').click();
cy.get('.ts-dropdown .option').contains('Germany').click();
cy.get('.tomselect .item').should('contain', 'Germany');

В Playwright аналогичный сценарий использует локаторы:

await page.click('.tomselect');
await page.click('.ts-dropdown .option:text("Germany")');
await expect(page.locator('.item')).toContainText('Germany');

Ключевая проверка заключается не в клике как таковом, а в том, что DOM отражает выбранное состояние.

Проверка поиска и фильтрации

Функция поиска внутри Tom Select часто является наиболее сложной частью для E2E тестов, поскольку включает:

  • обработку ввода
  • фильтрацию массива опций
  • обновление DOM без перерендера всей структуры
  • debounce задержки

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

cy.get('.ts-control input').type('ger');
cy.get('.ts-dropdown .option').should('have.length', 1);
cy.get('.ts-dropdown .option').first().should('contain', 'Germany');

В случае асинхронного источника данных добавляется ожидание сетевого запроса:

cy.intercept('GET', '/api/countries?q=ger').as('search');
cy.get('.ts-control input').type('ger');
cy.wait('@search');
cy.get('.ts-dropdown .option').should('exist');

Работа с удалёнными данными

Tom Select поддерживает режим remote data source, где данные подгружаются через API. В E2E тестировании этот режим требует контроля сетевого слоя.

Часто используется перехват запросов:

  • Cypress: cy.intercept
  • Playwright: page.route

Пример сценария:

page.route('**/api/countries*', route =>
  route.fulfill({
    status: 200,
    body: JSON.stringify([
      { value: 'kz', text: 'Kazakhstan' },
      { value: 'de', text: 'Germany' }
    ])
  })
);

После ввода текста тест проверяет, что данные отрисованы корректно и доступны для выбора.

Множественный выбор (multi-select)

Tom Select поддерживает режим множественного выбора, где пользователь может добавлять несколько элементов.

DOM в этом случае содержит набор .item внутри контейнера.

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

cy.get('.ts-control input').type('a');
cy.get('.ts-dropdown .option').first().click();

cy.get('.ts-control input').type('b');
cy.get('.ts-dropdown .option').first().click();

cy.get('.item').should('have.length', 2);

Особенность проверки заключается в порядке элементов: он может быть важен, если включена сортировка выбора.

Удаление выбранных элементов

Удаление реализуется через кнопку .remove, встроенную в каждый .item.

cy.get('.item .remove').first().click();
cy.get('.item').should('have.length', 1);

В сложных сценариях проверяется также синхронизация состояния <select>:

cy.get('select').find('option:selected').should('have.length', 1);

Обработка keyboard navigation

Tom Select активно использует клавиатурное управление:

  • ArrowDown / ArrowUp для навигации
  • Enter для выбора
  • Esc для закрытия dropdown

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

cy.get('.ts-control input').type('{downarrow}');
cy.get('.ts-control input').type('{enter}');
cy.get('.item').should('exist');

В Playwright:

await page.keyboard.press('ArrowDown');
await page.keyboard.press('Enter');

Ключевая проверка заключается в изменении состояния интерфейса без использования мыши.

Проверка состояния disabled и readonly

Tom Select может быть инициализирован на disabled <select> элементе. В этом случае взаимодействие должно быть заблокировано.

cy.get('.tomselect').should('have.class', 'disabled');
cy.get('.ts-control input').should('not.be.visible');

Readonly режим проверяется через отсутствие возможности изменения значений:

cy.get('.ts-control input').type('test');
cy.get('.item').should('have.length', 0);

Работа с динамическим обновлением опций

В некоторых сценариях список опций может изменяться после инициализации. Это требует проверки реактивности компонента.

cy.window().then(win => {
  const select = win.document.querySelector('#country-select');
  const tom = select.tomselect;
  tom.addOption({ value: 'fr', text: 'France' });
  tom.refreshOptions(false);
});

После этого E2E тест проверяет появление новой опции в dropdown.

Валидация состояния при очистке

Функция очистки состояния используется через метод clear или UI-кнопку.

cy.get('.clear-button').click();
cy.get('.item').should('not.exist');

Дополнительно проверяется сброс значения <select>:

cy.get('select').should('have.value', '');

Тайминги и нестабильность тестов

Основная сложность E2E тестирования Tom Select заключается в нестабильности, связанной с:

  • debounce ввода
  • асинхронными запросами
  • анимацией dropdown
  • задержкой рендера DOM

Решение заключается в опоре на состояния DOM:

  • should('exist')
  • should('be.visible')
  • should('have.length')

Использование фиксированных wait(1000) считается источником флаки-тестов и заменяется на событийные ожидания.

Интеграция с CI окружением

В CI среде E2E тесты Tom Select выполняются в headless-браузерах. Особое внимание уделяется:

  • стабильности шрифтов и layout
  • загрузке CSS до инициализации компонента
  • фиксации viewport размера
  • отключению анимаций через CSS

Пример стабилизации:

* {
  transition: none !important;
  animation: none !important;
}

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

В продвинутых сценариях проверяется комбинация:

  • remote search
  • multi-select
  • создание новых элементов (create mode)
  • кастомные render-функции опций

Пример сценария создания нового элемента:

cy.get('.ts-control input').type('new item');
cy.get('.create').click();
cy.get('.item').should('contain', 'new item');

Проверка кастомного рендера

Tom Select позволяет переопределять отображение опций через render.option и render.item. E2E тестирование здесь фокусируется на структуре DOM, а не на логике:

  • наличие кастомных классов
  • корректное отображение текста
  • сохранение структуры элементов
cy.get('.option.custom').should('exist');
cy.get('.item.custom').should('contain', 'Germany');

Поведенческая целостность компонента

Полное E2E тестирование Tom Select сводится к проверке связки:

  • input → фильтрация → dropdown
  • dropdown → selection → item rendering
  • item → removal → state update
  • state → select element synchronization

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