All checks were successful
Закрывает findings из docs/work-items/ET-011/12-review.md (REQUEST_CHANGES,
попытка 3/3):
P1-01 — добавлены поведенческие JS unit-тесты UI download-flow
- tests/web/track_download.test.js — 28 кейсов (node --test):
• _parseFilenameFromCD — RFC 5987 приоритет, plain fallback,
битый percent-encoding, null/empty (REQ-F-05.2, AC-2 UI)
• _handleDownloadError — 400/403/404/413/5xx тосты, defensive
при отсутствии showToast, поддержка flat (ADR-015 §G) и legacy
wrapped 403-форм (REQ-F-05.4, AC-7 UI)
• _renderTrackPopupHtml — наличие кнопки, aria-label «Скачать GPX»,
data-track-id, отсутствие при невалидном id, регрессия прочих
полей (REQ-F-01, AC-1)
- tests/web/test_track_download.py — pytest-обёртка (статические
проверки + запуск Node-раннера), исполняется в обычном pytest tests/
- 04b-ui-test-cases.md: AC-13 (mobile-bbox) явно маркирован как
MANUAL release-smoke (Playwright-раннер в проекте не настроен;
альтернатива согласована reviewer'ом в P1-01).
P2-01 — устранено расхождение «doc vs runtime» по контракту 403
- endpoint.py: HTTPException(detail={...}) → JSONResponse(content={...}),
чтобы FastAPI не оборачивал dict во второй слой «detail». Контракт
теперь совпадает с ADR-015 §G и ADR-014 §6:
{"detail":"source_forbidden","external_urls":[...]}
- test_gps_tracks_download.py IT-05: упрощено — body уже плоский,
без двухуровневого `body.get("detail", body)` workaround.
- gps_tracks.js::_handleDownloadError: flat-форма стала приоритетной,
wrapped-форма оставлена как defensive fallback (с комментарием).
Регрессия: 89/89 API-тестов + 24/24 предыдущих JS-тестов + 28 новых
JS-тестов download-flow проходят. ruff check — clean.
Refs: ET-011
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
3.7 KiB
Python
94 lines
3.7 KiB
Python
"""ET-011 — pytest-обёртка для JS unit-тестов download-UI.
|
|
|
|
Контекст: test-plan §E2E-01..E2E-04 предусматривал Playwright-спеку
|
|
`tests/web/test_track_download.spec.ts`, но в проекте нет настроенного
|
|
Playwright-раннера. Reviewer ET-011 (12-review.md, P1-01) разрешил закрыть
|
|
UI-сторону AC-1 / AC-2 / AC-7 поведенческими JS unit-тестами через
|
|
`node --test`. AC-13 (mobile-bbox) оставлен как manual smoke
|
|
(см. 04b-ui-test-cases.md TC-UI-02).
|
|
|
|
Этот файл — pytest-точка-входа, запускающая Node-раннер. Так JS-тесты
|
|
исполняются в обычном `pytest tests/` без отдельных шагов в Makefile/CI.
|
|
|
|
Запуск JS-тестов напрямую:
|
|
node --test tests/web/track_download.test.js
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from shutil import which
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
GPS_TRACKS_JS = REPO_ROOT / "src" / "web" / "gps_tracks.js"
|
|
JS_TEST = REPO_ROOT / "tests" / "web" / "track_download.test.js"
|
|
|
|
|
|
def _read(path: Path) -> str:
|
|
assert path.is_file(), f"не найден {path}"
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
# ─── Статические проверки: ET-011 артефакты на месте ─────────────────────────
|
|
|
|
|
|
def test_download_helpers_defined_in_gps_tracks_js():
|
|
"""ET-011: новые функции download-UI объявлены в gps_tracks.js."""
|
|
js = _read(GPS_TRACKS_JS)
|
|
for symbol in (
|
|
"function _parseFilenameFromCD(",
|
|
"function _handleDownloadError(",
|
|
"async function _downloadPublicTrack(",
|
|
):
|
|
assert symbol in js, (
|
|
f"ET-011: символ `{symbol}` не найден в src/web/gps_tracks.js"
|
|
)
|
|
|
|
|
|
def test_popup_renders_download_button_markup():
|
|
"""AC-1: _renderTrackPopupHtml содержит маркап кнопки «Скачать GPX»."""
|
|
js = _read(GPS_TRACKS_JS)
|
|
# Существенные куски, по которым держится UI-контракт
|
|
assert 'aria-label="Скачать GPX"' in js, (
|
|
"AC-1: aria-label='Скачать GPX' отсутствует в gps_tracks.js"
|
|
)
|
|
assert "track-popup-download-btn" in js, (
|
|
"AC-1: CSS-класс кнопки track-popup-download-btn отсутствует"
|
|
)
|
|
assert "data-track-id=" in js, (
|
|
"ADR-014 §3.b: data-track-id для делегированного клика отсутствует"
|
|
)
|
|
|
|
|
|
def test_js_test_file_exists():
|
|
"""JS-тест присутствует в репозитории — иначе субтесты ниже бессмыслены."""
|
|
assert JS_TEST.is_file(), f"не найден JS-тест {JS_TEST}"
|
|
|
|
|
|
# ─── Поведенческие JS unit-тесты через Node ──────────────────────────────────
|
|
|
|
|
|
node_required = pytest.mark.skipif(
|
|
which("node") is None,
|
|
reason="node не установлен — поведенческие JS unit-тесты пропущены",
|
|
)
|
|
|
|
|
|
@node_required
|
|
def test_js_track_download_unit_tests_pass():
|
|
"""ET-011 P1-01: AC-1 / AC-2 / AC-7 (UI) — JS-тесты download-flow."""
|
|
node = which("node")
|
|
result = subprocess.run(
|
|
[node, "--test", str(JS_TEST)],
|
|
capture_output=True,
|
|
text=True,
|
|
cwd=str(REPO_ROOT),
|
|
)
|
|
assert result.returncode == 0, (
|
|
f"JS unit-тесты track_download упали (код {result.returncode}):\n"
|
|
f"STDOUT:\n{result.stdout}\n\nSTDERR:\n{result.stderr}"
|
|
)
|