All checks were successful
Zapret source guards / validate (push) Successful in 4s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
214 lines
6.3 KiB
Python
214 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
IMPORTANT_CONTROL_MARKERS = (
|
|
"PushButton(",
|
|
"PrimaryPushButton(",
|
|
"ToolButton(",
|
|
"PrimaryToolButton(",
|
|
"TransparentToolButton(",
|
|
"LineEdit(",
|
|
"SearchLineEdit(",
|
|
"ComboBox(",
|
|
"CheckBox(",
|
|
"SwitchButton(",
|
|
"PlainTextEdit(",
|
|
"TextEdit(",
|
|
"QListWidget(",
|
|
"QTableWidget(",
|
|
)
|
|
|
|
ACCESSIBILITY_MARKERS = (
|
|
"set_control_accessibility",
|
|
"set_state_text",
|
|
"set_item_accessible_text",
|
|
"set_segmented_items_accessibility",
|
|
"enable_keyboard_click",
|
|
"enable_keyboard_toggle",
|
|
"accessibility",
|
|
)
|
|
|
|
KEYBOARD_COLLECTION_WIDGET_MARKERS = (
|
|
"QListWidget(",
|
|
"(QListWidget):",
|
|
"ListView(",
|
|
"(ListView):",
|
|
"QTableWidget(",
|
|
"(QTableWidget):",
|
|
"TableWidget(",
|
|
"(TableWidget):",
|
|
"SegmentedWidget(",
|
|
)
|
|
|
|
KEYBOARD_COLLECTION_ACCESS_MARKERS = (
|
|
"keyPressEvent",
|
|
"focusInEvent",
|
|
"currentItemChanged",
|
|
"currentChanged",
|
|
"currentCellChanged",
|
|
"set_segmented_items_accessibility",
|
|
"AccessibleTextRole",
|
|
"setFocusPolicy(Qt.FocusPolicy.StrongFocus)",
|
|
"screenReaderStateText",
|
|
"enable_keyboard_click",
|
|
"enable_keyboard_toggle",
|
|
"set_item_accessible_text",
|
|
)
|
|
|
|
CUSTOM_MOUSE_ACTION_MARKERS = (
|
|
"def mousePressEvent",
|
|
"def mouseReleaseEvent",
|
|
"def mouseDoubleClickEvent",
|
|
".mousePressEvent =",
|
|
".mouseReleaseEvent =",
|
|
".mouseDoubleClickEvent =",
|
|
)
|
|
|
|
CUSTOM_KEYBOARD_ACTION_MARKERS = (
|
|
"def keyPressEvent",
|
|
".keyPressEvent =",
|
|
"enable_keyboard_click",
|
|
"enable_keyboard_toggle",
|
|
)
|
|
|
|
ALLOWED_NO_FOCUS_SOURCES = {
|
|
"src/hosts/ui/services_build.py",
|
|
"src/ui/accessibility.py",
|
|
"src/ui/segmented_accessibility.py",
|
|
"src/ui/window_preset_file_drop.py",
|
|
"src/ui/widgets/fluent_scrollbar.py",
|
|
"src/ui/widgets/win11_controls.py",
|
|
}
|
|
|
|
|
|
def _source(path: str) -> str:
|
|
return (ROOT / path).read_text(encoding="utf-8")
|
|
|
|
|
|
def test_ui_files_with_important_controls_keep_accessibility_wiring() -> None:
|
|
missing: list[str] = []
|
|
for path in sorted((ROOT / "src").rglob("*.py")):
|
|
rel_path = path.relative_to(ROOT).as_posix()
|
|
if rel_path.startswith("src/themes/cache/"):
|
|
continue
|
|
source = path.read_text(encoding="utf-8", errors="ignore")
|
|
if not any(marker in source for marker in IMPORTANT_CONTROL_MARKERS):
|
|
continue
|
|
if any(marker in source for marker in ACCESSIBILITY_MARKERS):
|
|
continue
|
|
missing.append(rel_path)
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_collection_controls_keep_keyboard_or_row_accessibility() -> None:
|
|
missing: list[str] = []
|
|
for path in sorted((ROOT / "src").rglob("*.py")):
|
|
rel_path = path.relative_to(ROOT).as_posix()
|
|
if rel_path.startswith("src/themes/cache/"):
|
|
continue
|
|
source = path.read_text(encoding="utf-8", errors="ignore")
|
|
if not any(marker in source for marker in KEYBOARD_COLLECTION_WIDGET_MARKERS):
|
|
continue
|
|
if any(marker in source for marker in KEYBOARD_COLLECTION_ACCESS_MARKERS):
|
|
continue
|
|
missing.append(rel_path)
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_custom_mouse_actions_keep_keyboard_activation() -> None:
|
|
missing: list[str] = []
|
|
for path in sorted((ROOT / "src").rglob("*.py")):
|
|
rel_path = path.relative_to(ROOT).as_posix()
|
|
if rel_path.startswith("src/themes/cache/"):
|
|
continue
|
|
source = path.read_text(encoding="utf-8", errors="ignore")
|
|
if not any(marker in source for marker in CUSTOM_MOUSE_ACTION_MARKERS):
|
|
continue
|
|
if any(marker in source for marker in CUSTOM_KEYBOARD_ACTION_MARKERS):
|
|
continue
|
|
missing.append(rel_path)
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_no_focus_usage_stays_limited_to_decorative_helpers() -> None:
|
|
unexpected: list[str] = []
|
|
for path in sorted((ROOT / "src").rglob("*.py")):
|
|
rel_path = path.relative_to(ROOT).as_posix()
|
|
if rel_path.startswith("src/themes/cache/"):
|
|
continue
|
|
source = path.read_text(encoding="utf-8", errors="ignore")
|
|
if "FocusPolicy.NoFocus" not in source:
|
|
continue
|
|
if rel_path in ALLOWED_NO_FOCUS_SOURCES:
|
|
continue
|
|
unexpected.append(rel_path)
|
|
|
|
assert unexpected == []
|
|
|
|
|
|
def test_profile_shell_keeps_toolbar_accessibility_helper() -> None:
|
|
source = _source("src/profile/ui/shell.py")
|
|
|
|
assert "from profile.ui.shell_accessibility import apply_profile_shell_accessibility" in source
|
|
assert "apply_profile_shell_accessibility(" in source
|
|
for control_name in (
|
|
"add_profile_btn",
|
|
"request_btn",
|
|
"view_menu_btn",
|
|
"order_btn",
|
|
"info_btn",
|
|
"profile_search_input",
|
|
):
|
|
assert f"{control_name}={control_name}" in source
|
|
|
|
|
|
def test_user_presets_build_keeps_accessibility_helper_for_toolbar_and_list() -> None:
|
|
source = _source("src/presets/ui/common/user_presets_build.py")
|
|
|
|
assert (
|
|
"from presets.ui.common.user_presets_accessibility import apply_user_presets_accessibility"
|
|
in source
|
|
)
|
|
assert source.count("apply_user_presets_accessibility(") >= 2
|
|
for control_name in (
|
|
"get_configs_btn",
|
|
"create_btn",
|
|
"import_btn",
|
|
"open_folder_btn",
|
|
"reset_all_btn",
|
|
"presets_info_btn",
|
|
"info_btn",
|
|
"preset_search_input",
|
|
"presets_list",
|
|
):
|
|
assert f"{control_name}={control_name}" in source
|
|
|
|
|
|
def test_premium_build_keeps_button_accessibility_helper() -> None:
|
|
source = _source("src/donater/ui/build.py")
|
|
|
|
assert "apply_premium_button_accessibility" in source
|
|
for keyword in (
|
|
"activate_btn=activate_btn",
|
|
"open_bot_btn=open_bot_btn",
|
|
"refresh_btn=refresh_btn",
|
|
"change_key_btn=change_key_btn",
|
|
"test_btn=test_btn",
|
|
"extend_btn=extend_btn",
|
|
):
|
|
assert keyword in source
|
|
|
|
|
|
def test_notification_infobar_keeps_accessibility_for_dynamic_actions() -> None:
|
|
source = _source("src/ui/window_notification_center.py")
|
|
|
|
assert "self._set_infobar_accessibility(bar" in source
|
|
assert "self._set_infobar_action_button_accessibility(btn, action, button_text)" in source
|
|
assert 'name = f"Действие уведомления: {button_text}"' in source
|