check_design_parity.py reads its checklist from the audit page's own
proposals; it now reports every one of the 31 as implemented, with no
deliberate divergences left.
* Schedule: the Kanban/Calendar drop-list became a pair of tabs, and the
Running lane is outlined while it holds anything — dropping a card there
starts the task for real, so it should not look like the other six.
* Cowork: agent / routing / usage / folder moved out of the typing box into
their own status strip beneath it, styled as status rather than a second
toolbar. All of them stay interactive; the design's read-only strip would
have cost features.
* Folder: the path is written as the screen's title instead of sitting in a
read-only text box that looked editable and cost a row.
* GraphRAG: the second toolbar row is gone (Export joined the first), and
the one button that relabelled itself became Đồ thị | Tin nhắn tabs, so
the view you are NOT in is named too.
* Settings gained the theme picker, so language / provider / theme are all
reachable there as well as on the rail's account row.
* Task editor: the five group boxes are grouped into three step tabs
(Nội dung → Lịch chạy → Liên kết). All 22 fields verified present after
the move; only the old section index is gone, replaced by the tabs.
* The assistant dot now clears a screen's own bottom bar (Cowork's
composer), measured from the composer's top edge in window coordinates.
Also adds .gitattributes: without it a Windows checkout records CRLF and
every file reads as fully rewritten to a Linux CI runner.
Verification: 7 check_*.py suites green, no screen clipped at 1920/1366/1280,
and no dialog scrolls sideways at 9/11/14pt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
"""Check the section index added to Settings and the Task editor, offscreen.
|
|
|
|
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.
|
|
|
|
Run: python tools/check_dialogs.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO.parent))
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
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
|
|
|
|
|
|
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."""
|
|
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
|
|
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}")
|
|
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")
|
|
|
|
# Each row must scroll somewhere different (and the last one furthest down).
|
|
from PySide6.QtWidgets import QScrollArea
|
|
scroll = dlg.findChildren(QScrollArea)[0]
|
|
positions = []
|
|
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
|
|
|
|
|
|
def main() -> int:
|
|
sandbox = _isolate_home()
|
|
from PySide6.QtWidgets import QApplication
|
|
|
|
app = QApplication([])
|
|
_load_fonts()
|
|
|
|
from cowork_local.config import AppConfig, CONFIG_DIR
|
|
assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}"
|
|
|
|
from cowork_local.i18n import set_language, tr
|
|
from cowork_local.state import AppContext
|
|
from cowork_local.ui.settings_dialog import SettingsDialog
|
|
from cowork_local.ui.task_editor_dialog import TaskEditorDialog
|
|
|
|
set_language("vi")
|
|
ctx = AppContext(AppConfig.load())
|
|
|
|
fails = []
|
|
s = SettingsDialog(ctx)
|
|
s.resize(900, 600)
|
|
s.show()
|
|
app.processEvents()
|
|
n_s, f = check("Cai dat", s, app, 5)
|
|
fails += f
|
|
|
|
t = TaskEditorDialog(ctx=ctx) # task=None → a new task, all fields present
|
|
t.resize(900, 600)
|
|
t.show()
|
|
app.processEvents()
|
|
n_t, f = check_tabs("Task editor", t, app, 3)
|
|
fails += f
|
|
|
|
# Translations for the two names that had to be invented for the index.
|
|
print()
|
|
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()
|
|
if fails:
|
|
print("*** LOI ***")
|
|
for x in fails:
|
|
print(" " + x)
|
|
return 1
|
|
print("KET QUA: hai hop thoai co muc luc, khong mat control nao")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|