16 KiB
type, work_item_id, version, status, tester, date, commit_tested, verdict
| type | work_item_id | version | status | tester | date | commit_tested | verdict |
|---|---|---|---|---|---|---|---|
| test-report | ET-005 | 1 | pass | agent:tester | 2026-05-21 | 2fe5cfe |
PASS |
Test Report — ET-005
Verdict: PASS → stage:ready-to-deploy
Полный регресс зелёный: pytest 31 passed, 4 skipped, 0 failed;
JS-юнит-тесты units.js 20/20 pass; e2e Playwright TP-01…TP-05
6/6 pass (0 JS-ошибок на странице); lint чистый; тест-окружение
отвечает 200. Блокирующих багов (P0/P1) не найдено.
Весь тест-план 04-test-plan.yaml (TP-01…TP-05) исполнен в реальном
браузере — в отличие от ET-002, e2e не блокирован. Все 4 acceptance-
критерия покрыты и не нарушены.
Окружение
- Дата прогона: 2026-05-21
- Ветка:
feature/ET-005- - Код-коммит:
2fe5cfe(feat(web): переключатель единиц измерения расстояний (км/мили); источник —12-review.md) - HEAD:
d32ad8f(reviewer(ET): auto-commit ...) — поверх кода только артефакт ревью, изменений кода нет; тестировалось рабочее дерево - Python: 3.12.13
- pytest: 8.3.3 (plugins: asyncio-1.3.0, anyio-4.13.0)
- Node: v22.22.2 (
node --test) - Playwright: 1.60.0, Chromium headless (chromium-headless-shell v1223)
- ruff: установлен по
pyproject.toml [dev] - test-env: https://openclaw.mva154.duckdns.org/enduro/ → HTTP 200
Healthcheck
| Среда | URL | Код |
|---|---|---|
| local dev | http://localhost:5556/health | connection refused (dev не поднят — ОК, прогон оффлайн) |
| test | https://openclaw.mva154.duckdns.org/enduro/ | 200 |
ET-005 — фронтенд-изменение. В test задеплоен предыдущий код, поэтому
healthcheck подтверждает только живость окружения; фича попадёт в test
штатной перевыкладкой src/web/ после merge (см.
07-infra-requirements.md §7). e2e-прогон выполнен против кода ветки,
поднятого локально статическим сервером (python -m http.server →
src/web/), не против test-окружения. На prom ничего не запускалось.
Команды запуска
# Unit + integration (эквивалент make test)
python -m pytest tests/ -v
# JS behavioral unit-тесты units.js (TP-01..TP-04, AC-2, AC-3)
node --test tests/unit/units.test.js
# e2e (TP-01..TP-05): локальная раздача src/web + Playwright/Chromium
python -m http.server 8777 --directory src/web &
python /tmp/et005_e2e.py
# Lint
ruff check src/ tests/
Результаты pytest
python -m pytest tests/ -v → 31 passed, 4 skipped, 1 warning in 0.62s
| Файл | Тестов | PASS | SKIP |
|---|---|---|---|
integration/test_routing_barriers.py |
7 | 3 | 4 |
unit/test_health.py |
1 | 1 | 0 |
unit/test_poi_toggle.py (ET-002, регресс) |
10 | 10 | 0 |
unit/test_unit_toggle.py (ET-005) |
17 | 17 | 0 |
| Итого | 35 | 31 | 4 |
ET-005 — test_unit_toggle.py (17/17 PASS):
| Тест | Покрывает | Результат |
|---|---|---|
test_units_module_exists |
наличие src/web/units.js (ADR-0001 п.2) |
PASS |
test_units_module_public_api |
контракт window.Units (ADR-0001 п.3) |
PASS |
test_units_module_constants |
KM_TO_MI=0.621371, distance_unit, дефолт km |
PASS |
test_units_module_exports_for_browser_and_node |
window.Units + module.exports |
PASS |
test_unit_toggle_present_in_html |
кнопка km/mi в попапе (ФТ-1, AC-1) | PASS |
test_unit_toggle_reuses_seg_control_component |
переиспользование .seg-control (R8) |
PASS |
test_units_js_loaded_before_app_js |
порядок скриптов (R7, ADR-0001 п.2) | PASS |
test_unit_toggle_has_styles |
стили .terrain-unit-row (AC-4) |
PASS |
test_app_js_unit_functions_defined |
onUnitToggle/syncUnitToggleUI/onUnitChange |
PASS |
test_app_js_has_et005_block_markers |
блок-маркеры >>> ET-005 ... >>> |
PASS |
test_app_js_single_unitchange_subscription |
ровно одна подписка unitchange (ADR п.6) |
PASS |
test_app_js_uses_centralized_formatter |
форматирование через Units.formatDistance |
PASS |
test_app_js_distance_helpers_delegate_to_units |
хелперы делегируют в units.js (R1) |
PASS |
test_app_js_scale_bar_is_unit_aware |
scale-bar учитывает единицу (R3) | PASS |
test_app_js_gpx_export_stays_metric |
GPX-экспорт остаётся метрическим (R6) | PASS |
test_app_js_restores_unit_choice_on_load |
восстановление выбора при загрузке (AC-3) | PASS |
test_js_unit_tests_pass |
запуск units.test.js через Node-раннер |
PASS |
4 SKIP — интеграционные тесты роутинга ET-001
(test_routing_barriers.py::test_route_*); требуют поднятого OSRM,
недоступного в окружении тестера (штатный skip, чтобы CI без
инфраструктуры не падал). ET-005 — фронтенд-изменение, на роутинг не
влияет; к регрессу не относится.
Предупреждение PytestDeprecationWarning (asyncio_default_fixture_loop_scope)
— внешняя зависимость pytest-asyncio, к ET-005 отношения не имеет, не
блокирует.
Результаты JS unit-тестов units.js
node --test tests/unit/units.test.js → # tests 20, # pass 20, # fail 0
Тесты исполняют реальный src/web/units.js (сброс require.cache +
инъекция моков window/document/localStorage перед каждым тестом).
Покрыты TP-01…TP-04, AC-2, AC-3, граница 1000 м, недоступный
localStorage, валидация setUnit(), публикация неймспейса, единый
разделитель «запятая» (R4), точность по умолчанию.
Результаты e2e (Playwright / Chromium) — TP-01…TP-05
Прогон в headless-Chromium против кода ветки, поднятого локально
(src/web/ через http.server). Взаимодействие — через реальные
DOM-клики по кнопкам попапа (onUnitToggle срабатывает по inline
onclick). Пересчёт видимых расстояний верифицирован на живой
масштабной линейке карты (#scale-zoom-bar), которую перерисовывает
оркестратор onUnitChange().
| TC | Сценарий | Факт | Результат |
|---|---|---|---|
| TP-01 | дефолт после очистки localStorage |
getUnit()='km', кнопка «км» .active, «мили» нет, localStorage=пусто, formatDistance(12345)='12,3 км' |
PASS |
| TP-02 | переключение в мили | getUnit()='mi', «мили» .active, localStorage='mi', formatDistance(12345)='7,7 ми', scale-bar '55 km'→'35 mi' |
PASS |
| TP-03 | persistence после reload | после перезагрузки getUnit()='mi', «мили» .active |
PASS |
| TP-04 | возврат в км | getUnit()='km', «км» .active, localStorage='km', scale-bar снова '55 km' |
PASS |
| TP-05 | mobile responsive 375px | обе кнопки видимы и в пределах вьюпорта (km x=166 w=57, mi x=226 w=57), клик переключает |
PASS |
| NFR-perf | переключение < 100 мс | клик + пересчёт всех поверхностей = 0,5 мс | PASS |
Итог e2e: 6/6 PASS. На странице не зафиксировано ни одной
JS-ошибки (pageerror за весь прогон — none).
Покрытие тест-плана (04-test-plan.yaml)
| TC | Тип | Исполнение | Статус |
|---|---|---|---|
| TP-01 | e2e | Playwright + JS-тест units.test.js |
PASS |
| TP-02 | e2e | Playwright (scale-bar km→mi) + JS-тест |
PASS |
| TP-03 | e2e | Playwright (reload) + JS-тест | PASS |
| TP-04 | e2e | Playwright (scale-bar mi→km) + JS-тест |
PASS |
| TP-05 | e2e | Playwright, viewport 375×667 | PASS |
Исполнено и пройдено: 5/5 тест-кейсов.
Соответствие Acceptance Criteria
| AC | Описание | Источник проверки | Статус |
|---|---|---|---|
| AC-1 | Кнопка km/mi в панели, показывает выбор, клик переключает | e2e TP-01 (км active по умолчанию), TP-02/TP-04 (клик переключает класс .active), test_unit_toggle_present_in_html |
PASS |
| AC-2 | Пересчёт всех расстояний, коэф. 0.621371, округление до 1 знака | e2e TP-02 (scale-bar 55 km→35 mi, formatDistance(12345)=12,3 км→7,7 ми), units.test.js (KM_TO_MI, точность 1 знак), test_units_module_constants |
PASS |
| AC-3 | Сохранение/восстановление из localStorage, дефолт km |
e2e TP-01 (дефолт km, localStorage пуст), TP-02 (localStorage='mi'), TP-03 (выживает reload) |
PASS |
| AC-4 | Кнопка не перекрывает элементы, mobile, переключение < 100мс | e2e TP-05 (375px, кнопки в пределах вьюпорта, кликабельны), NFR-perf (0,5 мс ≪ 100 мс), test_unit_toggle_has_styles |
PASS |
Все 4 критерия имеют поведенческое покрытие в реальном браузере; ни один
не нарушен. Коэффициент 0.621371 и округление до 1 знака подтверждены
и unit-тестами на реальном units.js, и e2e-конвертацией.
Найденные баги
P0 (блокирующие)
Нет.
P1 (критические)
Нет.
P2 (важные)
T-01 (= R-01 из 12-review.md) — переключение единиц сбрасывает выбор
варианта связки. В режиме связки onUnitChange() вызывает
renderLinkCards(linkRoutes), которая всегда подсвечивает «Вариант 1»;
выбранный пользователем вариант 2/3 теряется при каждом переключении
км/мили. Дефект унаследован из ревью (зафиксирован reviewer'ом как
R-01/P2). Тестером в этом прогоне не воспроизводился инструментально —
режим связки требует построенного маршрута и поднятого OSRM, недоступного
в окружении (см. 4 SKIP). Расстояния при этом пересчитываются корректно,
ФТ-3 ТЗ формально выполнено; дефект ограничен UX режима связки.
P2 — merge/деплой не блокирует. Действие: dev — поправить в этом же
PR (ввести activeLinkIdx) либо осознанно вынести в техдолг.
P3 (косметика / наблюдения)
- (= R-02 из
12-review.md) Масштабная линейка в режиме «mi» использует латиницу и точку ('0.5 mi') вместо запятой и русских подписейunits.js('0,5 ми'). Это пред-существующее поведение scale-bar (в режиме «km» и раньше было'30 km'), ET-005 лишь расширил тот же стиль на мили — регрессии нет. e2e подтвердил: scale-bar корректно меняет суффиксkm↔mi. Косметика, не блокирует. - (= R-03 из
12-review.md) Слойapp.js(оркестратор, unit-aware ветка scale-bar) в репозитории покрыт только статикой. В этом прогоне пробел закрыт e2e: оркестратор проверен на живой масштабной линейке (TP-02/TP-04). Перерисовка карточек маршрута/связки черезonUnitChange()инструментально не покрыта (нет OSRM); поведение подтвержденоunits.test.jsна реальном коде + статикойtest_app_js_*. Техдолг на DOM/MapLibre-харнесс дляapp.jsостаётся. - Окружение тестера. Пакеты
shapely,mapbox-vector-tile(requirements.txt) иpytest-asyncio,ruff(pyproject.toml [dev]) не были предустановлены в песочнице — безshapelyпадал сборtest_health.py(импортsrc.api.main). Тестер доустановил их по манифестам проекта. Это дефект провижининга окружения, не дефект ET-005. CI обязан выполнятьpip install -r requirements.txtи.[dev]передmake test.
Замечания тестера
- e2e-инструментарий. Playwright + Chromium установлены только в
песочницу тестера для исполнения e2e. В артефакты проекта
(
requirements.txt,pyproject.toml,package.json) ничего не добавлено — ограничение07-infra-requirements.md §6(«новые npm/Python пакеты — Нет», касается production/build-зависимостей) не нарушено. e2e-тесты04-test-plan.yaml(TP-01…TP-05) явно ожидаются07-infra-requirements.md §9; здесь они исполнены без изменения кода и тестов проекта. Скрипт прогона — временный, в репозиторий не коммитится. - Ручная сверка реализации:
index.html:62-69— сегментированный переключатель#unit-seg(кнопкиunit-btn-km/unit-btn-mi) в попапе#terrain-popupпосле чекбокса POI, отделён<hr>;index.html:415—units.jsподключён строго передapp.js;app.js:2877-2948— блок ET-005 (onUnitToggle,syncUnitToggleUI, оркестраторonUnitChange);app.js:2437-2438— восстановление выбора и единственная подпискаunitchangeвDOMContentLoaded. Соответствует ТЗ, ADR-0001 и выводам12-review.md. - Тесты не подгонялись под код; продакшн-код не правился; на prom ничего не запускалось.
Итог
Verdict: PASS → stage:ready-to-deploy.
Весь тест-план (TP-01…TP-05) исполнен в реальном браузере и пройден;
все 4 acceptance-критерия зелёные; pytest-регресс (31 passed) и lint
чистые; JS-ошибок на странице нет; нефункциональное требование
«пересчёт < 100 мс» выполнено с запасом (0,5 мс). Блокирующих (P0/P1)
багов нет. Унаследованный из ревью T-01/P2 (сброс варианта связки)
деплой не блокирует — решение по нему за dev. Готово к штатной
перевыкладке фронтенда согласно 07-infra-requirements.md §7.