""" Tests for MainWindow UI component. """ import pytest # Skip tests if PyQt6 is not installed pytest.importorskip("PyQt6") from pathlib import Path from datetime import datetime from PyQt6.QtWidgets import QApplication from PyQt6.QtCore import Qt from PyQt6.QtTest import QTest from src.ui.models import FileItem, FileStatus from src.ui.main_window import MainWindow @pytest.fixture def app(qtbot): """Create QApplication fixture.""" test_app = QApplication.instance() if test_app is None: test_app = QApplication([]) yield test_app @pytest.fixture def main_window(app, qtbot): """Create MainWindow fixture.""" window = MainWindow() qtbot.addWidget(window) yield window window.close() class TestMainWindow: """Test MainWindow functionality.""" def test_initialization(self, main_window): """Test main window initializes correctly.""" assert main_window.windowTitle() == "BMAD Novel Translator" assert main_window.width() == main_window.DEFAULT_WIDTH assert main_window.height() == main_window.DEFAULT_HEIGHT assert main_window.minimumWidth() == main_window.MINIMUM_WIDTH assert main_window.minimumHeight() == main_window.MINIMUM_HEIGHT def test_menu_bar_exists(self, main_window): """Test menu bar is created.""" menubar = main_window.menuBar() assert menubar is not None # Check menus exist menus = {action.text(): action for action in menubar.actions()} assert "&File" in menus or "File" in menus assert "&Edit" in menus or "Edit" in menus assert "&View" in menus or "View" in menus assert "&Tools" in menus or "Tools" in menus assert "&Help" in menus or "Help" in menus def test_toolbar_exists(self, main_window): """Test toolbar is created.""" toolbars = main_window.findChildren(QMainWindow) assert main_window.findChild(type(None)) is not None # Basic check # Toolbar actions are checked through UI inspection def test_status_bar_exists(self, main_window): """Test status bar is created.""" status_bar = main_window.statusBar() assert status_bar is not None assert "v" in status_bar.currentMessage() or status_bar.children() def test_add_file_item(self, main_window, qtbot): """Test adding a file item.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=1024, status=FileStatus.PENDING ) main_window.add_file_item(item) assert len(main_window.file_items) == 1 assert main_window.file_items[0].name == "file.txt" def test_add_multiple_file_items(self, main_window): """Test adding multiple file items.""" items = [ FileItem( path=Path(f"/test/file{i}.txt"), name=f"file{i}.txt", size=1024 * (i + 1), status=FileStatus.PENDING ) for i in range(3) ] for item in items: main_window.add_file_item(item) assert len(main_window.file_items) == 3 def test_remove_file_item(self, main_window): """Test removing a file item.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=1024, status=FileStatus.PENDING ) main_window.add_file_item(item) assert len(main_window.file_items) == 1 main_window.remove_file_item(item) assert len(main_window.file_items) == 0 def test_clear_all_files(self, main_window): """Test clearing all files.""" items = [ FileItem( path=Path(f"/test/file{i}.txt"), name=f"file{i}.txt", size=1024, status=FileStatus.PENDING ) for i in range(3) ] for item in items: main_window.add_file_item(item) assert len(main_window.file_items) == 3 main_window.clear_all_files() assert len(main_window.file_items) == 0 def test_progress_update(self, main_window): """Test progress bar update.""" main_window.set_progress(50) # Progress bar value should be 50 # (Can't directly access widget value without exposing) def test_status_message(self, main_window): """Test status bar message update.""" main_window.set_status_message("Test message") # Status bar should show the message def test_task_status_update(self, main_window): """Test task status label update.""" main_window.set_task_status("Translating...") # Task status label should be updated def test_control_buttons_disabled_when_empty(self, main_window): """Test control buttons are disabled when no files.""" assert not main_window._start_btn.isEnabled() assert not main_window._start_action.isEnabled() def test_control_buttons_enabled_with_files(self, main_window): """Test control buttons are enabled when files are added.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=1024, status=FileStatus.READY ) main_window.add_file_item(item) assert main_window._start_btn.isEnabled() assert main_window._start_action.isEnabled() def test_signals_emitted(self, main_window): """Test that signals are emitted correctly.""" signal_received = [] main_window.translation_started.connect(lambda: signal_received.append("started")) main_window.translation_paused.connect(lambda: signal_received.append("paused")) main_window.translation_cancelled.connect(lambda: signal_received.append("cancelled")) main_window.settings_requested.connect(lambda: signal_received.append("settings")) main_window._on_start_translation() main_window._on_pause_translation() main_window._on_cancel_translation() main_window._on_settings() assert signal_received == ["started", "paused", "cancelled", "settings"] def test_file_selector_integration(self, main_window): """Test FileSelector component is integrated.""" file_selector = main_window.get_file_selector() if file_selector is not None: # FileSelector should be available assert hasattr(file_selector, 'file_selected') assert hasattr(file_selector, 'selection_cleared') else: # PyQt6 might not be available in test environment pass def test_progress_widget_integration(self, main_window): """Test ProgressWidget component is integrated.""" progress_widget = main_window.get_progress_widget() if progress_widget is not None: # ProgressWidget should be available assert hasattr(progress_widget, 'connect_to_notifier') assert hasattr(progress_widget, 'reset') else: # PyQt6 might not be available in test environment pass def test_splitter_ratio(self, main_window): """Test splitter has correct initial ratio (30/70).""" # Check that splitter exists central_widget = main_window.centralWidget() assert central_widget is not None # Find splitter splitter = central_widget.findChild(type(central_widget).__class__) # MainWindow should have a QSplitter from PyQt6.QtWidgets import QSplitter splitters = central_widget.findChildren(QSplitter) assert len(splitters) > 0 def test_left_right_panels(self, main_window): """Test left and right panels exist.""" # The MainWindow should have file list on left, details on right assert hasattr(main_window, '_file_table') assert main_window._file_table is not None class TestFileItem: """Test FileItem model.""" def test_file_item_creation(self): """Test creating a file item.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=2048, status=FileStatus.PENDING ) assert item.name == "file.txt" assert item.size == 2048 assert item.status == FileStatus.PENDING assert item.chapters == 0 assert item.total_words == 0 def test_progress_calculation(self): """Test progress calculation.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=1024, total_words=1000, translated_words=500 ) assert item.progress == 50.0 def test_progress_zero_when_no_words(self): """Test progress is zero when no words set.""" item = FileItem( path=Path("/test/file.txt"), name="file.txt", size=1024 ) assert item.progress == 0.0 def test_size_formatting(self): """Test file size formatting.""" # Bytes item1 = FileItem(path=Path("/test/small.txt"), name="small.txt", size=512) assert "B" in item1.size_formatted # KB item2 = FileItem(path=Path("/test/medium.txt"), name="medium.txt", size=2048) assert "KB" in item2.size_formatted # MB item3 = FileItem(path=Path("/test/large.txt"), name="large.txt", size=1024 * 1024 * 5) assert "MB" in item3.size_formatted