--- type: test-plan work_item_id: ET-006 title: "Test Plan: Загрузка и визуализация GPX-треков" version: 2 status: approved created_at: 2026-05-22 updated_at: 2026-05-22 authors: - "agent:analyst" test_suites: - name: unit-gpx-parser type: unit description: "Парсинг GPX XML → внутренняя модель" cases: - id: U-01 name: "Парсинг валидного GPX 1.1 с одним треком" input: "GPX-файл с 1 trk, 1 trkseg, 10 trkpt (lat, lon, ele, time)" expected: "Возвращает объект с 1 треком, 10 точками, корректными координатами и высотами" - id: U-02 name: "Парсинг GPX с несколькими треками" input: "GPX-файл с 3 trk" expected: "Возвращает массив из 3 треков" - id: U-03 name: "Парсинг waypoints" input: "GPX-файл с 5 wpt (lat, lon, name, ele)" expected: "Возвращает массив из 5 waypoints с именами и координатами" - id: U-04 name: "Парсинг route (rte)" input: "GPX-файл с 1 rte, 20 rtept" expected: "Возвращает как трек с 20 точками" - id: U-05 name: "GPX без данных высот" input: "GPX-файл с trkpt без " expected: "Точки имеют ele=null, stats.elevGain=null" - id: U-06 name: "Невалидный XML" input: "Файл с битым XML" expected: "Выбрасывает ошибку с сообщением" - id: U-07 name: "Пустой GPX (нет trk, wpt, rte)" input: "Валидный XML, но без данных" expected: "Выбрасывает ошибку 'no data'" - id: U-08 name: "GPX с namespace и без" input: "GPX без xmlns атрибута" expected: "Парсится корректно (fallback без namespace)" - name: unit-gpx-stats type: unit description: "Расчёт статистики трека" cases: - id: U-10 name: "Расчёт длины (Haversine)" input: "Трек из 3 точек: [37.6, 55.7], [37.7, 55.8], [37.8, 55.9]" expected: "Длина ≈ 28.3 км (±0.5 км)" - id: U-11 name: "Расчёт набора высоты" input: "Точки с ele: [100, 150, 120, 200, 180]" expected: "elevGain = 130 м (50 + 80), elevLoss = 70 м (30 + 20)" - id: U-12 name: "Фильтрация шума высот (дельта < 2м)" input: "Точки с ele: [100, 101, 100, 101, 150]" expected: "elevGain = 50 м (только 100→150), мелкие колебания игнорируются" - id: U-13 name: "Мин/макс высота" input: "Точки с ele: [100, 250, 80, 300, 150]" expected: "eleMin=80, eleMax=300" - id: U-14 name: "Статистика без данных высот" input: "Точки без ele" expected: "distanceKm рассчитан, elevGain/elevLoss/eleMin/eleMax = null" - name: unit-gpx-colors type: unit description: "Назначение цветов из палитры" cases: - id: U-20 name: "Первый файл получает первый цвет" input: "Загрузка первого файла" expected: "Цвет = #e6194b" - id: U-21 name: "Девятый файл получает первый цвет (цикл)" input: "Загрузка 9-го файла" expected: "Цвет = #e6194b (индекс 8 % 8 = 0)" - name: integration-gpx-map type: integration description: "Интеграция GPX с MapLibre" cases: - id: I-01 name: "Добавление source и layer при загрузке" input: "Загрузка валидного GPX" expected: "map.getSource(sourceId) !== null, map.getLayer(layerId) !== null" - id: I-02 name: "Удаление source и layer при удалении трека" input: "Удаление загруженного трека" expected: "map.getSource(sourceId) === null, map.getLayer(layerId) === null" - id: I-03 name: "Fit bounds после загрузки" input: "Загрузка GPX с bbox [37.5, 55.6, 37.9, 55.9]" expected: "map.getBounds() содержит указанный bbox" - id: I-04 name: "Waypoints как маркеры" input: "GPX с 3 waypoints" expected: "На карте 3 маркера с подписями" - id: I-05 name: "Клик по треку активирует его" input: "Клик на линию трека" expected: "Трек становится активным, показывается статистика" - id: I-06 name: "GPX-слои ниже маршрута OSRM" input: "Загружен GPX + построен маршрут" expected: "Layer order: gpx-layer before route-layer" - id: I-07 name: "Треки сохраняются после setStyle (переключение стиля карты)" input: "Загружен GPX-трек (линия + waypoints), вызывается switchMapStyle() / map.setStyle()" expected: "После события idle: map.getLayer(layerId) !== null, map.getSource(sourceId) !== null, waypoint-маркеры присутствуют, активный трек и его статистика/профиль сохранены" - name: integration-gpx-elevation type: integration description: "Профиль высот" cases: - id: I-10 name: "Рендеринг canvas профиля" input: "Активный трек с 100 точками и ele" expected: "Canvas отрисован, ширина = ширина контейнера, высота = 120px" - id: I-11 name: "Tooltip при наведении" input: "Mousemove по canvas на позиции 50%" expected: "Tooltip показывает высоту и расстояние средней точки" - id: I-12 name: "Маркер-курсор на карте при наведении на профиль" input: "Mousemove по canvas" expected: "На карте появляется маркер в соответствующей точке трека" - name: e2e-gpx-workflow type: e2e description: "Полный пользовательский сценарий" cases: - id: E-01 name: "Загрузка → визуализация → статистика → удаление" steps: - "Открыть приложение" - "Нажать кнопку GPX в правой панели" - "Выбрать файл test-track.gpx (1 трек, 500 точек, с ele)" - "Убедиться: трек на карте, панель открыта, статистика показана" - "Проверить профиль высот" - "Удалить трек" - "Убедиться: карта пуста, панель пуста" - id: E-02 name: "Множественная загрузка и различение цветов" steps: - "Загрузить 3 GPX-файла последовательно" - "Убедиться: 3 трека на карте разных цветов" - "Убедиться: 3 записи в панели" - "Кликнуть на второй трек в панели" - "Убедиться: показана статистика второго трека" - id: E-03 name: "Большой файл (50 МБ)" steps: - "Загрузить GPX-файл ~50 МБ" - "Убедиться: показан индикатор загрузки" - "Убедиться: трек отображается после парсинга" - "Убедиться: pan/zoom работают без фризов" - id: E-04 name: "Файл с waypoints" steps: - "Загрузить GPX с 5 waypoints" - "Убедиться: 5 маркеров на карте с подписями" - "Удалить трек" - "Убедиться: маркеры исчезли" - id: E-05 name: "GPX параллельно с роутингом" steps: - "Построить маршрут через OSRM" - "Загрузить GPX-файл" - "Убедиться: оба отображаются, маршрут выше GPX" - "Удалить GPX" - "Убедиться: маршрут не затронут" - id: E-06 name: "Ошибки: невалидный файл, превышение лимита" steps: - "Попытаться загрузить .txt файл переименованный в .gpx" - "Убедиться: toast с ошибкой" - "Попытаться загрузить файл > 50 МБ" - "Убедиться: toast с ошибкой" - "Убедиться: предыдущие треки не затронуты" - id: E-07 name: "Мобильное устройство (touch)" steps: - "Открыть на мобильном (или эмуляция)" - "Загрузить GPX" - "Тапнуть на трек в панели" - "Тапнуть на профиль высот" - "Убедиться: tooltip и маркер-курсор работают" - name: e2e-gpx-toolbar type: e2e description: "Кнопка GPX в toolbar" cases: - id: E-10 name: "Переключение панели через toolbar" steps: - "Нажать кнопку GPX в нижнем тулбаре" - "Убедиться: панель GPX открылась" - "Нажать ещё раз" - "Убедиться: панель свернулась" test_data: - name: "test-track-simple.gpx" description: "1 трек, 10 точек, с ele и time" - name: "test-track-multi.gpx" description: "3 трека в одном файле" - name: "test-track-waypoints.gpx" description: "1 трек + 5 waypoints с именами" - name: "test-track-no-ele.gpx" description: "1 трек без данных высот" - name: "test-track-large.gpx" description: "~50 МБ, 500K+ точек" - name: "test-track-invalid.gpx" description: "Битый XML" - name: "test-track-empty.gpx" description: "Валидный GPX без trk/wpt/rte" - name: "test-track-route.gpx" description: "GPX с вместо "