2
0

test_shortcuts.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. """
  2. Tests for the Keyboard Shortcuts UI component (Story 7.13).
  3. """
  4. import pytest
  5. from pathlib import Path
  6. from PyQt6.QtWidgets import QApplication
  7. from PyQt6.QtGui import QKeySequence
  8. from src.ui.shortcuts import (
  9. StandardAction,
  10. DEFAULT_SHORTCUTS,
  11. ACTION_NAMES,
  12. ShortcutEntry,
  13. ShortcutsManager,
  14. ShortcutsModel,
  15. get_shortcuts_manager,
  16. )
  17. @pytest.fixture
  18. def app(qtbot):
  19. """Create QApplication for tests."""
  20. return QApplication.instance() or QApplication([])
  21. @pytest.fixture
  22. def clean_manager():
  23. """Create a fresh ShortcutsManager for testing."""
  24. import src.ui.shortcuts as sc
  25. sc._shortcuts_manager_instance = None
  26. manager = ShortcutsManager()
  27. return manager
  28. class TestStandardAction:
  29. """Tests for StandardAction enum."""
  30. def test_action_values(self):
  31. """Test StandardAction enum values."""
  32. assert StandardAction.OPEN_FILE.value == "open_file"
  33. assert StandardAction.SAVE_FILE.value == "save_file"
  34. assert StandardAction.START_TRANSLATION.value == "start_translation"
  35. class TestDefaultShortcuts:
  36. """Tests for default shortcuts."""
  37. def test_all_actions_have_defaults(self):
  38. """Test that all standard actions have default shortcuts."""
  39. for action in StandardAction:
  40. assert action in DEFAULT_SHORTCUTS
  41. assert DEFAULT_SHORTCUTS[action]
  42. def test_all_actions_have_names(self):
  43. """Test that all standard actions have display names."""
  44. for action in StandardAction:
  45. assert action in ACTION_NAMES
  46. assert ACTION_NAMES[action]
  47. def test_common_shortcuts(self):
  48. """Test common shortcut defaults."""
  49. assert DEFAULT_SHORTCUTS[StandardAction.OPEN_FILE] == "Ctrl+O"
  50. assert DEFAULT_SHORTCUTS[StandardAction.SAVE_FILE] == "Ctrl+S"
  51. assert DEFAULT_SHORTCUTS[StandardAction.START_TRANSLATION] == "F5"
  52. assert DEFAULT_SHORTCUTS[StandardAction.ABOUT] == "Ctrl+F1"
  53. class TestShortcutEntry:
  54. """Tests for ShortcutEntry dataclass."""
  55. def test_entry_creation(self):
  56. """Test creating a shortcut entry."""
  57. entry = ShortcutEntry(
  58. action=StandardAction.OPEN_FILE,
  59. key_sequence="Ctrl+O",
  60. default_key_sequence="Ctrl+O",
  61. name=ACTION_NAMES[StandardAction.OPEN_FILE],
  62. category="文件"
  63. )
  64. assert entry.action == StandardAction.OPEN_FILE
  65. assert entry.key_sequence == "Ctrl+O"
  66. assert entry.default_key_sequence == "Ctrl+O"
  67. def test_is_modified_false(self):
  68. """Test is_modified when not modified."""
  69. entry = ShortcutEntry(
  70. action=StandardAction.OPEN_FILE,
  71. key_sequence="Ctrl+O",
  72. default_key_sequence="Ctrl+O",
  73. name="Open File",
  74. category="File"
  75. )
  76. assert entry.is_modified is False
  77. def test_is_modified_true(self):
  78. """Test is_modified when modified."""
  79. entry = ShortcutEntry(
  80. action=StandardAction.OPEN_FILE,
  81. key_sequence="Ctrl+Shift+O",
  82. default_key_sequence="Ctrl+O",
  83. name="Open File",
  84. category="File"
  85. )
  86. assert entry.is_modified is True
  87. class TestShortcutsManager:
  88. """Tests for ShortcutsManager."""
  89. def test_initialization(self, clean_manager):
  90. """Test manager initialization."""
  91. assert isinstance(clean_manager, ShortcutsManager)
  92. def test_get_shortcut(self, clean_manager):
  93. """Test getting shortcut for an action."""
  94. shortcut = clean_manager.get_shortcut(StandardAction.OPEN_FILE)
  95. assert isinstance(shortcut, QKeySequence)
  96. assert shortcut.toString() == "Ctrl+O"
  97. def test_get_all_shortcuts(self, clean_manager):
  98. """Test all actions have shortcuts."""
  99. for action in StandardAction:
  100. shortcut = clean_manager.get_shortcut(action)
  101. assert isinstance(shortcut, QKeySequence)
  102. def test_set_shortcut(self, clean_manager):
  103. """Test setting a shortcut."""
  104. result = clean_manager.set_shortcut(StandardAction.OPEN_FILE, "Ctrl+Shift+O")
  105. assert result is True
  106. shortcut = clean_manager.get_shortcut(StandardAction.OPEN_FILE)
  107. assert shortcut.toString() == "Ctrl+Shift+O"
  108. def test_set_shortcut_conflict(self, clean_manager):
  109. """Test that conflicting shortcuts are rejected."""
  110. # Try to set OPEN_FILE to the same as SAVE_FILE
  111. result = clean_manager.set_shortcut(StandardAction.OPEN_FILE, "Ctrl+S")
  112. assert result is False
  113. def test_reset_action(self, clean_manager):
  114. """Test resetting a single action to default."""
  115. # First change it
  116. clean_manager.set_shortcut(StandardAction.OPEN_FILE, "Ctrl+Shift+O")
  117. # Reset to default
  118. clean_manager.reset_action(StandardAction.OPEN_FILE)
  119. shortcut = clean_manager.get_shortcut(StandardAction.OPEN_FILE)
  120. assert shortcut.toString() == DEFAULT_SHORTCUTS[StandardAction.OPEN_FILE]
  121. def test_reset_to_defaults(self, clean_manager):
  122. """Test resetting all shortcuts to defaults."""
  123. # Change some shortcuts
  124. clean_manager.set_shortcut(StandardAction.OPEN_FILE, "Ctrl+Shift+O")
  125. clean_manager.set_shortcut(StandardAction.SAVE_FILE, "Ctrl+Shift+S")
  126. # Reset all
  127. clean_manager.reset_to_defaults()
  128. assert clean_manager.get_shortcut(StandardAction.OPEN_FILE).toString() == "Ctrl+O"
  129. assert clean_manager.get_shortcut(StandardAction.SAVE_FILE).toString() == "Ctrl+S"
  130. def test_get_all_entries(self, clean_manager):
  131. """Test getting all shortcut entries."""
  132. entries = clean_manager.get_all_entries()
  133. assert len(entries) == len(StandardAction)
  134. for entry in entries:
  135. assert isinstance(entry, ShortcutEntry)
  136. assert isinstance(entry.action, StandardAction)
  137. assert entry.name
  138. assert entry.category
  139. assert entry.key_sequence
  140. assert entry.default_key_sequence
  141. def test_create_action(self, app, clean_manager):
  142. """Test creating a QAction with shortcut."""
  143. callback_called = []
  144. def callback():
  145. callback_called.append(True)
  146. action = clean_manager.create_action(
  147. StandardAction.OPEN_FILE,
  148. parent=None,
  149. callback=callback
  150. )
  151. assert action is not None
  152. assert action.text() == ACTION_NAMES[StandardAction.OPEN_FILE]
  153. assert action.shortcut() == clean_manager.get_shortcut(StandardAction.OPEN_FILE)
  154. def test_register_action(self, app, clean_manager):
  155. """Test registering an existing QAction."""
  156. from PyQt6.QtWidgets import QAction
  157. qaction = QAction("Test", None)
  158. clean_manager.register_action(StandardAction.OPEN_FILE, qaction)
  159. assert qaction.shortcut() == clean_manager.get_shortcut(StandardAction.OPEN_FILE)
  160. def test_register_then_change_shortcut(self, app, clean_manager):
  161. """Test that registered QAction updates when shortcut changes."""
  162. from PyQt6.QtWidgets import QAction
  163. qaction = QAction("Test", None)
  164. clean_manager.register_action(StandardAction.OPEN_FILE, qaction)
  165. # Change the shortcut
  166. clean_manager.set_shortcut(StandardAction.OPEN_FILE, "Ctrl+Shift+O")
  167. # QAction should be updated
  168. assert qaction.shortcut().toString() == "Ctrl+Shift+O"
  169. class TestShortcutsModel:
  170. """Tests for ShortcutsModel."""
  171. def test_model_initialization(self, app, clean_manager):
  172. """Test model initialization."""
  173. entries = clean_manager.get_all_entries()
  174. model = ShortcutsModel(entries)
  175. assert model.rowCount() == len(entries)
  176. assert model.columnCount() == 3
  177. def test_data_retrieval(self, app, clean_manager):
  178. """Test data retrieval from model."""
  179. entries = clean_manager.get_all_entries()
  180. model = ShortcutsModel(entries)
  181. index = model.index(0, 0) # First row, action column
  182. data = model.data(index)
  183. assert data # Should have a name
  184. index = model.index(0, 1) # First row, shortcut column
  185. data = model.data(index)
  186. assert isinstance(data, str)
  187. def test_header_data(self, app, clean_manager):
  188. """Test header data."""
  189. entries = clean_manager.get_all_entries()
  190. model = ShortcutsModel(entries)
  191. assert model.headerData(0, Qt.Orientation.Horizontal) == "操作"
  192. assert model.headerData(1, Qt.Orientation.Horizontal) == "快捷键"
  193. assert model.headerData(2, Qt.Orientation.Horizontal) == "默认"
  194. def test_get_entry_at_row(self, app, clean_manager):
  195. """Test getting entry at specific row."""
  196. entries = clean_manager.get_all_entries()
  197. model = ShortcutsModel(entries)
  198. entry = model.get_entry_at_row(0)
  199. assert entry is not None
  200. assert isinstance(entry, ShortcutEntry)
  201. entry = model.get_entry_at_row(999)
  202. assert entry is None
  203. class TestShortcutsSingleton:
  204. """Tests for the singleton pattern."""
  205. def test_get_singleton(self):
  206. """Test getting singleton instance."""
  207. manager = get_shortcuts_manager()
  208. assert manager is not None
  209. assert isinstance(manager, ShortcutsManager)
  210. def test_singleton_persistence(self):
  211. """Test that singleton returns same instance."""
  212. manager1 = get_shortcuts_manager()
  213. manager2 = get_shortcuts_manager()
  214. assert manager1 is manager2