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-коммита. Раньше здесь
|
// Подставляется сборщиком из фактического git-коммита. Раньше здесь
|
||||||
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
|
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
|
||||||
// какого кода собран установщик.
|
// какого кода собран установщик.
|
||||||
public const string BuildCommit = "fa7bbef";
|
public const string BuildCommit = "90aeceb";
|
||||||
public const string MIN_HERMES_VERSION = "0.20.0";
|
public const string MIN_HERMES_VERSION = "0.20.0";
|
||||||
public const string MAX_TESTED_HERMES = "0.20.4";
|
public const string MAX_TESTED_HERMES = "0.20.4";
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -457,7 +457,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<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 class="setting-desc">Google отказывает по местоположению: «not currently available in your location». Выход через разрешённую страну снимает отказ. Пример: <code>socks5://127.0.0.1:1080</code>. Пусто — без прокси. Отдельному аккаунту можно задать свой адрес.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-control"><input id="setting-provider-proxy-url" class="input-text" type="text" placeholder="Н/Д: загрузка" aria-label="Адрес прокси для обращений провайдеров"></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-default-role','setting-theme']],
|
||||||
['Управление квотами',['setting-account-check-interval','setting-quota-interval','setting-quota-threshold-percent','setting-quota-threshold-action']],
|
['Управление квотами',['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']],
|
['Безопасность и API',['setting-server-host','setting-server-token-input','setting-email-masking-mode']],
|
||||||
|
['Сеть и доступ',['setting-provider-proxy-url']],
|
||||||
];
|
];
|
||||||
for (const [title,ids] of groups) {
|
for (const [title,ids] of groups) {
|
||||||
const card = document.createElement('section'); card.className='settings-card';
|
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);
|
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'));
|
if (title === 'Управление квотами') card.append(document.getElementById('btn-save-hub-settings').closest('.settings-actions'));
|
||||||
view.insertBefore(card, first);
|
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();
|
first.remove();
|
||||||
}
|
}
|
||||||
document.addEventListener('DOMContentLoaded', arrangeSettingsPanels);
|
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