2
0

test_main_window.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. """
  2. Tests for MainWindow UI component.
  3. """
  4. import pytest
  5. # Skip tests if PyQt6 is not installed
  6. pytest.importorskip("PyQt6")
  7. from pathlib import Path
  8. from datetime import datetime
  9. from PyQt6.QtWidgets import QApplication
  10. from PyQt6.QtCore import Qt
  11. from PyQt6.QtTest import QTest
  12. from src.ui.models import FileItem, FileStatus
  13. from src.ui.main_window import MainWindow
  14. @pytest.fixture
  15. def app(qtbot):
  16. """Create QApplication fixture."""
  17. test_app = QApplication.instance()
  18. if test_app is None:
  19. test_app = QApplication([])
  20. yield test_app
  21. @pytest.fixture
  22. def main_window(app, qtbot):
  23. """Create MainWindow fixture."""
  24. window = MainWindow()
  25. qtbot.addWidget(window)
  26. yield window
  27. window.close()
  28. class TestMainWindow:
  29. """Test MainWindow functionality."""
  30. def test_initialization(self, main_window):
  31. """Test main window initializes correctly."""
  32. assert main_window.windowTitle() == "BMAD Novel Translator"
  33. assert main_window.width() == main_window.DEFAULT_WIDTH
  34. assert main_window.height() == main_window.DEFAULT_HEIGHT
  35. assert main_window.minimumWidth() == main_window.MINIMUM_WIDTH
  36. assert main_window.minimumHeight() == main_window.MINIMUM_HEIGHT
  37. def test_menu_bar_exists(self, main_window):
  38. """Test menu bar is created."""
  39. menubar = main_window.menuBar()
  40. assert menubar is not None
  41. # Check menus exist
  42. menus = {action.text(): action for action in menubar.actions()}
  43. assert "&File" in menus or "File" in menus
  44. assert "&Edit" in menus or "Edit" in menus
  45. assert "&View" in menus or "View" in menus
  46. assert "&Tools" in menus or "Tools" in menus
  47. assert "&Help" in menus or "Help" in menus
  48. def test_toolbar_exists(self, main_window):
  49. """Test toolbar is created."""
  50. toolbars = main_window.findChildren(QMainWindow)
  51. assert main_window.findChild(type(None)) is not None # Basic check
  52. # Toolbar actions are checked through UI inspection
  53. def test_status_bar_exists(self, main_window):
  54. """Test status bar is created."""
  55. status_bar = main_window.statusBar()
  56. assert status_bar is not None
  57. assert "v" in status_bar.currentMessage() or status_bar.children()
  58. def test_add_file_item(self, main_window, qtbot):
  59. """Test adding a file item."""
  60. item = FileItem(
  61. path=Path("/test/file.txt"),
  62. name="file.txt",
  63. size=1024,
  64. status=FileStatus.PENDING
  65. )
  66. main_window.add_file_item(item)
  67. assert len(main_window.file_items) == 1
  68. assert main_window.file_items[0].name == "file.txt"
  69. def test_add_multiple_file_items(self, main_window):
  70. """Test adding multiple file items."""
  71. items = [
  72. FileItem(
  73. path=Path(f"/test/file{i}.txt"),
  74. name=f"file{i}.txt",
  75. size=1024 * (i + 1),
  76. status=FileStatus.PENDING
  77. )
  78. for i in range(3)
  79. ]
  80. for item in items:
  81. main_window.add_file_item(item)
  82. assert len(main_window.file_items) == 3
  83. def test_remove_file_item(self, main_window):
  84. """Test removing a file item."""
  85. item = FileItem(
  86. path=Path("/test/file.txt"),
  87. name="file.txt",
  88. size=1024,
  89. status=FileStatus.PENDING
  90. )
  91. main_window.add_file_item(item)
  92. assert len(main_window.file_items) == 1
  93. main_window.remove_file_item(item)
  94. assert len(main_window.file_items) == 0
  95. def test_clear_all_files(self, main_window):
  96. """Test clearing all files."""
  97. items = [
  98. FileItem(
  99. path=Path(f"/test/file{i}.txt"),
  100. name=f"file{i}.txt",
  101. size=1024,
  102. status=FileStatus.PENDING
  103. )
  104. for i in range(3)
  105. ]
  106. for item in items:
  107. main_window.add_file_item(item)
  108. assert len(main_window.file_items) == 3
  109. main_window.clear_all_files()
  110. assert len(main_window.file_items) == 0
  111. def test_progress_update(self, main_window):
  112. """Test progress bar update."""
  113. main_window.set_progress(50)
  114. # Progress bar value should be 50
  115. # (Can't directly access widget value without exposing)
  116. def test_status_message(self, main_window):
  117. """Test status bar message update."""
  118. main_window.set_status_message("Test message")
  119. # Status bar should show the message
  120. def test_task_status_update(self, main_window):
  121. """Test task status label update."""
  122. main_window.set_task_status("Translating...")
  123. # Task status label should be updated
  124. def test_control_buttons_disabled_when_empty(self, main_window):
  125. """Test control buttons are disabled when no files."""
  126. assert not main_window._start_btn.isEnabled()
  127. assert not main_window._start_action.isEnabled()
  128. def test_control_buttons_enabled_with_files(self, main_window):
  129. """Test control buttons are enabled when files are added."""
  130. item = FileItem(
  131. path=Path("/test/file.txt"),
  132. name="file.txt",
  133. size=1024,
  134. status=FileStatus.READY
  135. )
  136. main_window.add_file_item(item)
  137. assert main_window._start_btn.isEnabled()
  138. assert main_window._start_action.isEnabled()
  139. def test_signals_emitted(self, main_window):
  140. """Test that signals are emitted correctly."""
  141. signal_received = []
  142. main_window.translation_started.connect(lambda: signal_received.append("started"))
  143. main_window.translation_paused.connect(lambda: signal_received.append("paused"))
  144. main_window.translation_cancelled.connect(lambda: signal_received.append("cancelled"))
  145. main_window.settings_requested.connect(lambda: signal_received.append("settings"))
  146. main_window._on_start_translation()
  147. main_window._on_pause_translation()
  148. main_window._on_cancel_translation()
  149. main_window._on_settings()
  150. assert signal_received == ["started", "paused", "cancelled", "settings"]
  151. def test_file_selector_integration(self, main_window):
  152. """Test FileSelector component is integrated."""
  153. file_selector = main_window.get_file_selector()
  154. if file_selector is not None:
  155. # FileSelector should be available
  156. assert hasattr(file_selector, 'file_selected')
  157. assert hasattr(file_selector, 'selection_cleared')
  158. else:
  159. # PyQt6 might not be available in test environment
  160. pass
  161. def test_progress_widget_integration(self, main_window):
  162. """Test ProgressWidget component is integrated."""
  163. progress_widget = main_window.get_progress_widget()
  164. if progress_widget is not None:
  165. # ProgressWidget should be available
  166. assert hasattr(progress_widget, 'connect_to_notifier')
  167. assert hasattr(progress_widget, 'reset')
  168. else:
  169. # PyQt6 might not be available in test environment
  170. pass
  171. def test_splitter_ratio(self, main_window):
  172. """Test splitter has correct initial ratio (30/70)."""
  173. # Check that splitter exists
  174. central_widget = main_window.centralWidget()
  175. assert central_widget is not None
  176. # Find splitter
  177. splitter = central_widget.findChild(type(central_widget).__class__)
  178. # MainWindow should have a QSplitter
  179. from PyQt6.QtWidgets import QSplitter
  180. splitters = central_widget.findChildren(QSplitter)
  181. assert len(splitters) > 0
  182. def test_left_right_panels(self, main_window):
  183. """Test left and right panels exist."""
  184. # The MainWindow should have file list on left, details on right
  185. assert hasattr(main_window, '_file_table')
  186. assert main_window._file_table is not None
  187. class TestFileItem:
  188. """Test FileItem model."""
  189. def test_file_item_creation(self):
  190. """Test creating a file item."""
  191. item = FileItem(
  192. path=Path("/test/file.txt"),
  193. name="file.txt",
  194. size=2048,
  195. status=FileStatus.PENDING
  196. )
  197. assert item.name == "file.txt"
  198. assert item.size == 2048
  199. assert item.status == FileStatus.PENDING
  200. assert item.chapters == 0
  201. assert item.total_words == 0
  202. def test_progress_calculation(self):
  203. """Test progress calculation."""
  204. item = FileItem(
  205. path=Path("/test/file.txt"),
  206. name="file.txt",
  207. size=1024,
  208. total_words=1000,
  209. translated_words=500
  210. )
  211. assert item.progress == 50.0
  212. def test_progress_zero_when_no_words(self):
  213. """Test progress is zero when no words set."""
  214. item = FileItem(
  215. path=Path("/test/file.txt"),
  216. name="file.txt",
  217. size=1024
  218. )
  219. assert item.progress == 0.0
  220. def test_size_formatting(self):
  221. """Test file size formatting."""
  222. # Bytes
  223. item1 = FileItem(path=Path("/test/small.txt"), name="small.txt", size=512)
  224. assert "B" in item1.size_formatted
  225. # KB
  226. item2 = FileItem(path=Path("/test/medium.txt"), name="medium.txt", size=2048)
  227. assert "KB" in item2.size_formatted
  228. # MB
  229. item3 = FileItem(path=Path("/test/large.txt"), name="large.txt", size=1024 * 1024 * 5)
  230. assert "MB" in item3.size_formatted