from __future__ import annotations import os import unittest from types import SimpleNamespace from unittest.mock import Mock, patch os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtWidgets import QApplication from javis.core.provider_router import ApprovalChoice from javis.providers.base import ChatMessage from javis.security.privacy import CloudPolicy, PrivacyDecision from javis.ui.main_window import MainWindow from javis.ui.models import DesktopStatus, GenerationState from javis.ui.workers import ApprovalBridge class _Controller: def __init__(self) -> None: self.active_session_id = None self.state = GenerationState.READY self.session = SimpleNamespace( id="session-one", title="Desktop-Test", last_provider="ollama", message_count=2, updated_at="2026-07-30T20:00:00+00:00", ) def list_sessions(self, search: str = ""): return [self.session] if not search or search in self.session.title else [] def load_session(self, session_id: str): self.active_session_id = session_id return SimpleNamespace( session=self.session, messages=[ ChatMessage("user", "Hallo"), ChatMessage("assistant", "Guten Tag"), ], ) def new_session(self): self.active_session_id = self.session.id return self.session def rename_active_session(self, title: str): self.session.title = title return self.session def set_mode(self, _mode: str) -> None: return def status(self) -> DesktopStatus: return DesktopStatus( state=self.state, mode="Lokal", provider="ollama", ollama_reachable=True, local_model="test-model", privacy_policy="never", cloud_requests_today=0, local_fallback=False, data_dir="D:\\Testdaten", ) class _MessageBox: Question = object() AcceptRole = object() RejectRole = object() DestructiveRole = object() selected_label = "" def __init__(self, _parent) -> None: self.buttons: dict[str, object] = {} def setWindowTitle(self, _text: str) -> None: return def setIcon(self, _icon: object) -> None: return def setText(self, _text: str) -> None: return def setInformativeText(self, _text: str) -> None: return def addButton(self, label: str, _role: object) -> object: button = object() self.buttons[label] = button return button def exec(self) -> None: return def clickedButton(self) -> object | None: return self.buttons.get(type(self).selected_label) class DesktopGuiTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.application = QApplication.instance() or QApplication([]) def setUp(self) -> None: self.bridge = ApprovalBridge() self.window = MainWindow(_Controller(), self.bridge) self.window.show() self.application.processEvents() def tearDown(self) -> None: self.window.close() self.application.processEvents() def test_offscreen_window_loads_session_and_core_widgets(self) -> None: self.assertEqual(self.window.windowTitle(), "Javis Desktop") self.assertEqual(self.window.session_list.count(), 1) self.assertIn("Hallo", self.window.chat_view.toPlainText()) self.assertTrue(self.window.send_button.isEnabled()) self.assertFalse(self.window.stop_button.isEnabled()) self.assertEqual(self.window.status_labels["provider"].text(), "ollama") def test_approval_dialog_maps_all_three_choices(self) -> None: decision = PrivacyDecision(CloudPolicy.ASK, "persönliche Information") self.bridge.resolve = Mock() expected = { "Einmal erlauben": ApprovalChoice.ALLOW, "Lokal beantworten": ApprovalChoice.LOCAL, "Abbrechen": ApprovalChoice.CANCEL, } with patch("javis.ui.main_window.QMessageBox", _MessageBox): for label, choice in expected.items(): with self.subTest(label=label): _MessageBox.selected_label = label self.window._show_approval(decision) self.bridge.resolve.assert_called_with(choice)