Snapshot testing в контексте Mapbox GL JS применяется как инструмент визуальной регрессии, фиксирующий состояние карты в виде изображения или структурированного представления сцены (DOM + WebGL canvas) и сравнивающий его с эталонным снимком при каждом запуске тестов. В отличие от классических unit-тестов, snapshot-тестирование в картографических приложениях ориентировано не на данные, а на визуальный результат рендеринга сложной сцены, где участвуют тайлы, слои, источники данных, стили и GPU-пайплайн.
Карты в Mapbox GL JS не являются статичным DOM-деревом. Каждый кадр представляет собой результат WebGL-рендеринга, где:
Snapshot testing в таком контексте должен учитывать, что даже небольшое изменение входных данных или окружения может привести к визуально заметной разнице.
Основная идея заключается в фиксации состояния карты после полной стабилизации рендера:
Пример базового теста с Jest и Playwright:
import { test, expect } from '@playwright/test';
test('map snapshot', async ({ page }) => {
await page.goto('http://localhost:3000');
await page.evaluate(() => {
window.map.setCenter([37.6173, 55.7558]);
window.map.setZoom(10);
window.map.setPitch(0);
window.map.setBearing(0);
});
await page.waitForFunction(() => window.map.loaded());
const mapElement = await page.locator('#map');
await expect(mapElement).toHaveScreenshot('map-baseline.png');
});
Snapshot testing карт сталкивается с фундаментальной проблемой: недетерминированный рендеринг.
Источники нестабильности:
Для стабилизации тестов применяются следующие подходы:
map.setPaintProperty('water', 'fill-opacity-transition', { duration: 0 });
map.setLayoutProperty('road-label', 'text-rotate', 0);
Также часто полностью отключают transition через стиль:
const style = {
...baseStyle,
transition: { duration: 0 }
};
Любые snapshot-тесты должны выполняться при строго заданных параметрах:
Критический момент snapshot testing — корректное определение готовности сцены.
Типовые варианты:
await new Promise((resolve) => {
map.on('idle', resolve);
});
или более строгий вариант:
await map.once('render');
await map.once('idle');
В сложных сценариях необходимо учитывать:
Для стабильных snapshot-тестов часто изолируют карту от сети:
Пример intercept в Playwright:
await page.route('**/tiles/**', route => {
route.fulfill({
path: './fixtures/tiles/{z}/{x}/{y}.pbf'
});
});
Это позволяет полностью убрать зависимость от внешних сервисов Mapbox и других CDN.
Snapshot testing для карт обычно делится на несколько уровней:
Снимок WebGL canvas:
const canvas = await page.locator('canvas');
await expect(canvas).toHaveScreenshot();
Фиксация HTML + overlay UI:
Комбинация canvas + DOM overlays:
Использование фиксированных GeoJSON вместо динамических API:
map.addSource('points', {
type: 'geojson',
data: '/fixtures/points.json'
});
Mapbox GL JS использует сложные алгоритмы размещения подписей. Для стабилизации:
Symbol layers часто являются причиной флейков:
Рекомендуется минимизировать плотность данных в snapshot-сценариях.
Snapshot testing карт почти всегда выполняется в CI:
Типичный пайплайн:
Пример команды:
npx playwright test --update-snapshots
Эталонные изображения должны:
При изменении стиля карты (например, изменение цветовой схемы) происходит массовое обновление baseline:
jest --updateSnapshot
или
npx playwright test --update-snapshots
При расхождении snapshot генерируется diff:
Типичные причины изменений:
Решение:
idle;Решение:
Решение:
Типичная структура snapshot testing системы:
Каждый слой изолирует нестабильность WebGL и сети от логики тестов.
При большом количестве карт и стилей:
Особенно важно при работе с крупными картографическими системами на базе Mapbox, где количество стилей и слоёв может исчисляться десятками и сотнями.