---
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 без <ele>"
        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 с <rte> вместо <trk>"
