From e796a6cb03ab567db16c73a4da17d7f773611600 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 4 Jun 2026 11:03:45 +0000 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=87 --- docs/work-items/ET-014/01-brd.md | 92 +++++++ docs/work-items/ET-014/02-trz.md | 121 ++++++++ .../ET-014/03-acceptance-criteria.md | 124 +++++++++ docs/work-items/ET-014/04-test-plan.yaml | 178 ++++++++++++ docs/work-items/ET-014/04b-ui-test-cases.md | 260 ++++++++++++++++++ 5 files changed, 775 insertions(+) create mode 100644 docs/work-items/ET-014/01-brd.md create mode 100644 docs/work-items/ET-014/02-trz.md create mode 100644 docs/work-items/ET-014/03-acceptance-criteria.md create mode 100644 docs/work-items/ET-014/04-test-plan.yaml create mode 100644 docs/work-items/ET-014/04b-ui-test-cases.md diff --git a/docs/work-items/ET-014/01-brd.md b/docs/work-items/ET-014/01-brd.md new file mode 100644 index 0000000..59bbaec --- /dev/null +++ b/docs/work-items/ET-014/01-brd.md @@ -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 ~360–414 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 360–414) и 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 обязателен. | diff --git a/docs/work-items/ET-014/02-trz.md b/docs/work-items/ET-014/02-trz.md new file mode 100644 index 0000000..43efdea --- /dev/null +++ b/docs/work-items/ET-014/02-trz.md @@ -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 360–414 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» как универсальное + решение для будущих кейсов. + +Это рекомендация, конкретный набор файлов определит архитектор. diff --git a/docs/work-items/ET-014/03-acceptance-criteria.md b/docs/work-items/ET-014/03-acceptance-criteria.md new file mode 100644 index 0000000..257aaa3 --- /dev/null +++ b/docs/work-items/ET-014/03-acceptance-criteria.md @@ -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. diff --git a/docs/work-items/ET-014/04-test-plan.yaml b/docs/work-items/ET-014/04-test-plan.yaml new file mode 100644 index 0000000..b14dde1 --- /dev/null +++ b/docs/work-items/ET-014/04-test-plan.yaml @@ -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 относительно кнопки «Рельеф». + - Производительность тайлов / роутинга. diff --git a/docs/work-items/ET-014/04b-ui-test-cases.md b/docs/work-items/ET-014/04b-ui-test-cases.md new file mode 100644 index 0000000..47ed361 --- /dev/null +++ b/docs/work-items/ET-014/04b-ui-test-cases.md @@ -0,0 +1,260 @@ +# UI Test Cases — ET-014 + +Playwright UI тест-кейсы для визуальной приёмки фикса z-index. +Все тесты выполняются на test-среде https://openclaw.mva154.duckdns.org/enduro/. + +Общие соображения: +- Карта инициализируется ~2–4 секунды (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//.png`.