"""Check the Dashboard header regroup, on the real widget, offscreen. Nine controls were on one row. They are now on two, grouped by what they do — so this asserts that all nine are still present, still wired, and that the header really is two rows now (row 1 = title + Refresh, row 2 = the selectors). Run: python tools/check_dashboard.py """ from __future__ import annotations import os import sys sys.stdout.reconfigure(encoding="utf-8", errors="replace") 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 _apply_theme, _isolate_home, _load_fonts # noqa: E402 HEADER = ["_title", "chart_prev_btn", "_chart_period_lbl", "chart_next_btn", "gran_combo", "metric_combo", "currency_lbl", "currency_combo", "refresh_btn"] def main() -> int: sandbox = _isolate_home() from PySide6.QtWidgets import QApplication app = QApplication([]) _load_fonts() _apply_theme(app) # measure the styled widget, not a bare one from cowork_local.config import AppConfig, CONFIG_DIR assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}" from seed_demo_data import seed seed() from cowork_local.i18n import set_language from cowork_local.state import AppContext from cowork_local.ui.dashboard_tab import DashboardTab set_language("vi") tab = DashboardTab(AppContext(AppConfig.load())) tab.resize(1100, 800) tab.show() app.processEvents() tab.refresh() app.processEvents() fails: list[str] = [] missing = [n for n in HEADER if getattr(tab, n, None) is None] print(f"control header con nguyen: {len(HEADER) - len(missing)}/{len(HEADER)}") if missing: fails.append(f"mat control: {missing}") # Two rows: everything in the header must sit at one of exactly two y bands. tops = {} for n in HEADER: w = getattr(tab, n) tops.setdefault(round(w.mapTo(tab, w.rect().topLeft()).y() / 10), []).append(n) print(f"so hang cua header : {len(tops)}") for band, names in sorted(tops.items()): print(f" y~{band * 10:>4}px : {names}") if len(tops) != 2: fails.append(f"header co {len(tops)} hang, cho 2") # Still wired: changing the metric must not throw and must stick. before = tab.metric_combo.currentData() tab.metric_combo.setCurrentIndex(1 - tab.metric_combo.currentIndex()) app.processEvents() after = tab.metric_combo.currentData() print(f"doi chi so bieu do : {before} -> {after}") if after == before: fails.append("combo chi so khong doi duoc") tab.refresh_btn.click() app.processEvents() print("bam Lam moi : khong loi") print() if fails: print("*** LOI ***") for f in fails: print(" " + f) return 1 print("KET QUA: header Dashboard chia 2 hang, du 9 control") return 0 if __name__ == "__main__": raise SystemExit(main())