fix(ui): настройка прокси исчезала с экрана
Владелец не нашёл настройку. Она была в разметке, но её не было на экране. arrangeSettingsPanels пересобирает настройки по жёсткому списку идентификаторов, переносит перечисленные строки в новые карточки, а исходную удаляет целиком — вместе со всем, чего в списке нет. Новое поле попало под удаление и просто перестало существовать. Добавлена группа «Сеть и доступ» с полем прокси. Название уточнено до «Прокси / VPN для провайдеров»: владелец называет это впном, и искать он будет по этому слову. Устройство, которое так теряет настройки, тоже исправлено: строки, не попавшие ни в одну группу, собираются в карточку «Прочие настройки», а не выбрасываются. Забыть настройку в списке всё ещё можно, потерять её с экрана — уже нет. Обращение к отсутствующему элементу защищено: опечатка в списке больше не роняет сборку экрана целиком. Четыре теста закрывают это: каждый перечисленный идентификатор существует в разметке, поле прокси сгруппировано, остаток забирается до удаления карточки. 704 passed, 2 skipped; ruff чисто; релизный гейт пройден. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
90aeceb9fd
commit
e431e39915
6 changed files with 82 additions and 3 deletions
|
|
@ -18,7 +18,7 @@ namespace HermesHubSetup
|
|||
// Подставляется сборщиком из фактического git-коммита. Раньше здесь
|
||||
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
|
||||
// какого кода собран установщик.
|
||||
public const string BuildCommit = "fa7bbef";
|
||||
public const string BuildCommit = "90aeceb";
|
||||
public const string MIN_HERMES_VERSION = "0.20.0";
|
||||
public const string MAX_TESTED_HERMES = "0.20.4";
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -457,7 +457,7 @@
|
|||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label class="setting-label" for="setting-provider-proxy-url">Прокси для провайдеров</label>
|
||||
<label class="setting-label" for="setting-provider-proxy-url">Прокси / VPN для провайдеров</label>
|
||||
<div class="setting-desc">Google отказывает по местоположению: «not currently available in your location». Выход через разрешённую страну снимает отказ. Пример: <code>socks5://127.0.0.1:1080</code>. Пусто — без прокси. Отдельному аккаунту можно задать свой адрес.</div>
|
||||
</div>
|
||||
<div class="setting-control"><input id="setting-provider-proxy-url" class="input-text" type="text" placeholder="Н/Д: загрузка" aria-label="Адрес прокси для обращений провайдеров"></div>
|
||||
|
|
|
|||
|
|
@ -229,14 +229,32 @@ function arrangeSettingsPanels() {
|
|||
['Общие настройки',['setting-default-role','setting-theme']],
|
||||
['Управление квотами',['setting-account-check-interval','setting-quota-interval','setting-quota-threshold-percent','setting-quota-threshold-action']],
|
||||
['Безопасность и API',['setting-server-host','setting-server-token-input','setting-email-masking-mode']],
|
||||
['Сеть и доступ',['setting-provider-proxy-url']],
|
||||
];
|
||||
for (const [title,ids] of groups) {
|
||||
const card = document.createElement('section'); card.className='settings-card';
|
||||
const heading=document.createElement('h2'); heading.className='settings-group-title'; heading.textContent=title; card.append(heading);
|
||||
for (const id of ids) card.append(document.getElementById(id).closest('.setting-row'));
|
||||
for (const id of ids) {
|
||||
const row = document.getElementById(id)?.closest('.setting-row');
|
||||
if (row) card.append(row);
|
||||
}
|
||||
if (title === 'Управление квотами') card.append(document.getElementById('btn-save-hub-settings').closest('.settings-actions'));
|
||||
view.insertBefore(card, first);
|
||||
}
|
||||
|
||||
// Строки, которых нет ни в одной группе, раньше удалялись вместе с исходной
|
||||
// карточкой: новая настройка просто пропадала с экрана, и найти её было
|
||||
// нельзя. Так исчезло поле прокси. Теперь остаток не выбрасывается, а
|
||||
// собирается отдельной карточкой — забыть настройку в списке всё ещё можно,
|
||||
// но потерять её уже нет.
|
||||
const leftovers = Array.from(first.querySelectorAll('.setting-row'));
|
||||
if (leftovers.length) {
|
||||
const card = document.createElement('section'); card.className='settings-card';
|
||||
const heading=document.createElement('h2'); heading.className='settings-group-title';
|
||||
heading.textContent='Прочие настройки'; card.append(heading);
|
||||
for (const row of leftovers) card.append(row);
|
||||
view.insertBefore(card, first);
|
||||
}
|
||||
first.remove();
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', arrangeSettingsPanels);
|
||||
|
|
|
|||
61
tests/test_settings_screen_keeps_every_row.py
Normal file
61
tests/test_settings_screen_keeps_every_row.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
"""Экран настроек не должен молча терять поля.
|
||||
|
||||
Владелец не нашёл настройку прокси. Она была в разметке, но исчезала при
|
||||
открытии экрана: arrangeSettingsPanels пересобирает настройки по жёсткому
|
||||
списку идентификаторов, а исходную карточку удаляет целиком — вместе со всем,
|
||||
чего в списке нет.
|
||||
|
||||
Поле существовало в файле, но его не было на экране, и никакая проверка этого
|
||||
не ловила.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
STATIC = Path(__file__).resolve().parent.parent / "src" / "antigravity_provider" / "router" / "web" / "static"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def markup() -> str:
|
||||
return (STATIC / "index.html").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def workspace() -> str:
|
||||
return (STATIC / "workspace.js").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _grouped_ids(workspace: str) -> list[str]:
|
||||
block = workspace.split("const groups = [")[1].split("];")[0]
|
||||
return re.findall(r"'(setting-[a-z0-9-]+)'", block)
|
||||
|
||||
|
||||
def test_every_grouped_id_exists_in_the_markup(markup, workspace):
|
||||
for element_id in _grouped_ids(workspace):
|
||||
assert f'id="{element_id}"' in markup, f"{element_id} перечислен в группах, но его нет в разметке"
|
||||
|
||||
|
||||
def test_proxy_setting_is_reachable(markup, workspace):
|
||||
assert 'id="setting-provider-proxy-url"' in markup
|
||||
assert "setting-provider-proxy-url" in _grouped_ids(workspace), (
|
||||
"негруппированная строка удаляется вместе с исходной карточкой"
|
||||
)
|
||||
|
||||
|
||||
def test_unlisted_rows_are_kept_not_deleted(workspace):
|
||||
"""Забыть настройку в списке можно, потерять её с экрана — нет."""
|
||||
body = workspace.split("function arrangeSettingsPanels")[1].split("function ")[0]
|
||||
|
||||
assert "leftovers" in body, "остаток строк должен собираться, а не выбрасываться"
|
||||
assert body.index("leftovers") < body.index("first.remove()"), (
|
||||
"остаток надо забрать до удаления исходной карточки"
|
||||
)
|
||||
|
||||
|
||||
def test_missing_element_does_not_break_the_screen(workspace):
|
||||
"""Опечатка в списке не должна ронять сборку экрана целиком."""
|
||||
body = workspace.split("function arrangeSettingsPanels")[1].split("function ")[0]
|
||||
assert "?.closest" in body, "обращение к возможному null должно быть защищено"
|
||||
Loading…
Reference in a new issue