auto-sync: 2026-05-13 01:00:01
45
tasks/enduro-trails/TEST_CASES_UI_TERRAIN_EXT.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# UI Test Cases: Terrain — Расширенные
|
||||
|
||||
### TC-T-07 — Гипсометрия на зуме 10
|
||||
**Тип:** ui
|
||||
**Viewport:** desktop
|
||||
**URL:** https://openclaw.mva154.duckdns.org/enduro/#10/55.75/37.6
|
||||
|
||||
**Шаги:**
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/#10/55.75/37.6
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#terrain-hypso-cb"
|
||||
6. wait: 5000
|
||||
7. screenshot: "hypso-zoom10"
|
||||
8. check-visual: "Гипсометрия включена на зуме 10. Должен быть виден цветной полупрозрачный слой (зелёные тона для равнины ЦФО) поверх базовой карты."
|
||||
|
||||
**Визуальные критерии:**
|
||||
- Цветной overlay виден на карте
|
||||
- Зелёные/жёлтые тона рельефа
|
||||
- Карта под overlay читаема
|
||||
|
||||
---
|
||||
|
||||
### TC-T-08 — Hillshade на зуме 11
|
||||
**Тип:** ui
|
||||
**Viewport:** desktop
|
||||
**URL:** https://openclaw.mva154.duckdns.org/enduro/#11/55.75/37.6
|
||||
|
||||
**Шаги:**
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/#11/55.75/37.6
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#terrain-hypso-cb"
|
||||
6. wait: 1000
|
||||
7. click: "#terrain-hillshade-cb"
|
||||
8. wait: 5000
|
||||
9. screenshot: "hillshade-zoom11"
|
||||
10. check-visual: "Оба слоя включены на зуме 11: гипсометрия (цвет) + отмывка (тени). Должны быть видны тени на склонах рельефа."
|
||||
|
||||
**Визуальные критерии:**
|
||||
- Цветной overlay + тени видны
|
||||
- Рельеф выглядит объёмным
|
||||
- Нет чёрных/белых артефактов
|
||||
165
tasks/enduro-trails/prototype/static/app.js
vendored
@@ -2623,3 +2623,168 @@ function initMiniRouteInteraction() {
|
||||
selectRoute(activeRouteIdx);
|
||||
});
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// TERRAIN LAYERS (Phase 5.4)
|
||||
// ═══════════════════════════════════════════
|
||||
|
||||
const TERRAIN_BASE_URL = window.location.pathname.replace(/\/[^/]*$/, '') + '/terrain';
|
||||
|
||||
function toggleTerrainPopup() {
|
||||
const popup = document.getElementById('terrain-popup');
|
||||
const btn = document.getElementById('terrain-toggle');
|
||||
if (!popup || !btn) return;
|
||||
|
||||
const isVisible = popup.style.display !== 'none';
|
||||
popup.style.display = isVisible ? 'none' : 'block';
|
||||
btn.classList.toggle('active', !isVisible);
|
||||
|
||||
// Close on outside click
|
||||
if (!isVisible) {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', closeTerrainOnOutside);
|
||||
}, 10);
|
||||
} else {
|
||||
document.removeEventListener('click', closeTerrainOnOutside);
|
||||
}
|
||||
}
|
||||
|
||||
function closeTerrainOnOutside(e) {
|
||||
const popup = document.getElementById('terrain-popup');
|
||||
const btn = document.getElementById('terrain-toggle');
|
||||
if (!popup.contains(e.target) && e.target !== btn && !btn.contains(e.target)) {
|
||||
popup.style.display = 'none';
|
||||
btn.classList.remove('active');
|
||||
document.removeEventListener('click', closeTerrainOnOutside);
|
||||
}
|
||||
}
|
||||
|
||||
function onTerrainCheckbox() {
|
||||
const map = window._map;
|
||||
if (!map) return;
|
||||
|
||||
const hypsoChecked = document.getElementById('terrain-hypso-cb').checked;
|
||||
const hillshadeChecked = document.getElementById('terrain-hillshade-cb').checked;
|
||||
|
||||
// Save state
|
||||
localStorage.setItem('terrain-hypso', hypsoChecked ? '1' : '0');
|
||||
localStorage.setItem('terrain-hillshade', hillshadeChecked ? '1' : '0');
|
||||
|
||||
// Update button active state
|
||||
const btn = document.getElementById('terrain-toggle');
|
||||
btn.classList.toggle('active', hypsoChecked || hillshadeChecked);
|
||||
|
||||
// Apply layers
|
||||
applyTerrainLayer('terrain-hypso', TERRAIN_BASE_URL + '/hypso/{z}/{x}/{y}.png', hypsoChecked, 0.55, 5, 15);
|
||||
applyTerrainLayer('terrain-hillshade', TERRAIN_BASE_URL + '/hillshade/{z}/{x}/{y}.png', hillshadeChecked, 0.40, 10, 15);
|
||||
}
|
||||
|
||||
function applyTerrainLayer(id, tileUrl, enabled, opacity, minzoom, maxzoom) {
|
||||
const map = window._map;
|
||||
if (!map) return;
|
||||
|
||||
const sourceId = id + '-source';
|
||||
|
||||
if (enabled) {
|
||||
// Add source if not exists
|
||||
if (!map.getSource(sourceId)) {
|
||||
map.addSource(sourceId, {
|
||||
type: 'raster',
|
||||
tiles: [tileUrl],
|
||||
tileSize: 256,
|
||||
scheme: 'tms',
|
||||
bounds: [35, 45, 55, 62],
|
||||
minzoom: minzoom,
|
||||
maxzoom: maxzoom
|
||||
});
|
||||
}
|
||||
// Add layer if not exists
|
||||
if (!map.getLayer(id)) {
|
||||
// Insert before first road/trail layer for correct z-order
|
||||
const firstTrailLayer = map.getStyle().layers.find(l =>
|
||||
l.id.startsWith('trails-') || l.id.startsWith('poi-')
|
||||
);
|
||||
map.addLayer({
|
||||
id: id,
|
||||
type: 'raster',
|
||||
source: sourceId,
|
||||
paint: {
|
||||
'raster-opacity': opacity
|
||||
},
|
||||
minzoom: minzoom,
|
||||
maxzoom: maxzoom
|
||||
}, firstTrailLayer ? firstTrailLayer.id : undefined);
|
||||
}
|
||||
} else {
|
||||
// Remove layer and source
|
||||
if (map.getLayer(id)) map.removeLayer(id);
|
||||
if (map.getSource(sourceId)) map.removeSource(sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
function updateHillshadeAvailability() {
|
||||
const map = window._map;
|
||||
if (!map) return;
|
||||
|
||||
const zoom = map.getZoom();
|
||||
const cb = document.getElementById('terrain-hillshade-cb');
|
||||
const hint = document.getElementById('terrain-hillshade-hint');
|
||||
const label = cb ? cb.closest('.terrain-checkbox') : null;
|
||||
|
||||
if (zoom < 10) {
|
||||
if (cb) cb.disabled = true;
|
||||
if (label) label.classList.add('disabled');
|
||||
if (hint) hint.style.display = 'inline';
|
||||
} else {
|
||||
if (cb) cb.disabled = false;
|
||||
if (label) label.classList.remove('disabled');
|
||||
if (hint) hint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function restoreTerrainState() {
|
||||
const hypso = localStorage.getItem('terrain-hypso') === '1';
|
||||
const hillshade = localStorage.getItem('terrain-hillshade') === '1';
|
||||
|
||||
const hypsoCb = document.getElementById('terrain-hypso-cb');
|
||||
const hillshadeCb = document.getElementById('terrain-hillshade-cb');
|
||||
|
||||
if (hypsoCb) hypsoCb.checked = hypso;
|
||||
if (hillshadeCb) hillshadeCb.checked = hillshade;
|
||||
|
||||
if (hypso || hillshade) {
|
||||
onTerrainCheckbox();
|
||||
}
|
||||
|
||||
// Update button active state
|
||||
const btn = document.getElementById('terrain-toggle');
|
||||
if (btn) btn.classList.toggle('active', hypso || hillshade);
|
||||
}
|
||||
|
||||
// Hook into map load and zoom changes
|
||||
(function initTerrain() {
|
||||
const map = window._map;
|
||||
if (map) {
|
||||
map.on('zoomend', updateHillshadeAvailability);
|
||||
map.on('style.load', () => {
|
||||
// Re-apply terrain after style change (theme switch)
|
||||
setTimeout(restoreTerrainState, 100);
|
||||
});
|
||||
// Initial state
|
||||
updateHillshadeAvailability();
|
||||
restoreTerrainState();
|
||||
} else {
|
||||
// Map not ready yet, wait
|
||||
const interval = setInterval(() => {
|
||||
if (window._map) {
|
||||
clearInterval(interval);
|
||||
window._map.on('zoomend', updateHillshadeAvailability);
|
||||
window._map.on('style.load', () => {
|
||||
setTimeout(restoreTerrainState, 100);
|
||||
});
|
||||
updateHillshadeAvailability();
|
||||
restoreTerrainState();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,145 +1,37 @@
|
||||
{
|
||||
"timestamp": "2026-05-12T21:44:53.908Z",
|
||||
"testFile": "TEST_CASES_UI_TERRAIN.md",
|
||||
"timestamp": "2026-05-12T21:55:27.927Z",
|
||||
"testFile": "TEST_CASES_UI_TERRAIN_EXT.md",
|
||||
"results": [
|
||||
{
|
||||
"id": "TC-T-01",
|
||||
"name": "Кнопка рельеф видна",
|
||||
"id": "TC-T-07",
|
||||
"name": "Гипсометрия на зуме 10",
|
||||
"viewport": "desktop",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-01-desktop-terrain-btn-visible.png",
|
||||
"TC-T-01-desktop-check-1778622228148.png"
|
||||
"TC-T-07-desktop-hypso-zoom10.png",
|
||||
"TC-T-07-desktop-check-1778622909625.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Кнопка рельеф (иконка горы) видна в правой панели кнопок. Не обрезана, достаточного размера для тапа.",
|
||||
"screenshot": "TC-T-01-desktop-check-1778622228148.png"
|
||||
"description": "Гипсометрия включена на зуме 10. Должен быть виден цветной полупрозрачный слой (зелёные тона для равнины ЦФО) поверх базовой карты.",
|
||||
"screenshot": "TC-T-07-desktop-check-1778622909625.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"id": "TC-T-01",
|
||||
"name": "Кнопка рельеф видна",
|
||||
"viewport": "mobile",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-01-mobile-terrain-btn-visible.png",
|
||||
"TC-T-01-mobile-check-1778622233547.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Кнопка рельеф (иконка горы) видна в правой панели кнопок. Не обрезана, достаточного размера для тапа.",
|
||||
"screenshot": "TC-T-01-mobile-check-1778622233547.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"id": "TC-T-02",
|
||||
"name": "Попап рельеф открывается",
|
||||
"id": "TC-T-08",
|
||||
"name": "Hillshade на зуме 11",
|
||||
"viewport": "desktop",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-02-desktop-terrain-popup-open.png",
|
||||
"TC-T-02-desktop-check-1778622239862.png"
|
||||
"TC-T-08-desktop-hillshade-zoom11.png",
|
||||
"TC-T-08-desktop-check-1778622927607.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Попап рельеф открылся. Видны два чекбокса: Гипсометрия и Отмывка. Попап не обрезан, текст читаем.",
|
||||
"screenshot": "TC-T-02-desktop-check-1778622239862.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"id": "TC-T-02",
|
||||
"name": "Попап рельеф открывается",
|
||||
"viewport": "mobile",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-02-mobile-terrain-popup-open.png",
|
||||
"TC-T-02-mobile-check-1778622245753.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Попап рельеф открылся. Видны два чекбокса: Гипсометрия и Отмывка. Попап не обрезан, текст читаем.",
|
||||
"screenshot": "TC-T-02-mobile-check-1778622245753.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"id": "TC-T-03",
|
||||
"name": "Включение гипсометрии",
|
||||
"viewport": "desktop",
|
||||
"status": "completed_with_errors",
|
||||
"screenshots": [
|
||||
"TC-T-03-desktop-hypso-enabled.png",
|
||||
"TC-T-03-desktop-check-1778622259991.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Гипсометрия включена: на карте виден цветной полупрозрачный слой рельефа (зелёные/жёлтые/коричневые тона поверх базовой карты).",
|
||||
"screenshot": "TC-T-03-desktop-check-1778622259991.png"
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
"Click failed on \"#terrain-hypso-cb\": page.click: Timeout 5000ms exceeded.\nCall log:\n - waiting for locator('#terrain-hypso-cb')\n - lo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TC-T-04",
|
||||
"name": "Включение отмывки (hillshade)",
|
||||
"viewport": "desktop",
|
||||
"status": "completed_with_errors",
|
||||
"screenshots": [
|
||||
"TC-T-04-desktop-hillshade-enabled.png",
|
||||
"TC-T-04-desktop-check-1778622280567.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Отмывка включена: видны тени рельефа (затемнение на склонах). Если зум < 10 — чекбокс hillshade должен быть disabled с подсказкой 'Зум 10+'.",
|
||||
"screenshot": "TC-T-04-desktop-check-1778622280567.png"
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
"Click failed on \"#terrain-hypso-cb\": page.click: Timeout 5000ms exceeded.\nCall log:\n - waiting for locator('#terrain-hypso-cb')\n - lo",
|
||||
"Click failed on \"#terrain-hillshade-cb\": page.click: Timeout 5000ms exceeded.\nCall log:\n - waiting for locator('#terrain-hillshade-cb')\n "
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TC-T-05",
|
||||
"name": "Попап закрывается по повторному клику",
|
||||
"viewport": "desktop",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-05-desktop-popup-open.png",
|
||||
"TC-T-05-desktop-popup-closed.png",
|
||||
"TC-T-05-desktop-check-1778622287510.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Попап закрылся после повторного клика на кнопку рельеф. Попап не виден на экране.",
|
||||
"screenshot": "TC-T-05-desktop-check-1778622287510.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
{
|
||||
"id": "TC-T-06",
|
||||
"name": "Мобильный попап не обрезан",
|
||||
"viewport": "mobile",
|
||||
"status": "completed",
|
||||
"screenshots": [
|
||||
"TC-T-06-mobile-terrain-popup-mobile.png",
|
||||
"TC-T-06-mobile-check-1778622293523.png"
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"description": "Попап terrain полностью виден на мобильном экране. Не обрезан снизу/справа. Чекбоксы достаточного размера для тапа (>44px).",
|
||||
"screenshot": "TC-T-06-mobile-check-1778622293523.png"
|
||||
"description": "Оба слоя включены на зуме 11: гипсометрия (цвет) + отмывка (тени). Должны быть видны тени на склонах рельефа.",
|
||||
"screenshot": "TC-T-08-desktop-check-1778622927607.png"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
|
||||
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 829 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 797 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 797 KiB |
|
After Width: | Height: | Size: 918 KiB |
|
After Width: | Height: | Size: 918 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 918 KiB |
|
After Width: | Height: | Size: 1.0 MiB |