fix(ui): sections 17 and 18 were checked against the wrong text
check_design_parity read its checklist from build_audit_page.ANALYSIS. Eight
sections of the audit page are hand-written, and for those the generator's
text is NOT what the page says — so Settings and the Task editor were reported
as matching a design they had never been compared against. The Task editor was
in fact built backwards.
* The checker now reads the "Thay đổi" bullets straight out of
docs/ui-audit.html, and reports how many bullets on the page still have no
probe (57 on the page, 32 probed) instead of implying full coverage.
* Task editor: reverted from three step tabs to a left list + right panel,
five rows matching the five real group boxes — which is what the page asks
for, in as many words ("thay vì chia tab"), for consistency with Settings.
* Settings now switches panels rather than scrolling, so both dialogs are
navigated identically and neither is a long scroll any more.
* Settings field presentation, as the page's second bullet asks: the six
checkboxes became switches, and Language/Theme became segmented controls.
ToggleSwitch subclasses QCheckBox and SegmentedControl exposes the slice
of the QComboBox API this dialog uses, so no save/load path changed —
verified by round-tripping language/theme/tray through _save().
All 22 task-editor fields and 14 settings fields verified present after the
move; 7 suites green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+59
-71
@@ -1,8 +1,9 @@
|
||||
"""Check the section index added to Settings and the Task editor, offscreen.
|
||||
"""Check the left-list + right-panel navigation in Settings and Task editor.
|
||||
|
||||
The index is navigation only, so the test is again a subtraction test: every
|
||||
input control must still be there, and every index row must actually scroll to
|
||||
its section. Both dialogs are built with .show(), never .exec() — exec() blocks.
|
||||
Both dialogs are navigated the same way, as the audit page asks: a list of the
|
||||
real group boxes on the left, one panel shown at a time on the right. So the
|
||||
test is that picking a row swaps the panel, that the rows match the groups, and
|
||||
— since this is a rearrangement — that no input control went missing.
|
||||
|
||||
Run: python tools/check_dialogs.py
|
||||
"""
|
||||
@@ -19,69 +20,55 @@ os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from capture_screens import _isolate_home, _load_fonts # noqa: E402
|
||||
|
||||
|
||||
def controls(dlg):
|
||||
from PySide6.QtWidgets import (QCheckBox, QComboBox, QLineEdit, QListWidget,
|
||||
QPlainTextEdit, QPushButton, QSpinBox)
|
||||
n = 0
|
||||
for cls in (QComboBox, QLineEdit, QCheckBox, QSpinBox, QPlainTextEdit,
|
||||
QPushButton, QListWidget):
|
||||
n += len(dlg.findChildren(cls))
|
||||
return n
|
||||
# Every field each dialog must still offer after the move.
|
||||
SETTINGS_FIELDS = [
|
||||
"language_combo", "theme_combo", "tray_chk", "notify_chk",
|
||||
"provider_combo", "prov_base", "prov_key", "prov_model",
|
||||
"sandbox_pw_edit", "sandbox_unlock_btn", "sandbox_confirm",
|
||||
"sandbox_block_network", "sec_enabled", "ai_check",
|
||||
]
|
||||
TASK_FIELDS = [
|
||||
"title_edit", "desc_edit", "gen_desc_btn", "priority_combo", "status_combo",
|
||||
"workspace_combo", "provider_combo", "model_combo", "skill_combo",
|
||||
"sched_enabled", "run_at_edit", "files_list", "files_add_btn", "links_list",
|
||||
"links_add_btn", "next_combo", "run_next_combo", "pass_output_chk",
|
||||
"depends_list", "retry_spin", "timeout_spin", "approval_chk",
|
||||
]
|
||||
|
||||
|
||||
def check_tabs(name, dlg, app, expect):
|
||||
"""The Task editor uses step TABS, not an index — same goal, different
|
||||
control, so it gets its own check."""
|
||||
def check(name, dlg, app, expect_rows, fields):
|
||||
fails = []
|
||||
print(f"--- {name} ---")
|
||||
n_ctl = controls(dlg)
|
||||
tabs = dlg.step_tabs
|
||||
names = [tabs.tabText(i) for i in range(tabs.count())]
|
||||
print(f"buoc : {names}")
|
||||
print(f"tong control trong hop thoai: {n_ctl}")
|
||||
if tabs.count() != expect:
|
||||
fails.append(f"{name}: cho {expect} buoc, thay {tabs.count()}")
|
||||
# Every page must actually hold something — an empty step means a group box
|
||||
# was dropped on the way in.
|
||||
for i in range(tabs.count()):
|
||||
page = tabs.widget(i).widget()
|
||||
kids = [w for w in page.findChildren(type(dlg)) ] or page.children()
|
||||
n = len([c for c in page.findChildren(__import__(
|
||||
"PySide6.QtWidgets", fromlist=["QWidget"]).QWidget) if c.parent() is page])
|
||||
print(f" buoc {i + 1} co {n} khoi")
|
||||
if n == 0:
|
||||
fails.append(f"{name}: buoc {i + 1} rong")
|
||||
return n_ctl, fails
|
||||
|
||||
|
||||
def check(name, dlg, app, expect_rows):
|
||||
fails = []
|
||||
print(f"--- {name} ---")
|
||||
n_ctl = controls(dlg)
|
||||
idx = dlg.section_list
|
||||
idx, stack = dlg.section_list, dlg.section_stack
|
||||
rows = [idx.item(i).text() for i in range(idx.count())]
|
||||
print(f"muc luc : {rows}")
|
||||
print(f"tong control trong hop thoai: {n_ctl}")
|
||||
print(f"muc : {rows}")
|
||||
if len(rows) != expect_rows:
|
||||
fails.append(f"{name}: cho {expect_rows} muc, thay {len(rows)}")
|
||||
if any(not r or r.endswith(".g_basic") or r.startswith("settings.") for r in rows):
|
||||
fails.append(f"{name}: co muc chua dich")
|
||||
if idx.count() != stack.count():
|
||||
fails.append(f"{name}: {idx.count()} muc nhung {stack.count()} panel")
|
||||
|
||||
# Each row must scroll somewhere different (and the last one furthest down).
|
||||
from PySide6.QtWidgets import QScrollArea
|
||||
scroll = dlg.findChildren(QScrollArea)[0]
|
||||
positions = []
|
||||
# Picking a row must swap the panel — and each panel must hold something.
|
||||
swapped, empty = 0, []
|
||||
for i in range(idx.count()):
|
||||
idx.itemClicked.emit(idx.item(i))
|
||||
app.processEvents()
|
||||
positions.append(scroll.verticalScrollBar().value())
|
||||
print(f"vi tri cuon theo tung muc : {positions}")
|
||||
if positions != sorted(positions):
|
||||
fails.append(f"{name}: muc luc nhay khong theo thu tu tren xuong")
|
||||
if len(set(positions)) < 2:
|
||||
fails.append(f"{name}: bam muc nao cung dung mot cho — muc luc khong chay")
|
||||
return n_ctl, fails
|
||||
idx.setCurrentRow(i)
|
||||
for _ in range(3):
|
||||
app.processEvents()
|
||||
if stack.currentIndex() == i:
|
||||
swapped += 1
|
||||
page = stack.widget(i).widget()
|
||||
if not page.findChildren(type(page)):
|
||||
empty.append(rows[i])
|
||||
print(f"chon muc -> doi panel : {swapped}/{idx.count()}")
|
||||
if swapped != idx.count():
|
||||
fails.append(f"{name}: chon muc khong doi panel")
|
||||
if empty:
|
||||
fails.append(f"{name}: panel rong {empty}")
|
||||
|
||||
missing = [f for f in fields if getattr(dlg, f, None) is None]
|
||||
print(f"field con nguyen : {len(fields) - len(missing)}/{len(fields)}")
|
||||
if missing:
|
||||
fails.append(f"{name}: mat field {missing}")
|
||||
return fails
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -101,31 +88,32 @@ def main() -> int:
|
||||
|
||||
set_language("vi")
|
||||
ctx = AppContext(AppConfig.load())
|
||||
|
||||
fails = []
|
||||
|
||||
s = SettingsDialog(ctx)
|
||||
s.resize(900, 600)
|
||||
s.resize(900, 640)
|
||||
s.show()
|
||||
app.processEvents()
|
||||
n_s, f = check("Cai dat", s, app, 5)
|
||||
fails += f
|
||||
fails += check("Cai dat", s, app, 5, SETTINGS_FIELDS)
|
||||
|
||||
t = TaskEditorDialog(ctx=ctx) # task=None → a new task, all fields present
|
||||
t.resize(900, 600)
|
||||
t = TaskEditorDialog(ctx=ctx)
|
||||
t.resize(900, 640)
|
||||
t.show()
|
||||
app.processEvents()
|
||||
n_t, f = check_tabs("Task editor", t, app, 3)
|
||||
fails += f
|
||||
fails += check("Task editor", t, app, 5, TASK_FIELDS)
|
||||
|
||||
# Translations for the two names that had to be invented for the index.
|
||||
# Both dialogs must be navigated the SAME way — that is the stated point.
|
||||
same = (type(s.section_list) is type(t.section_list)
|
||||
and type(s.section_stack) is type(t.section_stack))
|
||||
print()
|
||||
print(f"hai hop thoai cung kieu dieu huong: {same}")
|
||||
if not same:
|
||||
fails.append("hai hop thoai dieu huong khac kieu")
|
||||
|
||||
for lang in ("vi", "en", "ja"):
|
||||
set_language(lang)
|
||||
print(f" {lang}: general={tr('settings.group.general')!r} "
|
||||
f"basic={tr('schedtask.g_basic')!r}")
|
||||
for key in ("settings.group.general", "schedtask.g_basic"):
|
||||
if tr(key) == key:
|
||||
fails.append(f"thieu ban dich {key} cho {lang}")
|
||||
set_language("vi")
|
||||
|
||||
print()
|
||||
@@ -134,7 +122,7 @@ def main() -> int:
|
||||
for x in fails:
|
||||
print(" " + x)
|
||||
return 1
|
||||
print("KET QUA: hai hop thoai co muc luc, khong mat control nao")
|
||||
print("KET QUA: hai hop thoai dung danh sach trai + panel phai, khong mat field")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user