zapretgui/tests/test_launch_title_badge.py
loop-uh 8fcf2b3fda
All checks were successful
Zapret source guards / validate (push) Successful in 12s
Windows-сборка / Dev Dev 21.1.6.48 опубликован
Между значками «PREMIUM» и «Работает» в заголовке окна промежуток 8 px — значки больше не слипаются
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:35:45 +03:00

154 lines
6.5 KiB
Python

from __future__ import annotations
import os
import sys
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import Mock
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
PROJECT_SRC = Path(__file__).resolve().parents[1] / "src"
if str(PROJECT_SRC) not in sys.path:
sys.path.insert(0, str(PROJECT_SRC))
from PyQt6.QtWidgets import QApplication # noqa: E402
from app.state_store import MainWindowStateStore # noqa: E402
from ui.fluent_app_window import ZapretFluentWindow # noqa: E402
from ui.launch_title_badge import LaunchTitleBadge, build_launch_badge_view # noqa: E402
from ui.navigation.search import attach_sidebar_search_to_titlebar, update_titlebar_search_width # noqa: E402
from ui.window_state_binder import bind_launch_title_badge, bind_window_ui_state # noqa: E402
from ui.subscription_title_badge import SubscriptionTitleBadge # noqa: E402
from ui.window_ui_facade import _SidebarSearchNavWidget # noqa: E402
class LaunchBadgeViewTests(unittest.TestCase):
def test_texts_follow_phase(self) -> None:
cases = {
"running": ("Работает", "Zapret 2 работает · нажмите, чтобы остановить"),
"starting": ("Запуск…", "Zapret 2 запускается · нажмите, чтобы остановить"),
"autostart_pending": ("Запуск…", "Zapret 2 запускается · нажмите, чтобы остановить"),
"stopping": ("Остановка…", "Zapret 2 останавливается…"),
"stopped": ("Остановлен", "Zapret 2 остановлен · нажмите, чтобы запустить"),
"failed": ("Ошибка", "Ошибка запуска Zapret 2 · нажмите, чтобы попробовать снова"),
}
for phase, (text, tooltip) in cases.items():
with self.subTest(phase=phase):
view = build_launch_badge_view(phase=phase, launch_method="zapret2_mode", language="ru")
self.assertEqual((view.text, view.tooltip), (text, tooltip))
def test_english_texts_and_mode_names(self) -> None:
view = build_launch_badge_view(phase="running", launch_method="zapret1_mode", language="en")
self.assertEqual(view.text, "Running")
self.assertEqual(view.tooltip, "Zapret 1 is running · click to stop")
class LaunchTitleBadgeBindingTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def _make_window(self) -> ZapretFluentWindow:
window = ZapretFluentWindow()
window.ui_session = SimpleNamespace(
sidebar_search_nav_widget=_SidebarSearchNavWidget(),
sidebar_search_titlebar_attached=False,
ui_language="ru",
)
window.resize(1400, 900)
window.show()
self._app.processEvents()
attach_sidebar_search_to_titlebar(window)
update_titlebar_search_width(window)
self.addCleanup(window.deleteLater)
return window
@staticmethod
def _set_phase(store: MainWindowStateStore, phase: str) -> None:
store.update(launch_phase=phase, launch_running=phase == "running", launch_method="zapret2_mode")
def test_badge_follows_launch_phase(self) -> None:
window = self._make_window()
store = MainWindowStateStore()
badge = bind_launch_title_badge(window, store, Mock())
self.assertFalse(badge.isHidden())
self.assertEqual(badge.text(), "Остановлен")
self._set_phase(store, "starting")
self.assertEqual(badge.text(), "Запуск…")
self._set_phase(store, "running")
self.assertEqual(badge.text(), "Работает")
self.assertIn("остановить", badge.toolTip())
def test_click_toggles_through_launch_control(self) -> None:
window = self._make_window()
store = MainWindowStateStore()
launch_control = Mock()
badge = bind_launch_title_badge(window, store, launch_control)
badge.click()
launch_control.toggle.assert_called_once_with()
def test_badge_is_disabled_while_stopping(self) -> None:
window = self._make_window()
store = MainWindowStateStore()
badge = bind_launch_title_badge(window, store, Mock())
self._set_phase(store, "stopping")
self.assertFalse(badge.isEnabled())
self._set_phase(store, "stopped")
self.assertTrue(badge.isEnabled())
def test_badge_sits_after_subscription_badge(self) -> None:
window = self._make_window()
store = MainWindowStateStore()
bind_window_ui_state(window, store, launch_control=Mock())
layout = window.titleBar.hBoxLayout
subscription = window.titleBar.findChild(SubscriptionTitleBadge)
launch = window.titleBar.findChild(LaunchTitleBadge)
# Между значками только промежуток, других элементов нет.
self.assertEqual(layout.indexOf(launch), layout.indexOf(subscription) + 2)
self.assertIsNotNone(layout.itemAt(layout.indexOf(subscription) + 1).spacerItem())
def test_badges_have_visible_gap(self) -> None:
from ui.window_state_binder import TITLE_BADGE_GAP
window = self._make_window()
store = MainWindowStateStore()
bind_window_ui_state(window, store, launch_control=Mock())
subscription = window.titleBar.findChild(SubscriptionTitleBadge)
subscription.show()
launch = window.titleBar.findChild(LaunchTitleBadge)
window.titleBar.hBoxLayout.activate()
self._app.processEvents()
gap = launch.geometry().left() - subscription.geometry().right() - 1
self.assertGreaterEqual(gap, TITLE_BADGE_GAP)
def test_no_badge_without_launch_control(self) -> None:
window = self._make_window()
self.assertIsNone(bind_launch_title_badge(window, MainWindowStateStore(), None))
self.assertIsNone(window.titleBar.findChild(LaunchTitleBadge))
def test_binding_twice_reuses_single_badge(self) -> None:
window = self._make_window()
store = MainWindowStateStore()
first = bind_launch_title_badge(window, store, Mock())
second = bind_launch_title_badge(window, store, Mock())
self.assertIs(first, second)
self.assertEqual(len(window.titleBar.findChildren(LaunchTitleBadge)), 1)
if __name__ == "__main__":
unittest.main()