E2E тестирование в контексте картографических интерфейсов на Leaflet отличается высокой долей асинхронности, сетевых зависимостей и графической отрисовки через DOM и canvas. Основная сложность заключается в том, что карта формируется не как статический набор элементов, а как результат последовательной загрузки тайлов, слоёв и интерактивных объектов.
Ключевые особенности поведения Leaflet в тестовой среде:
Типичная структура E2E тестирования картографического интерфейса включает следующие уровни:
Важным аспектом является разделение логики UI и сетевого слоя через перехват HTTP-запросов.
Для E2E тестирования Leaflet обычно используются:
Наиболее распространённый подход — Cypress или Playwright, так как они позволяют перехватывать сетевые запросы и работать с DOM в реальном времени.
Базовые требования к окружению:
Пример настройки viewport:
cy.viewport(1280, 800);
или Playwright:
await page.setViewportSize({ width: 1280, height: 800 });
При тестировании важно контролировать момент создания карты и её готовности к взаимодействию.
Типичная инициализация Leaflet:
const map = L.map('map', {
center: [51.505, -0.09],
zoom: 13,
zoomControl: false
});
В E2E тестах проверяется:
Признак готовности карты часто определяется через событие
load:
map.whenReady(() => {
console.log('map ready');
});
Основная проблема E2E тестов Leaflet — нестабильность внешних tile server’ов. Поэтому используется перехват запросов.
cy.intercept('GET', '**/*.png', {
fixture: 'tile.png'
}).as('tiles');
После чего тест ожидает загрузки:
cy.wait('@tiles');
await page.route('**/*.png', route => {
route.fulfill({
path: 'fixtures/tile.png'
});
});
Контроль сетевых запросов позволяет:
Маркер в Leaflet создаётся через L.marker, и в DOM он
представлен как элемент внутри overlay-pane.
const marker = L.marker([51.5, -0.09]).addTo(map);
marker.bindPopup('Hello');
Проверки в E2E тестах:
cy.get('.leaflet-marker-icon').should('exist');
cy.get('.leaflet-marker-icon').click();
cy.get('.leaflet-popup').should('contain', 'Hello');
Leaflet активно использует слои: tile layers, vector layers, overlays.
Тестирование слоёв включает:
const layer = L.tileLayer(url).addTo(map);
map.removeLayer(layer);
Проверка в E2E:
cy.get('.leaflet-tile').should('exist');
или проверка исчезновения слоя:
cy.get('.leaflet-tile').should('not.exist');
GeoJSON слои требуют проверки геометрии и интерактивности.
L.geoJSON(data, {
onEachFeature: function (feature, layer) {
layer.bindPopup(feature.properties.name);
}
}).addTo(map);
E2E проверки:
cy.get('path.leaflet-interactive').should('exist');
cy.get('path.leaflet-interactive').first().click();
cy.get('.leaflet-popup').should('be.visible');
Leaflet сильно зависит от асинхронных операций:
Неправильные ожидания приводят к флаки тестам.
Используются стратегии:
should.Пример:
cy.get('.leaflet-tile-loaded', { timeout: 10000 }).should('exist');
Взаимодействие с картой — ключевой сценарий E2E.
map.setView([51.505, -0.09], 10);
map.zoomIn();
map.panTo([52.0, -0.1]);
В тестах:
cy.get('.leaflet-container').trigger('wheel', { deltaY: -100 });
cy.get('.leaflet-container').trigger('mousedown', { which: 1 });
cy.get('.leaflet-container').trigger('mousemove', 200, 200);
cy.get('.leaflet-container').trigger('mouseup');
describe('Leaflet map E2E', () => {
it('loads map and displays marker', () => {
cy.visit('/map');
cy.intercept('GET', '**/*.png', { fixture: 'tile.png' });
cy.get('#map').should('exist');
cy.get('.leaflet-marker-icon', { timeout: 10000 })
.should('be.visible')
.click();
cy.get('.leaflet-popup')
.should('contain', 'Location');
});
});
import { test, expect } from '@playwright/test';
test('leaflet map renders', async ({ page }) => {
await page.route('**/*.png', route => {
route.fulfill({ path: 'fixtures/tile.png' });
});
await page.goto('/map');
const marker = page.locator('.leaflet-marker-icon');
await expect(marker).toBeVisible();
await marker.click();
await expect(page.locator('.leaflet-popup')).toBeVisible();
});
Типичные причины нестабильности:
Методы стабилизации:
.leaflet-fade-anim .leaflet-tile {
transition: none !important;
}
В headless браузерах возможны отличия:
Решения:
requestAnimationFrame-циклов;Для картографических интерфейсов часто применяется snapshot-тестирование:
Проблема — динамические тайлы, поэтому:
Карта — тяжёлый DOM/Canvas объект, поэтому важно:
Метрики: