zapretgui/tests/test_premium_controls_accessibility.py
loop-uh 1f957e6a36
All checks were successful
Zapret source guards / validate (push) Successful in 4s
Windows-сборка / Dev Dev 21.1.5.61 опубликован
Автоматически завершать привязку Premium
2026-08-14 19:49:51 +03:00

465 lines
22 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import os
import unittest
from datetime import datetime
from types import SimpleNamespace
from unittest.mock import Mock, patch
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from qfluentwidgets import PushButton, SubtitleLabel
from PyQt6.QtWidgets import QApplication, QWidget
from donater.ui.build import (
build_premium_actions_section,
build_premium_activation_section,
build_premium_device_info_section,
)
from donater.ui.page_plans import build_connection_test_start_plan
from donater.ui.pairing_workflow import (
apply_device_info_snapshot_labels,
apply_pair_code_result_ui,
apply_pair_code_start_ui,
)
from donater.ui.page_lifecycle import apply_premium_language, render_activation_status_label
from donater.ui.status_workflow import (
apply_connection_test_plan,
apply_status_check_success,
render_server_status_label,
)
from donater.ui.accessibility import apply_premium_button_accessibility
from donater.ui.page import PremiumPage
from donater.ui.status_card import StatusCard
class _DialogButton:
def __init__(self) -> None:
self._accessible_name = ""
self._accessible_description = ""
def accessibleName(self) -> str: # noqa: N802
return self._accessible_name
def setAccessibleName(self, text: str) -> None: # noqa: N802
self._accessible_name = str(text)
def accessibleDescription(self) -> str: # noqa: N802
return self._accessible_description
def setAccessibleDescription(self, text: str) -> None: # noqa: N802
self._accessible_description = str(text)
class _MessageBox:
instances: list["_MessageBox"] = []
def __init__(self, title: str, body: str, parent=None) -> None:
self.title = title
self.body = body
self.parent = parent
self.yesButton = _DialogButton()
self.cancelButton = _DialogButton()
self.exec_called = False
_MessageBox.instances.append(self)
def exec(self) -> bool:
self.exec_called = True
return False
class PremiumControlsAccessibilityTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def test_premium_buttons_have_screen_reader_names(self) -> None:
parent = QWidget()
activation = build_premium_activation_section(
tr=lambda _key, default: default,
on_create_pair_code=lambda: None,
)
device = build_premium_device_info_section(
tr=lambda _key, default: default,
on_open_bot=lambda: None,
)
actions = build_premium_actions_section(
parent=parent,
tr=lambda _key, default: default,
on_check_status=lambda: None,
on_change_key=lambda: None,
on_test_connection=lambda: None,
on_open_bot=lambda: None,
)
self.assertEqual(activation.activate_btn.accessibleName(), "Создать код привязки Premium")
self.assertEqual(activation.activate_btn.property("screenReaderStateText"), "Создать код привязки Premium")
self.assertIn("Telegram", activation.activate_btn.accessibleDescription())
self.assertEqual(
activation.instructions_label.property("screenReaderStateText"),
(
"Инструкция Premium: 1. Нажмите «Создать код» "
"2. Отправьте код боту @zapretvpns_bot в Telegram (сообщением) "
"3. Вернитесь сюда — приложение обновит статус автоматически"
),
)
self.assertIn("как привязать Premium", activation.instructions_label.accessibleDescription())
self.assertEqual(device.open_bot_btn.accessibleName(), "Открыть Premium-бота")
self.assertIn("Telegram", device.open_bot_btn.accessibleDescription())
self.assertEqual(actions.refresh_btn.accessibleName(), "Обновить Premium-статус")
self.assertIn("Premium-статус", actions.refresh_btn.accessibleDescription())
self.assertEqual(actions.change_key_btn.accessibleName(), "Сбросить Premium-активацию")
self.assertIn("Удаляет токен", actions.change_key_btn.accessibleDescription())
self.assertEqual(actions.test_btn.accessibleName(), "Проверить соединение Premium")
self.assertEqual(actions.test_btn.property("screenReaderStateText"), "Проверить соединение Premium")
self.assertIn("Premium backend", actions.test_btn.accessibleDescription())
self.assertEqual(actions.extend_btn.accessibleName(), "Продлить Premium-подписку")
self.assertIn("Telegram-бота", actions.extend_btn.accessibleDescription())
self.assertEqual(activation.key_input.accessibleName(), "Код привязки Premium: пока не создан")
self.assertIn("код, который нужно отправить", activation.key_input.accessibleDescription())
def test_disabled_premium_buttons_report_unavailable_state(self) -> None:
activate_btn = PushButton("Создать код")
test_btn = PushButton("Проверить соединение")
self.addCleanup(activate_btn.deleteLater)
self.addCleanup(test_btn.deleteLater)
activate_btn.setEnabled(False)
test_btn.setEnabled(False)
apply_premium_button_accessibility(
tr_fn=lambda _key, default: default,
activate_btn=activate_btn,
test_btn=test_btn,
)
self.assertEqual(activate_btn.accessibleName(), "Создать код привязки Premium, недоступно")
self.assertEqual(
activate_btn.property("screenReaderStateText"),
"Создать код привязки Premium, недоступно",
)
self.assertIn("сейчас недоступно", activate_btn.accessibleDescription().lower())
self.assertEqual(test_btn.accessibleName(), "Проверить соединение Premium, недоступно")
self.assertEqual(
test_btn.property("screenReaderStateText"),
"Проверить соединение Premium, недоступно",
)
self.assertIn("сейчас недоступно", test_btn.accessibleDescription().lower())
def test_reset_activation_dialog_buttons_are_named_for_screen_reader(self) -> None:
page = PremiumPage.__new__(PremiumPage)
page._request_reset_storage = Mock()
page._tr = lambda _key, default, **kwargs: default.format(**kwargs) if kwargs else default
page.window = lambda: None
_MessageBox.instances = []
with patch("donater.ui.page.MessageBox", _MessageBox):
PremiumPage._change_key(page)
dialog = _MessageBox.instances[0]
self.assertEqual(dialog.yesButton.accessibleName(), "Сбросить Premium-активацию")
self.assertIn(
"сервер отзовёт точную привязку",
dialog.yesButton.accessibleDescription(),
)
self.assertEqual(dialog.cancelButton.accessibleName(), "Отменить сброс Premium-активации")
self.assertTrue(dialog.exec_called)
page._request_reset_storage.assert_not_called()
def test_premium_device_info_labels_expose_state_text(self) -> None:
device = build_premium_device_info_section(
tr=lambda _key, default: default,
on_open_bot=lambda: None,
)
self.assertEqual(device.device_id_label.accessibleName(), "ID устройства: загрузка...")
self.assertEqual(device.saved_key_label.accessibleName(), "Токен устройства: не найден")
self.assertEqual(device.last_check_label.accessibleName(), "Последняя проверка Premium: —")
self.assertEqual(device.server_status_label.accessibleName(), "Статус Premium-сервера: проверка...")
apply_device_info_snapshot_labels(
snapshot={
"device_id": "abcdef0123456789abcdef",
"device_token": "token",
"pair_code": "ABCD12EF",
"last_check": datetime(2026, 6, 10, 12, 30),
},
tr=lambda _key, default, **kwargs: default.format(**kwargs) if kwargs else default,
device_id_label=device.device_id_label,
saved_key_label=device.saved_key_label,
last_check_label=device.last_check_label,
)
self.assertEqual(device.device_id_label.accessibleName(), "ID устройства: abcdef0123456789...")
self.assertEqual(device.saved_key_label.accessibleName(), "Токен устройства: есть. Код привязки: ABCD12EF")
self.assertEqual(device.last_check_label.accessibleName(), "Последняя проверка Premium: 10.06.2026 12:30")
def test_premium_language_refresh_keeps_screen_reader_names(self) -> None:
parent = QWidget()
activation = build_premium_activation_section(
tr=lambda _key, default: default,
on_create_pair_code=lambda: None,
)
device = build_premium_device_info_section(
tr=lambda _key, default: default,
on_open_bot=lambda: None,
)
actions = build_premium_actions_section(
parent=parent,
tr=lambda _key, default: default,
on_check_status=lambda: None,
on_change_key=lambda: None,
on_test_connection=lambda: None,
on_open_bot=lambda: None,
)
apply_premium_language(
tr_fn=lambda _key, default: default,
activation_in_progress=True,
connection_test_in_progress=True,
instructions_label=activation.instructions_label,
key_input=activation.key_input,
activate_btn=activation.activate_btn,
open_bot_btn=device.open_bot_btn,
refresh_btn=actions.refresh_btn,
change_key_btn=actions.change_key_btn,
extend_btn=actions.extend_btn,
test_btn=actions.test_btn,
render_server_status_fn=lambda: None,
render_days_label_fn=lambda: None,
render_status_badge_fn=lambda: None,
render_activation_status_fn=lambda: None,
)
self.assertEqual(activation.activate_btn.accessibleName(), "Создание кода привязки Premium")
self.assertEqual(activation.activate_btn.property("screenReaderStateText"), "Создание кода привязки Premium")
self.assertEqual(actions.test_btn.accessibleName(), "Проверка соединения Premium выполняется")
self.assertEqual(actions.test_btn.property("screenReaderStateText"), "Проверка соединения Premium выполняется")
self.assertEqual(actions.refresh_btn.accessibleName(), "Обновить Premium-статус")
self.assertEqual(actions.extend_btn.accessibleName(), "Продлить Premium-подписку")
self.assertEqual(activation.key_input.accessibleName(), "Код привязки Premium: пока не создан")
self.assertEqual(
activation.instructions_label.property("screenReaderStateText"),
(
"Инструкция Premium: 1. Нажмите «Создать код» "
"2. Отправьте код боту @zapretvpns_bot в Telegram (сообщением) "
"3. Вернитесь сюда — приложение обновит статус автоматически"
),
)
def test_linked_status_clears_consumed_pair_code_from_ui(self) -> None:
activation = build_premium_activation_section(
tr=lambda _key, default, **kwargs: default.format(**kwargs),
on_create_pair_code=lambda: None,
)
activation.key_input.setText("7YRFE33E")
activation_status = Mock()
apply_status_check_success(
{
"activated": True,
"is_premium": True,
"found": True,
"days_remaining": 30,
"status": "Активировано",
},
tr=lambda _key, default, **kwargs: default.format(**kwargs),
refresh_btn=SimpleNamespace(set_loading=lambda _value: None),
key_input=activation.key_input,
update_device_info=lambda: None,
set_status_badge=lambda **_kwargs: None,
set_activation_status=activation_status,
set_activation_section_visible=lambda _visible: None,
stop_autopoll=lambda: None,
sync_autopoll=lambda: None,
apply_subscription_state=lambda _premium, _days: None,
)
self.assertEqual(activation.key_input.text(), "")
self.assertEqual(
activation.key_input.accessibleName(),
"Код привязки Premium: пока не создан",
)
activation_status.assert_called_once_with(
text_key="page.premium.activation.success.linked_active",
text_default="✅ Устройство привязано. Premium активен.",
)
def test_premium_runtime_actions_update_screen_reader_names(self) -> None:
parent = QWidget()
activation = build_premium_activation_section(
tr=lambda _key, default: default,
on_create_pair_code=lambda: None,
)
actions = build_premium_actions_section(
parent=parent,
tr=lambda _key, default: default,
on_check_status=lambda: None,
on_change_key=lambda: None,
on_test_connection=lambda: None,
on_open_bot=lambda: None,
)
apply_pair_code_start_ui(
activate_btn=activation.activate_btn,
key_input=activation.key_input,
tr=lambda _key, default: default,
set_activation_status=lambda **_kwargs: None,
stop_autopoll=lambda: None,
)
apply_connection_test_plan(
build_connection_test_start_plan(checker_ready=True),
tr=lambda _key, default: default,
test_btn=actions.test_btn,
render_server_status=lambda: None,
set_server_status_state=lambda *_args: None,
)
self.assertEqual(activation.activate_btn.accessibleName(), "Создание кода привязки Premium")
self.assertEqual(activation.activate_btn.property("screenReaderStateText"), "Создание кода привязки Premium")
self.assertEqual(actions.test_btn.accessibleName(), "Проверка соединения Premium выполняется")
self.assertEqual(actions.test_btn.property("screenReaderStateText"), "Проверка соединения Premium выполняется")
self.assertEqual(activation.key_input.accessibleName(), "Код привязки Premium: пока не создан")
apply_pair_code_result_ui(
(True, "ok", "ABCD12EF"),
activate_btn=activation.activate_btn,
key_input=activation.key_input,
tr=lambda _key, default: default,
set_activation_status=lambda **_kwargs: None,
update_device_info=lambda: None,
start_autopoll=lambda: None,
stop_autopoll=lambda: None,
)
self.assertEqual(activation.key_input.accessibleName(), "Код привязки Premium: ABCD12EF")
self.assertEqual(activation.key_input.property("screenReaderStateText"), "Код привязки Premium: ABCD12EF")
def test_premium_status_labels_expose_state_text(self) -> None:
activation = build_premium_activation_section(
tr=lambda _key, default: default,
on_create_pair_code=lambda: None,
)
device = build_premium_device_info_section(
tr=lambda _key, default, **kwargs: default.format(**kwargs),
on_open_bot=lambda: None,
)
render_activation_status_label(
activation_status_state={
"text_key": "page.premium.activation.created",
"text_default": "Код создан: {code}",
"text_kwargs": {"code": "ABCD12EF"},
},
tr_fn=lambda _key, default, **kwargs: default.format(**kwargs),
activation_status_label=activation.activation_status,
)
render_server_status_label(
device.server_status_label,
tr=lambda _key, default, **kwargs: default.format(**kwargs),
mode="result",
message="соединение работает",
success=True,
)
self.assertEqual(
activation.activation_status.property("screenReaderStateText"),
"Статус Premium-активации: Код создан: ABCD12EF",
)
self.assertEqual(
device.server_status_label.property("screenReaderStateText"),
"Статус Premium-сервера: соединение работает",
)
def test_premium_page_activation_status_updates_state_text(self) -> None:
activation = build_premium_activation_section(
tr=lambda _key, default: default,
on_create_pair_code=lambda: None,
)
page = SimpleNamespace(
activation_status=activation.activation_status,
_tr=lambda _key, default, **kwargs: default.format(**kwargs),
)
PremiumPage._set_activation_status(
page,
text_key="page.premium.activation.created",
text_default="Код создан: {code}",
text_kwargs={"code": "ABCD12EF"},
)
self.assertEqual(
activation.activation_status.property("screenReaderStateText"),
"Статус Premium-активации: Код создан: ABCD12EF",
)
def test_premium_days_label_exposes_state_text(self) -> None:
page = SimpleNamespace(
days_label=SubtitleLabel(""),
_tr=lambda _key, default, **kwargs: default.format(**kwargs),
)
self.addCleanup(page.days_label.deleteLater)
page._days_state_kind = "normal"
page._days_state_value = 40
PremiumPage._render_days_label(page)
self.assertEqual(page.days_label.accessibleName(), "Осталось дней Premium: 40")
self.assertEqual(page.days_label.property("screenReaderStateText"), "Осталось дней Premium: 40")
page._days_state_kind = "urgent"
page._days_state_value = 3
PremiumPage._render_days_label(page)
self.assertEqual(page.days_label.accessibleName(), "Premium срочно нужно продлить, осталось дней: 3")
self.assertEqual(
page.days_label.property("screenReaderStateText"),
"Premium срочно нужно продлить, осталось дней: 3",
)
page._days_state_kind = "none"
page._days_state_value = 0
PremiumPage._render_days_label(page)
self.assertEqual(page.days_label.accessibleName(), "Premium-подписка не активна")
self.assertEqual(page.days_label.property("screenReaderStateText"), "Premium-подписка не активна")
def test_premium_status_card_exposes_state_text(self) -> None:
card = StatusCard()
self.addCleanup(card.deleteLater)
card.set_status("Premium активен", "Осталось 7 дней", "active")
self.assertEqual(card.accessibleName(), "Статус Premium: Premium активен. Осталось 7 дней")
self.assertEqual(
card.property("screenReaderStateText"),
"Статус Premium: Premium активен. Осталось 7 дней",
)
def test_premium_status_card_inner_labels_expose_state_text(self) -> None:
card = StatusCard()
self.addCleanup(card.deleteLater)
card.set_status("Premium активен", "Осталось 7 дней", "active")
self.assertEqual(card._icon_lbl.accessibleName(), "Индикатор Premium: Premium активен. Осталось 7 дней")
self.assertEqual(card._icon_lbl.property("screenReaderStateText"), "Индикатор Premium: Premium активен. Осталось 7 дней")
self.assertEqual(card._title_lbl.accessibleName(), "Статус Premium: Premium активен")
self.assertEqual(card._title_lbl.property("screenReaderStateText"), "Статус Premium: Premium активен")
self.assertEqual(card._detail_lbl.accessibleName(), "Детали Premium: Осталось 7 дней")
self.assertEqual(card._detail_lbl.property("screenReaderStateText"), "Детали Premium: Осталось 7 дней")
def test_premium_status_card_exposes_initial_empty_state(self) -> None:
card = StatusCard()
self.addCleanup(card.deleteLater)
self.assertEqual(card.accessibleName(), "Статус Premium: статус пока не загружен")
self.assertEqual(
card.property("screenReaderStateText"),
"Статус Premium: статус пока не загружен",
)
self.assertIn("Premium", card.accessibleDescription())
if __name__ == "__main__":
unittest.main()