Files
enduro-trails/docs/work-items/ET-008/02-trz.md
claude-bot e77e4a2ab7
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 6s
CI / build (push) Successful in 1s
docs(ET-008): analyst artifacts - BRD, TRZ, AC, TestPlan
2026-05-31 19:51:53 +03:00

14 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, updated_at, authors, depends_on
type work_item_id title version status created_at updated_at authors depends_on
trz ET-008 ТЗ: Smoke test analyst integration 1 draft 2026-05-31 2026-05-31
agent:analyst
ET-008/01-brd.md

Техническое задание — ET-008: Smoke test analyst integration

1. Общая архитектура изменений

Изменение строго фронтовое и максимально минимальное. Затрагиваются ровно два файла:

  • src/web/index.html
    • В <head> добавить инлайн-скрипт, который выставляет document.body.classList.add('smoke-mode'), если в URL присутствует smoke=et-008search или hash).
    • В <body> (рядом с <div id="map"> или сразу перед <!-- Scripts -->) добавить элемент <div id="pipeline-smoke" aria-hidden="true" role="presentation">ET-008 ✓</div>.
  • src/web/app.css
    • В конец файла добавить блок с правилами для #pipeline-smoke.

Запрещено трогать: src/web/app.js, src/web/units.js, src/web/gpx.js, src/web/style.json, src/web/style-dark.json, src/api/**, tests/api/**, миграции, Docker-конфигурацию.

2. Функциональные требования

REQ-F-01. DOM-маркер #pipeline-smoke

В src/web/index.html добавить элемент:

<div id="pipeline-smoke" aria-hidden="true" role="presentation">ET-008 ✓</div>

Размещение: после <div id="map"></div> (т.е. внутри <body>, сразу после карты), но до <!-- Scripts -->. Конкретное место — после блока <div id="no-data-warning"> или эквивалентного места, где сейчас лежат «свободно плавающие» элементы (#ruler-toast, #app-toast).

Текст узла: ровно ET-008 ✓ (без кавычек, с одним пробелом перед галочкой, U+2713 CHECK MARK).

REQ-F-02. Инлайн-скрипт активации в <head>

В <head> src/web/index.html, после <link rel="stylesheet" href="app.css"> и до закрывающего </head>, добавить:

<script>
  // ET-008: smoke marker activation. Pure URL-based, no storage, no I/O.
  (function () {
    try {
      var s = (location.search || '') + ' ' + (location.hash || '');
      if (s.indexOf('smoke=et-008') !== -1) {
        document.documentElement.classList.add('smoke-mode');
      }
    } catch (e) { /* swallow — smoke is non-critical */ }
  })();
</script>

Замечания:

  • Скрипт ставит класс на <html> (documentElement), а не на <body>, потому что на момент выполнения (в <head>) <body> ещё не существует. Эквивалентный селектор для CSS — html.smoke-mode #pipeline-smoke. Это требование к CSS-блоку (см. REQ-F-04).
  • try/catch обязателен — если по какой-то причине location не доступен (тест с jsdom без URL), скрипт не должен ронять страницу.
  • Скрипт синхронный, не модуль, без defer/async — он должен сработать до того, как CSS попытается применить display:none к видимому в smoke маркеру (избежать FOUC).

REQ-F-03. CSS базовое состояние (скрыт)

В конец src/web/app.css добавить:

/* ── ET-008: pipeline smoke marker ───────────────────────────
   По умолчанию полностью скрыт. Включается классом .smoke-mode
   на <html>, который ставит инлайн-скрипт в <head>, если
   в URL присутствует ?smoke=et-008 или #smoke=et-008.
   ──────────────────────────────────────────────────────────── */
#pipeline-smoke {
  display: none;
}

REQ-F-04. CSS активное состояние (видим)

Сразу после REQ-F-03 в src/web/app.css:

html.smoke-mode #pipeline-smoke {
  display: inline-block;
  position: fixed;
  left: 8px;
  bottom: 8px;
  z-index: 1;
  padding: 2px 6px;
  font: 11px/1.4 system-ui, -apple-system, sans-serif;
  color: #e7e7e7;
  background: rgba(20, 20, 28, 0.7);
  border-radius: 4px;
  pointer-events: none;
  user-select: none;
  letter-spacing: 0.02em;
}

Значения цвета и фона не зависят от темы — это намеренно, чтобы маркер был стабильно читаем и в светлой, и в тёмной теме без отдельных правил body.theme-dark.

REQ-F-05. Условие активации

Маркер показывается, если любое из условий выполнено:

  • location.search (часть URL после ?) содержит подстроку smoke=et-008;
  • location.hash (часть URL после #) содержит подстроку smoke=et-008.

Сравнение точное, чувствительно к регистру. Любое отклонение (smoke=ET-008, smoke=et_008, smoke=et-007, smoketest=et-008) не активирует маркер.

REQ-F-06. Не-конфликтность с темами

После toggleTheme() маркер:

  • если был видим (smoke-mode) — остаётся видимым с теми же цветами;
  • если был скрыт (no smoke-mode) — остаётся скрытым.

То есть body-класс theme-dark не влияет на отображение #pipeline-smoke.

REQ-F-07. Не-конфликтность с layout

z-index: 1 ниже, чем у #map-controls-r (фактический z-index у существующих контролов — выше). Маркер не должен:

  • перекрывать #map-controls-r (top-right);
  • перекрывать .maplibregl-ctrl-attrib (bottom-right);
  • мешать #toolbar на мобильном (нижняя панель);
  • перекрывать любой .bottom-sheet, когда тот открыт.

В частности на мобильном бутылочное горлышко — #toolbar снизу. Маркер в left: 8px; bottom: 8px помещается слева, тулбар обычно центрирован/растянут, так что небольшой риск перекрытия допустим, но визуально не критичен (маркер маленький и полупрозрачный).

REQ-F-08. ARIA

  • aria-hidden="true" — обязательно.
  • role="presentation" — обязательно.
  • Никаких подсказок (title), aria-label, фокусируемых элементов внутри.

REQ-F-09. Отсутствие сетевых эффектов

Реализация не должна:

  • добавлять <link> / <script src="..."> / <img> / <iframe>;
  • обращаться к fetch / XMLHttpRequest / navigator.sendBeacon;
  • использовать localStorage / sessionStorage / cookie / IndexedDB / caches.

REQ-F-10. Совместимость с Playwright route(...)

Playwright-тесты ET-008 имеют право ассертить отсутствие новых сетевых запросов после добавления маркера (например, через page.on('request', ...)). Любая сетевая активность из ET-008 — дефект.

3. Нефункциональные требования

REQ-NF-01. Производительность

  • Дополнительный DOM-узел — 1 (<div>).
  • Дополнительный CSS-блок — ≤ 20 строк.
  • Дополнительный JS — инлайн ≤ 10 строк, выполняется один раз до парсинга <body>.
  • Никаких подписок на события, MutationObserver, requestAnimationFrame.

REQ-NF-02. Совместимость браузеров

Те же, что у основного приложения:

  • Chrome ≥ 110, Firefox ≥ 110, Safari ≥ 16, мобильные Chrome/Safari iOS ≥ 16.
  • String.prototype.indexOf — ES1, поддержано всегда.

REQ-NF-03. Логирование

В нормальной работе скрипт ничего не пишет в консоль. В исключительной — молча проглатывает (try/catch без console.warn). Это смягчение для тестов, которые ассертят «no console.error».

REQ-NF-04. Локализация

Текст ET-008 ✓ не локализуется. Это технический идентификатор, одинаковый во всех языках.

REQ-NF-05. Безопасность

  • Никаких пользовательских данных в маркере.
  • Никакого innerHTML / eval / Function(...).
  • Никаких внешних URL.

4. Структура данных

REQ-D-01. URL-параметры

Параметр Тип Значение Эффект
?smoke=et-008 query строго et-008 html.classList.add('smoke-mode')
#smoke=et-008 fragment строго et-008 то же самое
(нет параметра) маркер display:none
?smoke=... иное query любое отличное от et-008 маркер display:none
#smoke=... иное fragment любое отличное от et-008 маркер display:none

REQ-D-02. Классы CSS

Класс Селектор где Назначение
smoke-mode на <html> флаг видимости маркера

5. UI / UX контракт

Элемент Местоположение Поведение
#pipeline-smoke <body>, после #map, до <!-- Scripts --> По умолчанию display:none. В smoke-режиме — fixed left:8 bottom:8
Содержимое Текстовый узел ET-008 ✓ Не меняется в рантайме
Фон rgba(20,20,28,0.7) Полупрозрачный, чтобы видеть карту под ним
Текст #e7e7e7, 11px Контраст ≥ 4.5:1 над тёмным фоном маркера
Курсор / фокус pointer-events: none, не фокусируется Не мешает кликам по карте под ним

6. Тестируемость

  • Unit: проверка наличия CSS-блока и DOM-узла через парсинг файлов (без запуска браузера).
  • Integration: запуск страницы в jsdom с поддельным location, проверка добавления класса.
  • E2E (Playwright): загрузка test-окружения с ?smoke=et-008 и без, проверка видимости/скрытости, отсутствия сетевых запросов, отсутствия console.error.
  • UI (Playwright скриншоты): см. 04b-ui-test-cases.md.

7. Совместимость и миграции

  • БД: миграций нет.
  • API: новых эндпоинтов нет.
  • Конфиги: новых ENV не добавляем.
  • Существующие work item: не затрагиваются (только статические файлы, никаких JS-символов).
  • CI: новые тесты добавляются как файл tests/web/e2e/pipeline-smoke.spec.ts, существующая команда make test подхватывает их без изменения конфигов.

8. Откат

В случае проблем:

  1. В src/web/index.html удалить инлайн-<script> из <head> (REQ-F-02).
  2. В src/web/index.html удалить <div id="pipeline-smoke">…</div> из <body> (REQ-F-01).
  3. В src/web/app.css удалить блок с правилами для #pipeline-smoke (REQ-F-03 и REQ-F-04).

Полный откат — один коммит, без побочных эффектов и без изменения JS / API / БД.