analyst(ET): auto-commit from analyst run_id=87
All checks were successful
CI / lint (push) Successful in 5s
CI / test (push) Successful in 10s
CI / build (push) Successful in 2s

This commit is contained in:
2026-06-04 11:03:45 +00:00
parent bf2c93021d
commit e796a6cb03
5 changed files with 775 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
# BRD — ET-014: Панель «Фильтры» открывается позади панели слоёв (z-index)
**Work Item:** ET-014
**Тип:** Bug / UX-fix
**Фаза:** PH-5 Redesign (затрагивает PH-8 / ET-008 — публичные GPS-треки)
**Приоритет:** High (блокирует функциональность фильтров публичных треков)
**Среды:** dev, test (https://openclaw.mva154.duckdns.org/enduro/)
---
## 1. Бизнес-контекст
В рамках PH-8 / ET-008 реализованы публичные GPS-треки с фильтрами по
активности, источнику и цвету линий. Доступ к фильтрам — через ссылку
«Фильтры…» внутри панели слоёв (terrain-popup, кнопка-гора справа).
Сейчас на устройствах в реальной эксплуатации (mobile, viewport ~360414 px,
а также desktop) панель «Фильтры публичных треков» (`#sheet-gps-filters`)
открывается **позади** панели слоёв (`#terrain-popup`). Пользователь видит
только левую кромку sheet'а — основная часть с чекбоксами и сегментными
переключателями полностью перекрыта панелью слоёв.
В итоге **фильтрами публичных треков пользоваться невозможно**, хотя они
заявлены как готовая функция.
## 2. Проблема (как видит пользователь)
1. Пользователь открывает карту → жмёт кнопку «Рельеф» (иконка горы справа).
2. Открывается панель слоёв (Подложка / Эндуро / Публичные треки / POI).
3. Включает чекбокс «Публичные треки» → появляется ссылка «Фильтры…».
4. Жмёт «Фильтры…» → ожидает увидеть панель фильтров.
5. **Факт:** панель фильтров появляется снизу, но **скрыта за** панелью
слоёв. На мобильном видна узкая левая полоска, на desktop — частично
видно содержимое слева, основной блок недоступен.
6. Кликнуть по чекбоксам/кнопкам фильтра нельзя — клики ловит панель слоёв.
Подтверждение: скриншот мобильного браузера в зоне Москвы, zoom 12.
## 3. Бизнес-цель
Сделать фильтры публичных треков **реально доступными** для пользователя
с обеих сред (мобильной и десктопной), без визуальных артефактов при
открытии и закрытии.
## 4. Бизнес-требования
| ID | Требование |
|-------|------------|
| BR-01 | При нажатии «Фильтры…» панель фильтров должна быть полностью видна и интерактивна на mobile и desktop. |
| BR-02 | Панель слоёв (terrain-popup) не должна визуально перекрывать панель фильтров. |
| BR-03 | Закрытие фильтров (кнопкой «✕», свайпом или кликом по backdrop на mobile) возвращает пользователя к карте без артефактов наложения. |
| BR-04 | Поведение остальных bottom-sheets (маршрут, разведка, связка, красивый, GPX) **не должно регрессировать**. |
| BR-05 | Поведение `terrain-popup` для остальных кейсов (открытие/закрытие, чекбоксы рельефа, переключатели подложки/единиц) **не должно регрессировать**. |
| BR-06 | Решение должно одинаково работать в светлой и тёмной теме. |
## 5. Не входит в scope
- Редизайн панели слоёв или панели фильтров.
- Изменение состава фильтров или логики `gps_tracks.js`.
- Изменение позиционирования `terrain-popup` относительно кнопки «Рельеф».
- Добавление новых способов открытия фильтров (например, отдельной кнопки
на toolbar).
## 6. Стейкхолдеры
- Owner / PM проекта enduro-trails — приёмка.
- Конечные пользователи (райдеры) — пользуются фильтрами публичных треков
с мобильных устройств.
## 7. Метрики успеха
- Ручная проверка на mobile (viewport 360414) и desktop (≥1024) — фильтры
открываются полностью видимыми и кликабельными.
- UI e2e тест-кейсы из 04b-ui-test-cases.md проходят на обеих средах.
- Сценарий «открыть слои → включить публичные треки → открыть фильтры →
изменить активность → закрыть» выполняется без визуальных дефектов.
## 8. Допущения
- Используется текущая HTML-структура: `#terrain-popup` (position:fixed,
z-index:500) и `#sheet-gps-filters` (`.bottom-sheet`, z-index:400),
`#sheet-backdrop` (z-index:390).
- Открытие фильтров инициируется только из `togglePublicTracksFiltersSheet()`
(gps_tracks.js); других точек входа сейчас нет.
## 9. Риски
| ID | Риск | Митигация |
|-----|------|-----------|
| R1 | Изменение z-index может задеть другие оверлеи (marker-dialog z=500, search-panel/ruler-info z=600). | В тест-плане отдельно проверить эти оверлеи. |
| R2 | Закрытие terrain-popup при открытии фильтров может удивить пользователя — потеряет состояние «панель слоёв открыта». | Допустимо: панель слоёв — точка входа в фильтры, после закрытия фильтров пользователь возвращается к карте, а не к панели слоёв. Решение архитектора. |
| R3 | На desktop sheet-backdrop скрыт (`display:none` в media-query); если решение опирается на backdrop — нужна проверка desktop отдельно. | Тест-кейс на desktop обязателен. |

View File

@@ -0,0 +1,121 @@
# ТРЗ — ET-014: Z-index конфликт terrain-popup vs sheet-gps-filters
**Work Item:** ET-014
**Связан с BRD:** 01-brd.md
**Тип задачи:** Bug-fix (UI / стили / DOM-stacking)
---
## 1. Анализ текущего состояния
### 1.1 DOM-структура (как есть)
- `#terrain-popup` (`src/web/index.html:43`) — `position: fixed`, `z-index: 500`
(`src/web/app.css:785-795`). Открывается по клику на кнопку «Рельеф»
(`#terrain-toggle` в `#map-controls-r`). Содержит чекбоксы слоёв,
переключатели подложки и единиц, а также кнопку-ссылку
`#public-tracks-filters-btn` с текстом «Фильтры…».
- `#sheet-gps-filters` (`src/web/index.html:478`) — класс `.bottom-sheet`,
`position: fixed`, `z-index: 400` (`src/web/app.css:183-196`). Открывается
через `togglePublicTracksFiltersSheet()` в `src/web/gps_tracks.js:737`,
который вызывает `openSheet('sheet-gps-filters')`.
- `#sheet-backdrop` (`src/web/index.html:19`) — `z-index: 390`
(`src/web/app.css:222-228`). На mobile перекрывает экран при открытом
sheet'е; на desktop скрыт (`#sheet-backdrop { display: none; }` в
media-query, `src/web/app.css:543`).
### 1.2 Стек z-index в проекте (для ориентира)
| Элемент | z-index | Файл/строка |
|-------------------|---------|-------------------------|
| `#map` | 0 | app.css:68 |
| `#no-data-warning`| 200 | app.css:410 |
| `#sheet-backdrop` | 390 | app.css:225 |
| `.bottom-sheet` | 400 | app.css:188 |
| `#map-controls-r` | 400 | app.css:129 |
| `.terrain-popup` | **500** | app.css:787 |
| `#marker-dialog` | 500 | app.css:399 |
| `#search-panel` | 600 | app.css:1101 |
| `#ruler-info` | 600 | app.css:1122 |
### 1.3 Корень проблемы
1. `togglePublicTracksFiltersSheet()` открывает sheet (z=400), но **не
закрывает** `#terrain-popup` (z=500). Popup остаётся на экране и
визуально/event-but перекрывает sheet.
2. Клик по ссылке «Фильтры…» внутри popup не триггерит
`closeTerrainOnOutside` (popup.contains(target) === true), поэтому popup
не закрывается сам.
3. Backdrop sheet'а (z=390) тоже ниже popup'а (z=500), поэтому даже на
mobile нет визуальной индикации, что popup стал «фоном».
## 2. Требования к решению
### 2.1 Функциональные (REQ-F)
| ID | Требование |
|------------|------------|
| REQ-F-01 | При открытии `#sheet-gps-filters` из «Фильтры…» панель `#terrain-popup` НЕ должна перекрывать sheet ни визуально, ни для событий ввода. |
| REQ-F-02 | Когда `#sheet-gps-filters` открыт, состояние кнопки `#terrain-toggle` (класс `.active`) должно быть консистентно с состоянием popup: если popup скрывается / закрывается на время открытия фильтров — кнопка не должна оставаться визуально «прижатой». |
| REQ-F-03 | После закрытия `#sheet-gps-filters` (через `✕`, свайп вниз, клик по backdrop на mobile, либо `closeAllSheets()`) пользователь возвращается к карте. Возврат панели слоёв — на усмотрение архитектора (см. §3 «Варианты решения»). В любом случае не должно оставаться «фантомных» оверлеев / неактивных DOM в видимой области. |
| REQ-F-04 | Решение должно работать единообразно при инициации фильтров повторно (открыли → закрыли → открыли снова). |
| REQ-F-05 | Поведение `#terrain-popup` для всех других сценариев (открыть/закрыть кнопкой, кликнуть вне popup'а, переключить чекбокс/подложку/единицы) **не должно регрессировать**. |
| REQ-F-06 | Поведение остальных bottom-sheets (`#sheet-route`, `#sheet-recon`, `#sheet-scenic`, `#sheet-link`, `#sheet-gpx`) **не должно регрессировать**. |
| REQ-F-07 | Решение должно одинаково корректно работать в светлой и тёмной теме. |
### 2.2 Нефункциональные (REQ-NF)
| ID | Требование |
|-------------|------------|
| REQ-NF-01 | Изменения локализованы во фронте (`src/web/`). Backend (`src/api/`) не затрагивается. |
| REQ-NF-02 | Нет регрессий по производительности (никаких новых тяжёлых обработчиков resize/scroll). |
| REQ-NF-03 | Если решение меняет z-index — оно не должно ломать стекинг `#marker-dialog` (z=500), `#search-panel` (z=600), `#ruler-info` (z=600). |
| REQ-NF-04 | Решение совместимо с PWA-режимом (PH-9, в работе): в standalone display и при наличии safe-area-inset. |
| REQ-NF-05 | Решение работает на mobile viewport 360414 px (Chrome Android), desktop ≥1024 px (Chrome desktop). |
## 3. Варианты решения (на усмотрение архитектора)
> Аналитик не выбирает архитектуру. Перечисляю опции, которые могут быть
> рассмотрены реализатором/архитектором:
- **Вариант A — закрывать `#terrain-popup` при открытии sheet-gps-filters.**
В `togglePublicTracksFiltersSheet()` перед `openSheet(...)` явно скрыть
popup (как делает `closeTerrainOnOutside`) и снять `.active` с
`#terrain-toggle`. Backdrop sheet'а корректно затемнит фон на mobile.
- **Вариант B — поднять z-index sheet'ов выше terrain-popup.** Например,
`.bottom-sheet { z-index: 510; }` и `#sheet-backdrop { z-index: 505; }`.
Тогда sheet физически окажется поверх popup'а. Требует проверки на не-
конфликт с marker-dialog (z=500) и не-перекрытие toolbar / search-panel.
- **Вариант C — точечно поднять z-index только `#sheet-gps-filters` и его
backdrop.** Узкий хак: `#sheet-gps-filters { z-index: 510; }`. Менее
системно, но минимальные риски регрессии для других sheet'ов.
Решение фиксируется архитектором в ADR работы (`06-adr/`).
## 4. Acceptance hooks
См. полные критерии в `03-acceptance-criteria.md`.
Краткая выжимка:
- Открытие фильтров → панель полностью видна, кликабельна (mobile и
desktop).
- Панель слоёв не перекрывает фильтры (визуально и для событий).
- Закрытие фильтров → возврат к карте без артефактов.
- Остальные оверлеи (marker-dialog, search-panel, ruler-info, остальные
sheets) — без регрессий.
## 5. Тесты
См. `04-test-plan.yaml` (функциональные тесты) и
`04b-ui-test-cases.md` (Playwright UI тест-кейсы).
## 6. Артефакты для модификации (ожидание аналитика)
- `src/web/app.css` — стили stacking-context (если выбран вариант B/C).
- `src/web/gps_tracks.js` — логика `togglePublicTracksFiltersSheet()`
(если выбран вариант A).
- Возможно `src/web/app.js` — если в `openSheet` / `closeAllSheets`
требуется хук «при открытии sheet закрыть popup» как универсальное
решение для будущих кейсов.
Это рекомендация, конкретный набор файлов определит архитектор.

View File

@@ -0,0 +1,124 @@
# Acceptance Criteria — ET-014
**Work Item:** ET-014
**Связаны:** BR-01…BR-06 (01-brd.md), REQ-F-01…REQ-F-07 (02-trz.md)
Формат: Given / When / Then.
---
## AC-01: Открытие фильтров на mobile — sheet полностью виден поверх
**Покрывает:** BR-01, BR-02, REQ-F-01, REQ-F-05
- **Given** мобильный viewport 390×844, тёмная тема, карта https://openclaw.mva154.duckdns.org/enduro/ загружена и стабилизирована (зум по умолчанию).
- **When** пользователь:
1. Кликает кнопку `#terrain-toggle` («Рельеф»).
2. Включает чекбокс `#public-tracks-cb` («Публичные треки»).
3. Кликает кнопку `#public-tracks-filters-btn` («Фильтры…»).
- **Then**
- `#sheet-gps-filters` имеет класс `open` (DOM-проверка).
- Заголовок «Фильтры публичных треков», секция «ТИП АКТИВНОСТИ» и кнопка `✕` полностью видны в viewport и кликабельны (visible & in front, no element with higher stacking covers them).
- Никакая часть `#terrain-popup` не визуально перекрывает `#sheet-gps-filters` в области sheet'а (скриншот-сравнение).
## AC-02: Открытие фильтров на desktop — sheet полностью виден поверх
**Покрывает:** BR-01, BR-02, REQ-F-01, REQ-NF-05
- **Given** desktop viewport 1440×900, любая тема.
- **When** те же шаги что в AC-01.
- **Then** sheet «Фильтры публичных треков» отображается слева (как другие sheets на desktop, ширина ≈ 380 px) и полностью видим. `#terrain-popup` не перекрывает sheet.
## AC-03: Кликабельность контролов внутри фильтров
**Покрывает:** BR-01, REQ-F-01
- **Given** AC-01 (фильтры открыты на mobile).
- **When** пользователь кликает на чекбоксы активностей внутри `#gps-activity-grid` и на сегментный переключатель «По источнику / По активности».
- **Then** клики срабатывают (визуальное состояние чекбокса/кнопки меняется). Никакой невидимый слой не «съедает» события.
## AC-04: Закрытие фильтров кнопкой ✕ — без артефактов
**Покрывает:** BR-03, REQ-F-03
- **Given** фильтры открыты (AC-01).
- **When** пользователь кликает кнопку `✕` в шапке `#sheet-gps-filters`.
- **Then**
- `#sheet-gps-filters` теряет класс `open`, скрывается.
- На viewport не остаётся видимых частей панели слоёв или sheet'а в полупрозрачном/частичном состоянии.
- Карта полностью интерактивна (свободно скроллится, zoom работает).
## AC-05: Закрытие фильтров кликом по backdrop (mobile)
**Покрывает:** BR-03, REQ-F-03
- **Given** фильтры открыты на mobile (AC-01).
- **When** пользователь тапает по затемнённой области выше sheet'а (`#sheet-backdrop`).
- **Then** sheet закрывается. Возврат к карте без артефактов.
## AC-06: Повторное открытие фильтров работает
**Покрывает:** REQ-F-04
- **Given** пользователь только что закрыл фильтры (AC-04 или AC-05).
- **When** повторяет шаги AC-01 (Рельеф → Публичные треки → Фильтры…).
- **Then** sheet снова открывается полностью видимым. Никаких залипших состояний кнопок / классов.
## AC-07: Чекбоксы рельефа в terrain-popup продолжают работать
**Покрывает:** BR-05, REQ-F-05
- **Given** карта загружена, фильтры не открывались в этой сессии.
- **When** пользователь открывает `#terrain-popup` и переключает `#terrain-hillshade-cb`, `#terrain-tri-cb`, `#trails-track-cb`, `#trails-path-cb`, `#poi-visible-cb`, переключатели подложки и единиц.
- **Then** все чекбоксы реагируют как раньше, popup остаётся открытым до клика вне popup'а. Регрессий нет.
## AC-08: Закрытие terrain-popup кликом вне popup'а
**Покрывает:** REQ-F-05
- **Given** `#terrain-popup` открыт.
- **When** пользователь кликает по карте или любой области вне popup'а и вне `#terrain-toggle`.
- **Then** popup закрывается (existing `closeTerrainOnOutside`). Класс `.active` с кнопки снимается.
## AC-09: Остальные bottom-sheets не регрессируют
**Покрывает:** BR-04, REQ-F-06
- **Given** карта загружена.
- **When** пользователь поочерёдно открывает `#sheet-route`, `#sheet-recon`, `#sheet-scenic`, `#sheet-link`, `#sheet-gpx` через тулбар.
- **Then** каждый sheet открывается, виден полностью, кнопки внутри работают, закрывается ✕ / свайпом / backdrop'ом без артефактов.
## AC-10: Marker-dialog не регрессирует
**Покрывает:** REQ-NF-03
- **Given** карта загружена.
- **When** пользователь активирует «Метка» в тулбаре, тапает по карте.
- **Then** `#marker-dialog` (z=500) открывается поверх всего, кликабелен. После выбора типа — закрывается без артефактов.
## AC-11: Search-panel не регрессирует
**Покрывает:** REQ-NF-03
- **Given** карта загружена.
- **When** пользователь нажимает «Поиск» в тулбаре, вводит запрос.
- **Then** `#search-panel` (z=600) виден полностью, ввод работает, результаты подгружаются.
## AC-12: Ruler-info не регрессирует
**Покрывает:** REQ-NF-03
- **Given** карта загружена.
- **When** пользователь активирует «Линейка», ставит точки.
- **Then** `#ruler-info` (z=600) виден поверх всего и кликабелен.
## AC-13: Светлая тема
**Покрывает:** BR-06, REQ-F-07
- **Given** mobile viewport, светлая тема (включена кнопкой `#btn-theme`).
- **When** повторяются шаги AC-01.
- **Then** результат идентичен AC-01: sheet поверх, всё видно, кликабельно. Никаких theme-specific артефактов.
## AC-14: Сценарий из тикета (мобильный, z12 Москва)
**Покрывает:** BR-01, BR-02 (прямое воспроизведение бага)
- **Given** мобильный viewport (390×844), карта на зуме 12 в центре около Москвы (lng=37.6, lat=55.75).
- **When** Рельеф → ✓ Публичные треки → Фильтры…
- **Then** Скриншот после открытия фильтров сопоставим с эталонным «good»: панель «Фильтры публичных треков» полностью видна; ни одна часть terrain-popup не находится поверх sheet'а в его координатах.
---
## Definition of Done
- Все AC-01…AC-14 проходят на test-среде https://openclaw.mva154.duckdns.org/enduro/.
- `make test` и `make lint` зелёные.
- UI-тесты из `04b-ui-test-cases.md` зелёные на CI (или в локальном Playwright прогоне).
- Owner подтвердил визуальную приёмку по скриншотам AC-01, AC-02, AC-14.

View File

@@ -0,0 +1,178 @@
# Test Plan — ET-014
# Z-index fix: панель «Фильтры» должна открываться поверх панели слоёв.
# Все тесты ориентированы на test-среду: https://openclaw.mva154.duckdns.org/enduro/
work_item: ET-014
related_acs: [AC-01, AC-02, AC-03, AC-04, AC-05, AC-06, AC-07, AC-08, AC-09, AC-10, AC-11, AC-12, AC-13, AC-14]
tests:
# ─── Unit ──────────────────────────────────────────────────────────
- id: TC-U-01
type: unit
layer: frontend
title: togglePublicTracksFiltersSheet корректно открывает/закрывает sheet
target: src/web/gps_tracks.js :: togglePublicTracksFiltersSheet
given: |
JSDOM с минимальным DOM: #sheet-gps-filters, #terrain-popup,
#sheet-backdrop, мок openSheet/closeAllSheets.
when: |
Вызвать togglePublicTracksFiltersSheet() дважды подряд.
then: |
- Первый вызов: openSheet('sheet-gps-filters') вызван 1 раз;
_buildGpsFiltersUI вызван.
- Второй вызов: closeAllSheets() вызван 1 раз.
covers: [REQ-F-04]
- id: TC-U-02
type: unit
layer: frontend
title: При открытии sheet-gps-filters состояние terrain-popup корректно
target: src/web/gps_tracks.js или общий хук в src/web/app.js
given: |
JSDOM: #terrain-popup со style.display='block' и #terrain-toggle.classList
содержит 'active'. #sheet-gps-filters существует.
when: |
Вызвать togglePublicTracksFiltersSheet() при открытом popup'е.
then: |
В зависимости от выбранного варианта решения:
- Вариант A: popup.style.display === 'none', terrain-toggle без 'active'.
- Вариант B/C: popup может оставаться открытым, но stacking-tests
ниже (TC-I-01) обязаны быть зелёными.
covers: [REQ-F-01, REQ-F-02]
# ─── Integration / DOM ─────────────────────────────────────────────
- id: TC-I-01
type: integration
layer: frontend
title: Stacking — sheet-gps-filters визуально выше terrain-popup
given: |
Полный DOM из src/web/index.html, app.css загружен, jsdom + getComputedStyle
или Playwright страница. terrain-popup открыт, sheet-gps-filters открыт.
when: |
Получить элемент в центре области #sheet-gps-filters через
document.elementFromPoint(x, y).
then: |
Возвращённый элемент принадлежит #sheet-gps-filters (или его потомкам),
НЕ принадлежит #terrain-popup.
covers: [REQ-F-01, AC-01, AC-02]
- id: TC-I-02
type: integration
layer: frontend
title: Stacking — marker-dialog поверх всего сохраняется
given: |
Полный DOM. marker-dialog открыт (style.display: flex), параллельно
моделируем «грязное» состояние (terrain-popup открыт).
when: |
document.elementFromPoint в координатах кнопки внутри marker-dialog.
then: |
Элемент принадлежит #marker-dialog.
covers: [REQ-NF-03, AC-10]
- id: TC-I-03
type: integration
layer: frontend
title: Stacking — search-panel и ruler-info остаются на верху (z=600)
given: |
Полный DOM, search-panel.display=block или ruler-info видим.
when: |
elementFromPoint в центре панели.
then: |
Возвращённый элемент принадлежит соответствующей панели,
НЕ перекрывается ни sheet'ом, ни terrain-popup.
covers: [REQ-NF-03, AC-11, AC-12]
- id: TC-I-04
type: integration
layer: frontend
title: Закрытие sheet-gps-filters через closeAllSheets очищает состояние
given: |
sheet-gps-filters.open, sheet-backdrop.visible.
when: |
Вызвать closeAllSheets().
then: |
- sheet-gps-filters без класса 'open'.
- sheet-backdrop без класса 'visible'.
- Никаких inline стилей-«артефактов» (например, лишних z-index, opacity).
covers: [REQ-F-03, AC-04]
# ─── E2E (Playwright; см. также 04b-ui-test-cases.md) ──────────────
- id: TC-E-01
type: e2e
layer: ui
title: Mobile — открыть фильтры публичных треков из панели слоёв
env: test
viewport: { width: 390, height: 844 }
steps_summary: |
open / wait map / click #terrain-toggle / click #public-tracks-cb /
click #public-tracks-filters-btn / assert sheet visible & on top
expected: |
sheet-gps-filters имеет class 'open'; visually центр sheet'а не
перекрыт terrain-popup (elementFromPoint).
covers: [AC-01, AC-03, AC-14]
reference: 04b-ui-test-cases.md :: TC-UI-01
- id: TC-E-02
type: e2e
layer: ui
title: Desktop — фильтры открываются слева, terrain-popup не перекрывает
env: test
viewport: { width: 1440, height: 900 }
expected: |
sheet-gps-filters виден слева (≈380px), terrain-popup не перекрывает.
covers: [AC-02]
reference: 04b-ui-test-cases.md :: TC-UI-02
- id: TC-E-03
type: e2e
layer: ui
title: Закрытие фильтров кнопкой ✕ возвращает к карте
env: test
viewport: { width: 390, height: 844 }
expected: |
Нет видимых частей sheet'а или backdrop'а после клика по ✕.
covers: [AC-04]
reference: 04b-ui-test-cases.md :: TC-UI-03
- id: TC-E-04
type: e2e
layer: ui
title: Повторное открытие/закрытие фильтров стабильно
env: test
viewport: { width: 390, height: 844 }
expected: |
После 3 циклов open/close — DOM-классы консистентны, sheet
продолжает открываться поверх terrain-popup.
covers: [AC-06]
reference: 04b-ui-test-cases.md :: TC-UI-04
- id: TC-E-05
type: e2e
layer: ui
title: Регрессия — открыть остальные bottom-sheets, проверить отображение
env: test
viewport: { width: 390, height: 844 }
expected: |
sheet-route, sheet-recon, sheet-scenic, sheet-link, sheet-gpx —
каждый открывается, виден, закрывается.
covers: [AC-09]
reference: 04b-ui-test-cases.md :: TC-UI-05
- id: TC-E-06
type: e2e
layer: ui
title: Светлая тема — сценарий открытия фильтров
env: test
viewport: { width: 390, height: 844 }
theme: light
expected: |
Sheet поверх terrain-popup, всё видно, контраст корректный.
covers: [AC-13]
reference: 04b-ui-test-cases.md :: TC-UI-06
# ─── Не входит ────────────────────────────────────────────────────────
out_of_scope:
- Тесты бизнес-логики фильтров (это покрывается ET-008/ET-009).
- Тесты позиционирования terrain-popup относительно кнопки «Рельеф».
- Производительность тайлов / роутинга.

View File

@@ -0,0 +1,260 @@
# UI Test Cases — ET-014
Playwright UI тест-кейсы для визуальной приёмки фикса z-index.
Все тесты выполняются на test-среде https://openclaw.mva154.duckdns.org/enduro/.
Общие соображения:
- Карта инициализируется ~24 секунды (MapLibre + загрузка стилей/тайлов).
Везде где идёт первый `navigate` — пауза 4000 мс перед действиями.
- Селекторы взяты из `src/web/index.html`.
---
### TC-UI-01 — Mobile: фильтры открываются ПОВЕРХ панели слоёв
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
- theme: dark (по умолчанию)
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. screenshot: 01-map-loaded
4. click: #terrain-toggle
5. wait: 400
6. screenshot: 02-terrain-popup-open
7. check-visual: видна панель `#terrain-popup` с чекбоксами; visible(`#public-tracks-cb`) === true
8. click: #public-tracks-cb
9. wait: 300
10. check-visual: visible(`#public-tracks-filters-btn`) === true (кнопка «Фильтры…» появилась)
11. click: #public-tracks-filters-btn
12. wait: 600
13. screenshot: 03-filters-sheet-opened
14. check-visual: `#sheet-gps-filters` имеет класс `open`; заголовок «Фильтры публичных треков», секции «ТИП АКТИВНОСТИ», «ИСТОЧНИК», «ЦВЕТ ЛИНИЙ» и кнопка `✕` полностью видны в viewport
15. check-visual: `document.elementFromPoint(195, 600)` принадлежит `#sheet-gps-filters` или его потомкам (НЕ `#terrain-popup`)
16. check-visual: bounding box `#sheet-gps-filters` не пересекается с видимой частью `#terrain-popup`, либо если пересекается — sheet поверх (через elementFromPoint в центрах пересечения)
Ожидаемый результат: панель фильтров полностью видна, ничем не перекрыта.
---
### TC-UI-02 — Desktop: фильтры открываются ПОВЕРХ панели слоёв
- type: ui
- viewport: desktop
- viewport-size: 1440 × 900
- theme: dark
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #terrain-toggle
4. wait: 400
5. click: #public-tracks-cb
6. wait: 300
7. click: #public-tracks-filters-btn
8. wait: 600
9. screenshot: desktop-filters-opened
10. check-visual: `#sheet-gps-filters` виден слева (получить bbox через `getBoundingClientRect`, ожидание: left ≤ 80, right ≥ 380)
11. check-visual: `document.elementFromPoint(bbox.left + bbox.width/2, bbox.top + bbox.height/2)` принадлежит `#sheet-gps-filters` или его потомкам
Ожидаемый результат: на desktop sheet открыт как боковая панель, terrain-popup не перекрывает.
---
### TC-UI-03 — Закрытие фильтров кнопкой ✕
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #terrain-toggle
4. wait: 300
5. click: #public-tracks-cb
6. wait: 300
7. click: #public-tracks-filters-btn
8. wait: 500
9. click: #sheet-gps-filters .sheet-close
10. wait: 600
11. screenshot: after-close
12. check-visual: `#sheet-gps-filters` НЕ имеет класса `open`
13. check-visual: `#sheet-backdrop` НЕ имеет класса `visible`
14. check-visual: `document.elementFromPoint(195, 600)` принадлежит `#map` или его canvas-потомку (карта снова интерактивна)
Ожидаемый результат: возврат к карте, никаких артефактов.
---
### TC-UI-04 — Повторное открытие/закрытие фильтров
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #terrain-toggle
4. wait: 300
5. click: #public-tracks-cb
6. wait: 300
7. click: #public-tracks-filters-btn
8. wait: 500
9. click: #sheet-gps-filters .sheet-close
10. wait: 500
11. click: #terrain-toggle
12. wait: 300
13. click: #public-tracks-filters-btn
14. wait: 500
15. screenshot: second-open
16. check-visual: `#sheet-gps-filters` имеет класс `open`, виден полностью, элемент в центре sheet'а через elementFromPoint принадлежит sheet'у
17. click: #sheet-gps-filters .sheet-close
18. wait: 500
19. click: #terrain-toggle
20. wait: 300
21. click: #public-tracks-filters-btn
22. wait: 500
23. check-visual: третий цикл — sheet снова открыт корректно
Ожидаемый результат: 3 цикла open/close без деградации.
---
### TC-UI-05 — Регрессия остальных bottom-sheets
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #tb-route
4. wait: 400
5. check-visual: `#sheet-route` имеет класс `open`, заголовок «Маршрут» виден
6. screenshot: sheet-route
7. click: #sheet-route .sheet-close
8. wait: 400
9. click: #tb-recon
10. wait: 400
11. check-visual: `#sheet-recon` имеет класс `open`
12. screenshot: sheet-recon
13. click: #sheet-recon .sheet-close
14. wait: 400
15. click: #tb-scenic
16. wait: 400
17. check-visual: `#sheet-scenic` имеет класс `open`
18. screenshot: sheet-scenic
19. click: #sheet-scenic .sheet-close
20. wait: 400
21. click: #tb-link
22. wait: 400
23. check-visual: `#sheet-link` имеет класс `open`
24. screenshot: sheet-link
25. click: #sheet-link .sheet-close
26. wait: 400
27. click: #tb-gpx
28. wait: 400
29. check-visual: `#sheet-gpx` имеет класс `open`
30. screenshot: sheet-gpx
31. click: #sheet-gpx .sheet-close
32. wait: 400
Ожидаемый результат: все sheet'ы открываются и закрываются без артефактов и не «застревают».
---
### TC-UI-06 — Светлая тема: фильтры поверх
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
- theme: light
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #btn-theme
4. wait: 500
5. check-visual: `document.body` НЕ содержит класса `theme-dark` (или содержит `theme-light`)
6. screenshot: 01-light-theme
7. click: #terrain-toggle
8. wait: 300
9. click: #public-tracks-cb
10. wait: 300
11. click: #public-tracks-filters-btn
12. wait: 500
13. screenshot: 02-light-filters-open
14. check-visual: `#sheet-gps-filters` имеет класс `open`, текст читаем (контраст), sheet полностью виден
15. check-visual: elementFromPoint в центре sheet'а возвращает элемент внутри `#sheet-gps-filters`
Ожидаемый результат: поведение полностью аналогично тёмной теме, без визуальных дефектов на светлом фоне.
---
### TC-UI-07 — Регрессия: terrain-popup сам по себе работает
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #terrain-toggle
4. wait: 300
5. screenshot: terrain-popup
6. check-visual: `#terrain-popup` style.display !== 'none'; `#terrain-toggle` имеет класс `active`
7. click: #terrain-hillshade-cb
8. wait: 300
9. check-visual: popup всё ещё открыт; чекбокс перешёл в состояние checked
10. click: #base-btn-satellite
11. wait: 600
12. check-visual: popup всё ещё открыт; кнопка `#base-btn-satellite` имеет класс `active`
13. click: #map // клик по карте вне popup
14. wait: 400
15. check-visual: `#terrain-popup` style.display === 'none'; `#terrain-toggle` БЕЗ класса `active`
Ожидаемый результат: без регрессий — popup ведёт себя как раньше.
---
### TC-UI-08 — Регрессия: marker-dialog поверх
- type: ui
- viewport: mobile
- viewport-size: 390 × 844
Шаги:
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
2. wait: 4000
3. click: #tb-marker
4. wait: 400
5. click: #map // тап по карте чтобы открыть dialog выбора типа метки
6. wait: 500
7. screenshot: marker-dialog
8. check-visual: `#marker-dialog` виден (computed style: opacity > 0)
9. check-visual: elementFromPoint в центре dialog'а возвращает элемент внутри `#marker-dialog`
Ожидаемый результат: marker-dialog корректно поверх всего.
---
## Helpers / Assertions
Для check-visual использовать:
- `await page.locator(selector).isVisible()` для базовой видимости.
- `await page.evaluate(() => document.elementFromPoint(x, y)?.closest('#sheet-gps-filters')?.id)` для проверки stacking.
- `await page.locator('#sheet-gps-filters').evaluate(el => el.classList.contains('open'))` для DOM-классов.
- `await expect(page).toHaveScreenshot(...)` если используется baseline-сравнение.
Скриншоты сохранять в `tests/e2e/__screenshots__/ET-014/<TC-UI-XX>/<step>.png`.