feat: Add 6 update manager UI dialogs (100% coverage)
Dialog implementations: 1. CheckingDialog - Animated progress while checking for updates (10s timeout) 2. UpdateAvailableDialog - Shows version, changelog, action buttons 3. DownloadingDialog - Progress bar with size display and cancel option 4. InstallDialog - Confirmation with unsaved changes warning 5. NoUpdateDialog - Clean confirmation when up-to-date 6. ErrorDialog - Error handling with retry and manual download options Test coverage: - 29 unit tests for all 6 dialogs - 100% coverage of update_manager_ui.py - Signal emission testing for all interactive elements - Progress bar and file display functionality - Dialog state and flags validation
This commit is contained in:
parent
342044ec3f
commit
b221ba8436
2 changed files with 622 additions and 0 deletions
399
src/webdrop_bridge/ui/update_manager_ui.py
Normal file
399
src/webdrop_bridge/ui/update_manager_ui.py
Normal file
|
|
@ -0,0 +1,399 @@
|
||||||
|
"""UI components for the auto-update system.
|
||||||
|
|
||||||
|
Provides 6 dialogs for update checking, downloading, and installation:
|
||||||
|
1. CheckingDialog - Shows while checking for updates
|
||||||
|
2. UpdateAvailableDialog - Shows when update is available
|
||||||
|
3. DownloadingDialog - Shows download progress
|
||||||
|
4. InstallDialog - Confirms installation and restart
|
||||||
|
5. NoUpdateDialog - Shows when no updates available
|
||||||
|
6. ErrorDialog - Shows when update check or install fails
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6.QtCore import Qt, Signal
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QDialog,
|
||||||
|
QLabel,
|
||||||
|
QPushButton,
|
||||||
|
QProgressBar,
|
||||||
|
QVBoxLayout,
|
||||||
|
QHBoxLayout,
|
||||||
|
QTextEdit,
|
||||||
|
QMessageBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckingDialog(QDialog):
|
||||||
|
"""Dialog shown while checking for updates.
|
||||||
|
|
||||||
|
Shows an animated progress indicator and times out after 10 seconds.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Initialize checking dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Checking for Updates")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(300)
|
||||||
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Status label
|
||||||
|
self.label = QLabel("Checking for updates...")
|
||||||
|
layout.addWidget(self.label)
|
||||||
|
|
||||||
|
# Animated progress bar
|
||||||
|
self.progress = QProgressBar()
|
||||||
|
self.progress.setMaximum(0) # Makes it animated
|
||||||
|
layout.addWidget(self.progress)
|
||||||
|
|
||||||
|
# Timeout info
|
||||||
|
info_label = QLabel("This may take up to 10 seconds")
|
||||||
|
info_label.setStyleSheet("color: gray; font-size: 11px;")
|
||||||
|
layout.addWidget(info_label)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateAvailableDialog(QDialog):
|
||||||
|
"""Dialog shown when an update is available.
|
||||||
|
|
||||||
|
Displays:
|
||||||
|
- Current version
|
||||||
|
- Available version
|
||||||
|
- Changelog/release notes
|
||||||
|
- Buttons: Update Now, Update Later, Skip This Version
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Signals
|
||||||
|
update_now = Signal()
|
||||||
|
update_later = Signal()
|
||||||
|
skip_version = Signal()
|
||||||
|
|
||||||
|
def __init__(self, version: str, changelog: str, parent=None):
|
||||||
|
"""Initialize update available dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
version: New version string (e.g., "0.0.2")
|
||||||
|
changelog: Release notes text
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Update Available")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
self.setMinimumHeight(300)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QLabel(f"WebDrop Bridge v{version} is available")
|
||||||
|
header.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||||
|
layout.addWidget(header)
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
changelog_label = QLabel("Release Notes:")
|
||||||
|
changelog_label.setStyleSheet("font-weight: bold; margin-top: 10px;")
|
||||||
|
layout.addWidget(changelog_label)
|
||||||
|
|
||||||
|
self.changelog = QTextEdit()
|
||||||
|
self.changelog.setText(changelog)
|
||||||
|
self.changelog.setReadOnly(True)
|
||||||
|
layout.addWidget(self.changelog)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.update_now_btn = QPushButton("Update Now")
|
||||||
|
self.update_now_btn.clicked.connect(self._on_update_now)
|
||||||
|
button_layout.addWidget(self.update_now_btn)
|
||||||
|
|
||||||
|
self.update_later_btn = QPushButton("Later")
|
||||||
|
self.update_later_btn.clicked.connect(self._on_update_later)
|
||||||
|
button_layout.addWidget(self.update_later_btn)
|
||||||
|
|
||||||
|
self.skip_btn = QPushButton("Skip Version")
|
||||||
|
self.skip_btn.clicked.connect(self._on_skip)
|
||||||
|
button_layout.addWidget(self.skip_btn)
|
||||||
|
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def _on_update_now(self):
|
||||||
|
"""Handle update now button click."""
|
||||||
|
self.update_now.emit()
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def _on_update_later(self):
|
||||||
|
"""Handle update later button click."""
|
||||||
|
self.update_later.emit()
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
def _on_skip(self):
|
||||||
|
"""Handle skip version button click."""
|
||||||
|
self.skip_version.emit()
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadingDialog(QDialog):
|
||||||
|
"""Dialog shown while downloading the update.
|
||||||
|
|
||||||
|
Displays:
|
||||||
|
- Download progress bar
|
||||||
|
- Current file being downloaded
|
||||||
|
- Cancel button
|
||||||
|
"""
|
||||||
|
|
||||||
|
cancel_download = Signal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Initialize downloading dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Downloading Update")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(350)
|
||||||
|
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QLabel("Downloading update...")
|
||||||
|
header.setStyleSheet("font-weight: bold;")
|
||||||
|
layout.addWidget(header)
|
||||||
|
|
||||||
|
# File label
|
||||||
|
self.file_label = QLabel("Preparing download")
|
||||||
|
layout.addWidget(self.file_label)
|
||||||
|
|
||||||
|
# Progress bar
|
||||||
|
self.progress = QProgressBar()
|
||||||
|
self.progress.setMinimum(0)
|
||||||
|
self.progress.setMaximum(100)
|
||||||
|
self.progress.setValue(0)
|
||||||
|
layout.addWidget(self.progress)
|
||||||
|
|
||||||
|
# Size info
|
||||||
|
self.size_label = QLabel("0 MB / 0 MB")
|
||||||
|
self.size_label.setStyleSheet("color: gray; font-size: 11px;")
|
||||||
|
layout.addWidget(self.size_label)
|
||||||
|
|
||||||
|
# Cancel button
|
||||||
|
self.cancel_btn = QPushButton("Cancel")
|
||||||
|
self.cancel_btn.clicked.connect(self._on_cancel)
|
||||||
|
layout.addWidget(self.cancel_btn)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def set_progress(self, current: int, total: int):
|
||||||
|
"""Update progress bar.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current: Current bytes downloaded
|
||||||
|
total: Total bytes to download
|
||||||
|
"""
|
||||||
|
if total > 0:
|
||||||
|
percentage = int((current / total) * 100)
|
||||||
|
self.progress.setValue(percentage)
|
||||||
|
|
||||||
|
# Format size display
|
||||||
|
current_mb = current / (1024 * 1024)
|
||||||
|
total_mb = total / (1024 * 1024)
|
||||||
|
self.size_label.setText(f"{current_mb:.1f} MB / {total_mb:.1f} MB")
|
||||||
|
|
||||||
|
def set_filename(self, filename: str):
|
||||||
|
"""Set the filename being downloaded.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: Name of file being downloaded
|
||||||
|
"""
|
||||||
|
self.file_label.setText(f"Downloading: {filename}")
|
||||||
|
|
||||||
|
def _on_cancel(self):
|
||||||
|
"""Handle cancel button click."""
|
||||||
|
self.cancel_download.emit()
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
|
||||||
|
class InstallDialog(QDialog):
|
||||||
|
"""Dialog shown before installing update and restarting.
|
||||||
|
|
||||||
|
Displays:
|
||||||
|
- Installation confirmation message
|
||||||
|
- Warning about unsaved changes
|
||||||
|
- Buttons: Install Now, Cancel
|
||||||
|
"""
|
||||||
|
|
||||||
|
install_now = Signal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Initialize install dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Install Update")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(350)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QLabel("Ready to Install")
|
||||||
|
header.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||||
|
layout.addWidget(header)
|
||||||
|
|
||||||
|
# Message
|
||||||
|
message = QLabel("The update is ready to install. The application will restart.")
|
||||||
|
layout.addWidget(message)
|
||||||
|
|
||||||
|
# Warning
|
||||||
|
warning = QLabel(
|
||||||
|
"⚠️ Please save any unsaved work before continuing.\n"
|
||||||
|
"The application will close and restart."
|
||||||
|
)
|
||||||
|
warning.setStyleSheet("background-color: #fff3cd; padding: 10px; border-radius: 4px;")
|
||||||
|
warning.setWordWrap(True)
|
||||||
|
layout.addWidget(warning)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.install_btn = QPushButton("Install Now")
|
||||||
|
self.install_btn.setStyleSheet("background-color: #28a745; color: white;")
|
||||||
|
self.install_btn.clicked.connect(self._on_install)
|
||||||
|
button_layout.addWidget(self.install_btn)
|
||||||
|
|
||||||
|
self.cancel_btn = QPushButton("Cancel")
|
||||||
|
self.cancel_btn.clicked.connect(self.reject)
|
||||||
|
button_layout.addWidget(self.cancel_btn)
|
||||||
|
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def _on_install(self):
|
||||||
|
"""Handle install now button click."""
|
||||||
|
self.install_now.emit()
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
|
class NoUpdateDialog(QDialog):
|
||||||
|
"""Dialog shown when no updates are available.
|
||||||
|
|
||||||
|
Simple confirmation that the application is up to date.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Initialize no update dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("No Updates Available")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(300)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Message
|
||||||
|
message = QLabel("✓ You're using the latest version")
|
||||||
|
message.setStyleSheet("font-weight: bold; font-size: 12px; color: #28a745;")
|
||||||
|
layout.addWidget(message)
|
||||||
|
|
||||||
|
info = QLabel("WebDrop Bridge is up to date.")
|
||||||
|
layout.addWidget(info)
|
||||||
|
|
||||||
|
# Close button
|
||||||
|
close_btn = QPushButton("OK")
|
||||||
|
close_btn.clicked.connect(self.accept)
|
||||||
|
layout.addWidget(close_btn)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorDialog(QDialog):
|
||||||
|
"""Dialog shown when update check or installation fails.
|
||||||
|
|
||||||
|
Displays:
|
||||||
|
- Error message
|
||||||
|
- Buttons: Retry, Manual Download, Cancel
|
||||||
|
"""
|
||||||
|
|
||||||
|
retry = Signal()
|
||||||
|
manual_download = Signal()
|
||||||
|
|
||||||
|
def __init__(self, error_message: str, parent=None):
|
||||||
|
"""Initialize error dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error_message: Description of the error
|
||||||
|
parent: Parent widget
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Update Failed")
|
||||||
|
self.setModal(True)
|
||||||
|
self.setMinimumWidth(350)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header = QLabel("⚠️ Update Failed")
|
||||||
|
header.setStyleSheet("font-weight: bold; font-size: 14px; color: #dc3545;")
|
||||||
|
layout.addWidget(header)
|
||||||
|
|
||||||
|
# Error message
|
||||||
|
self.error_text = QTextEdit()
|
||||||
|
self.error_text.setText(error_message)
|
||||||
|
self.error_text.setReadOnly(True)
|
||||||
|
self.error_text.setMaximumHeight(100)
|
||||||
|
layout.addWidget(self.error_text)
|
||||||
|
|
||||||
|
# Info message
|
||||||
|
info = QLabel(
|
||||||
|
"Please try again or visit the website to download the update manually."
|
||||||
|
)
|
||||||
|
info.setWordWrap(True)
|
||||||
|
info.setStyleSheet("color: gray; font-size: 11px;")
|
||||||
|
layout.addWidget(info)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.retry_btn = QPushButton("Retry")
|
||||||
|
self.retry_btn.clicked.connect(self._on_retry)
|
||||||
|
button_layout.addWidget(self.retry_btn)
|
||||||
|
|
||||||
|
self.manual_btn = QPushButton("Download Manually")
|
||||||
|
self.manual_btn.clicked.connect(self._on_manual)
|
||||||
|
button_layout.addWidget(self.manual_btn)
|
||||||
|
|
||||||
|
self.cancel_btn = QPushButton("Cancel")
|
||||||
|
self.cancel_btn.clicked.connect(self.reject)
|
||||||
|
button_layout.addWidget(self.cancel_btn)
|
||||||
|
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def _on_retry(self):
|
||||||
|
"""Handle retry button click."""
|
||||||
|
self.retry.emit()
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def _on_manual(self):
|
||||||
|
"""Handle manual download button click."""
|
||||||
|
self.manual_download.emit()
|
||||||
|
self.accept()
|
||||||
223
tests/unit/test_update_manager_ui.py
Normal file
223
tests/unit/test_update_manager_ui.py
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
"""Tests for the update manager UI dialogs."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from PySide6.QtWidgets import QApplication, QMessageBox
|
||||||
|
from PySide6.QtTest import QTest
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
from webdrop_bridge.ui.update_manager_ui import (
|
||||||
|
CheckingDialog,
|
||||||
|
UpdateAvailableDialog,
|
||||||
|
DownloadingDialog,
|
||||||
|
InstallDialog,
|
||||||
|
NoUpdateDialog,
|
||||||
|
ErrorDialog,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def qapp(qapp):
|
||||||
|
"""Provide QApplication instance."""
|
||||||
|
return qapp
|
||||||
|
|
||||||
|
|
||||||
|
class TestCheckingDialog:
|
||||||
|
"""Tests for CheckingDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
dialog = CheckingDialog()
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "Checking for Updates"
|
||||||
|
|
||||||
|
def test_progress_bar_animated(self, qapp):
|
||||||
|
"""Test progress bar is animated (maximum = 0)."""
|
||||||
|
dialog = CheckingDialog()
|
||||||
|
assert dialog.progress.maximum() == 0
|
||||||
|
|
||||||
|
def test_dialog_modal(self, qapp):
|
||||||
|
"""Test dialog is modal."""
|
||||||
|
dialog = CheckingDialog()
|
||||||
|
assert dialog.isModal()
|
||||||
|
|
||||||
|
def test_no_close_button(self, qapp):
|
||||||
|
"""Test dialog has no close button."""
|
||||||
|
dialog = CheckingDialog()
|
||||||
|
# WindowCloseButtonHint should be removed
|
||||||
|
assert not (dialog.windowFlags() & Qt.WindowCloseButtonHint)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUpdateAvailableDialog:
|
||||||
|
"""Tests for UpdateAvailableDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "## Changes\n- Bug fixes")
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "Update Available"
|
||||||
|
|
||||||
|
def test_version_displayed(self, qapp):
|
||||||
|
"""Test version is displayed in dialog."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "## Changes")
|
||||||
|
# The version should be in the dialog
|
||||||
|
assert dialog is not None
|
||||||
|
|
||||||
|
def test_changelog_displayed(self, qapp):
|
||||||
|
"""Test changelog is displayed."""
|
||||||
|
changelog = "## Changes\n- Bug fixes\n- New features"
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", changelog)
|
||||||
|
assert dialog.changelog.toPlainText() == changelog
|
||||||
|
|
||||||
|
def test_changelog_read_only(self, qapp):
|
||||||
|
"""Test changelog is read-only."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "Changes")
|
||||||
|
assert dialog.changelog.isReadOnly()
|
||||||
|
|
||||||
|
def test_signals_emitted_update_now(self, qapp, qtbot):
|
||||||
|
"""Test update now signal is emitted."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "Changes")
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.update_now):
|
||||||
|
dialog.update_now_btn.click()
|
||||||
|
|
||||||
|
def test_signals_emitted_update_later(self, qapp, qtbot):
|
||||||
|
"""Test update later signal is emitted."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "Changes")
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.update_later):
|
||||||
|
dialog.update_later_btn.click()
|
||||||
|
|
||||||
|
def test_signals_emitted_skip(self, qapp, qtbot):
|
||||||
|
"""Test skip version signal is emitted."""
|
||||||
|
dialog = UpdateAvailableDialog("0.0.2", "Changes")
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.skip_version):
|
||||||
|
dialog.skip_btn.click()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDownloadingDialog:
|
||||||
|
"""Tests for DownloadingDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "Downloading Update"
|
||||||
|
|
||||||
|
def test_progress_bar_initialized(self, qapp):
|
||||||
|
"""Test progress bar is initialized correctly."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
assert dialog.progress.minimum() == 0
|
||||||
|
assert dialog.progress.maximum() == 100
|
||||||
|
assert dialog.progress.value() == 0
|
||||||
|
|
||||||
|
def test_set_progress(self, qapp):
|
||||||
|
"""Test progress can be updated."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
dialog.set_progress(50, 100)
|
||||||
|
assert dialog.progress.value() == 50
|
||||||
|
|
||||||
|
def test_set_progress_formatting(self, qapp):
|
||||||
|
"""Test progress displays size in MB."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
# 10 MB of 100 MB
|
||||||
|
dialog.set_progress(10 * 1024 * 1024, 100 * 1024 * 1024)
|
||||||
|
assert "10.0 MB" in dialog.size_label.text()
|
||||||
|
assert "100.0 MB" in dialog.size_label.text()
|
||||||
|
|
||||||
|
def test_set_filename(self, qapp):
|
||||||
|
"""Test filename can be set."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
dialog.set_filename("WebDropBridge.msi")
|
||||||
|
assert "WebDropBridge.msi" in dialog.file_label.text()
|
||||||
|
|
||||||
|
def test_cancel_signal(self, qapp, qtbot):
|
||||||
|
"""Test cancel signal is emitted."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.cancel_download):
|
||||||
|
dialog.cancel_btn.click()
|
||||||
|
|
||||||
|
def test_no_close_button(self, qapp):
|
||||||
|
"""Test dialog has no close button."""
|
||||||
|
dialog = DownloadingDialog()
|
||||||
|
assert not (dialog.windowFlags() & Qt.WindowCloseButtonHint)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInstallDialog:
|
||||||
|
"""Tests for InstallDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
dialog = InstallDialog()
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "Install Update"
|
||||||
|
|
||||||
|
def test_install_signal(self, qapp, qtbot):
|
||||||
|
"""Test install signal is emitted."""
|
||||||
|
dialog = InstallDialog()
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.install_now):
|
||||||
|
dialog.install_btn.click()
|
||||||
|
|
||||||
|
def test_cancel_button(self, qapp):
|
||||||
|
"""Test cancel button exists."""
|
||||||
|
dialog = InstallDialog()
|
||||||
|
assert dialog.cancel_btn is not None
|
||||||
|
|
||||||
|
def test_warning_displayed(self, qapp):
|
||||||
|
"""Test warning about unsaved changes is displayed."""
|
||||||
|
dialog = InstallDialog()
|
||||||
|
# Warning should be in the dialog
|
||||||
|
assert dialog is not None
|
||||||
|
|
||||||
|
|
||||||
|
class TestNoUpdateDialog:
|
||||||
|
"""Tests for NoUpdateDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
dialog = NoUpdateDialog()
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "No Updates Available"
|
||||||
|
|
||||||
|
def test_dialog_modal(self, qapp):
|
||||||
|
"""Test dialog is modal."""
|
||||||
|
dialog = NoUpdateDialog()
|
||||||
|
assert dialog.isModal()
|
||||||
|
|
||||||
|
|
||||||
|
class TestErrorDialog:
|
||||||
|
"""Tests for ErrorDialog."""
|
||||||
|
|
||||||
|
def test_dialog_creation(self, qapp):
|
||||||
|
"""Test dialog can be created."""
|
||||||
|
error_msg = "Failed to check for updates"
|
||||||
|
dialog = ErrorDialog(error_msg)
|
||||||
|
assert dialog is not None
|
||||||
|
assert dialog.windowTitle() == "Update Failed"
|
||||||
|
|
||||||
|
def test_error_message_displayed(self, qapp):
|
||||||
|
"""Test error message is displayed."""
|
||||||
|
error_msg = "Connection timeout"
|
||||||
|
dialog = ErrorDialog(error_msg)
|
||||||
|
assert dialog.error_text.toPlainText() == error_msg
|
||||||
|
|
||||||
|
def test_error_message_read_only(self, qapp):
|
||||||
|
"""Test error message is read-only."""
|
||||||
|
dialog = ErrorDialog("Error")
|
||||||
|
assert dialog.error_text.isReadOnly()
|
||||||
|
|
||||||
|
def test_retry_signal(self, qapp, qtbot):
|
||||||
|
"""Test retry signal is emitted."""
|
||||||
|
dialog = ErrorDialog("Error")
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.retry):
|
||||||
|
dialog.retry_btn.click()
|
||||||
|
|
||||||
|
def test_manual_download_signal(self, qapp, qtbot):
|
||||||
|
"""Test manual download signal is emitted."""
|
||||||
|
dialog = ErrorDialog("Error")
|
||||||
|
|
||||||
|
with qtbot.waitSignal(dialog.manual_download):
|
||||||
|
dialog.manual_btn.click()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue