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

Картографические интерфейсы в браузере относятся к классу сложных визуально-интерактивных систем, где результат работы зависит одновременно от данных, стилей, тайлов, GPU-рендеринга и состояния пользовательского взаимодействия. При использовании MapLibre GL JS это особенно заметно, так как рендеринг выполняется через WebGL и включает множество недетерминированных факторов.

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


Архитектура тестируемого слоя карты

Карта в MapLibre GL JS состоит из нескольких уровней, каждый из которых влияет на итоговый рендер:

  • стиль (Style JSON)
  • источники данных (sources)
  • слои (layers)
  • тайлы (vector/raster tiles)
  • WebGL pipeline
  • canvas framebuffer

Ключевая сложность E2E тестирования заключается в том, что итоговый результат — это изображение, а не структурированный DOM.

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


Подходы к E2E тестированию карт

Тестирование через браузерные фреймворки

На практике применяются:

  • Playwright
  • Cypress
  • WebdriverIO

Playwright наиболее часто используется для картографических сценариев благодаря:

  • стабильному headless Chromium
  • встроенной поддержке скриншотов
  • контролю сети
  • изоляции контекста браузера

Основная стратегия — запуск карты в реальном браузере с последующим сравнением пиксельного результата.


Контроль детерминизма рендеринга

Главная проблема E2E тестов карт — недетерминированность. На визуальный результат влияют:

  • порядок загрузки тайлов
  • сетевые задержки
  • антиалиасинг WebGL
  • различия GPU и драйверов
  • асинхронная отрисовка

Для стабилизации тестов применяются следующие техники.

Фиксация стиля

Стиль должен быть полностью статическим:

  • запрещены динамические URL
  • все источники данных замоканы
  • версии шрифтов и sprite фиксированы

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

Сетевые запросы перехватываются и заменяются фикстурами:

  • vector tiles (MVT)
  • raster tiles (PNG/WebP)
  • glyphs
  • sprites

Это устраняет зависимость от внешних API и сети.


Изоляция сетевых запросов

В E2E тестах важно перехватывать все внешние обращения карты:

  • /{z}/{x}/{y}.pbf
  • /glyphs/{fontstack}/{range}.pbf
  • /style.json
  • /sprite.png

В Playwright это реализуется через routing:

  • блокировка внешних доменов
  • подмена ответов локальными файлами
  • контроль кэширования

Критично обеспечить одинаковый набор данных между запусками.


Пиксельное сравнение (visual regression)

Основной метод проверки карт — сравнение изображений canvas.

Базовый процесс:

  1. Инициализация карты
  2. Ожидание idle состояния рендера
  3. Снятие скриншота контейнера
  4. Сравнение с эталонным изображением

Важно учитывать:

  • задержку WebGL отрисовки
  • анимации (их нужно отключать)
  • плавное появление тайлов

Ожидание стабильного состояния карты

MapLibre GL JS не предоставляет явного сигнала “рендер завершён”, поэтому используется комбинация событий:

  • load
  • idle
  • render

Практический критерий стабильности:

  • отсутствие новых render событий в течение заданного интервала
  • завершение загрузки всех источников
  • отсутствие активных тайлов

Отключение анимаций и переходов

Для стабильных E2E тестов отключаются:

  • transitionDuration
  • fadeDuration
  • pitch/rotation animations

Пример логики конфигурации:

  • fadeDuration: 0
  • rotateAnimation: false
  • pitchWithRotate: false

Это исключает различия между кадрами.


Headless WebGL и проблемы окружения

Headless режим браузера создаёт дополнительные сложности:

  • различия в WebGL implementation
  • отсутствие GPU ускорения в CI
  • fallback на software rendering

В CI-средах часто используется:

  • xvfb (виртуальный дисплей)
  • software WebGL (SwiftShader)
  • фиксированные версии Chromium

Любые изменения в окружении могут менять пиксельный результат.


Тестирование взаимодействий

E2E тесты карт включают не только рендер, но и взаимодействие:

  • zoom
  • pan
  • rotation
  • click on features
  • hover states

Каждое действие проверяется через последовательность:

  1. событие пользователя
  2. ожидание стабилизации рендера
  3. скриншот состояния

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

  • инерции (inertia scrolling)
  • easing функций
  • каскадным перерисовкам слоёв

Проверка слоёв и источников данных

Хотя визуальная проверка основная, полезно дополнительно проверять структуру карты:

  • наличие источников (map.getSource)
  • количество слоёв (map.getStyle().layers)
  • корректность фильтров
  • порядок слоёв (z-index логика)

Это снижает вероятность ошибок, которые не всегда проявляются визуально.


Работа с фикстурами данных

Фикстуры являются основой стабильного тестирования:

  • GeoJSON наборы
  • упрощённые vector tiles
  • минимальные стили
  • тестовые sprite sheets

Фикстуры должны быть:

  • минимальными по размеру
  • детерминированными
  • независимыми от внешних сервисов

CI/CD интеграция

E2E тесты карт встраиваются в pipeline с учётом:

  • увеличенного времени выполнения
  • необходимости GPU-эмуляции
  • хранения baseline изображений

Часто применяются стратегии:

  • разделение smoke и visual regression тестов
  • параллельный запуск по сценарию карт
  • хранение эталонов в артефакт-репозитории

Типичные причины нестабильности тестов

На практике нестабильность чаще всего возникает из-за:

  • различий антиалиасинга
  • изменения порядка загрузки тайлов
  • сетевых таймаутов
  • шрифтовых различий
  • неполной фиксации стилей
  • различий WebGL контекста

Каждый из этих факторов требует отдельной стабилизации через фикстуры или конфигурацию окружения.


Стратегия построения тестового покрытия

Эффективное E2E покрытие карт обычно строится слоями:

  • базовый рендер карты без данных
  • рендер с одним источником
  • рендер сложного стиля с несколькими слоями
  • интерактивные сценарии
  • edge cases (пустые данные, ошибки источников)

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