14 KiB
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 |
|
|
Техническое задание — ET-008: Smoke test analyst integration
1. Общая архитектура изменений
Изменение строго фронтовое и максимально минимальное. Затрагиваются ровно два файла:
src/web/index.html- В
<head>добавить инлайн-скрипт, который выставляетdocument.body.classList.add('smoke-mode'), если в URL присутствуетsmoke=et-008(вsearchили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. Откат
В случае проблем:
- В
src/web/index.htmlудалить инлайн-<script>из<head>(REQ-F-02). - В
src/web/index.htmlудалить<div id="pipeline-smoke">…</div>из<body>(REQ-F-01). - В
src/web/app.cssудалить блок с правилами для#pipeline-smoke(REQ-F-03 и REQ-F-04).
Полный откат — один коммит, без побочных эффектов и без изменения JS / API / БД.