3dd153f718
Per spec FR1 + Phase 1.3 + architecture feedback: src/command_palette.py
split by responsibility:
- Command/ScoredCommand/CommandRegistry/fuzzy_match/_close_palette/_execute (data/ops)
-> src/commands.py (which already owns _LazyCommandRegistry pattern)
- render_palette_modal (view/ImGui) -> src/gui_2.py
GUI is a pure view; the registry/data classes are ops; commands.py owns
the registry because commands.py is where @registry.register decorators live.
gui_2.render_palette_modal imports Command from commands.py to type its
parameters.
Also fixes Phase 1.1 (bg_shader) per architecture feedback:
BackgroundShader no longer owns 'enabled' state - the GUI is pure view.
State is now owned by AppController.bg_shader_enabled (read on load from
config, written from gui_2 checkbox via app's __setattr__ delegation).
Tests:
- tests/test_command_palette.py: imports from src.commands (was src.command_palette)
- tests/test_commands_no_top_level_command_palette.py: rewritten for the
new architecture (eager registry in commands.py; render in gui_2; no
circular import between commands.py and gui_2)
140 lines
4.1 KiB
Python
140 lines
4.1 KiB
Python
from src.commands import Command, ScoredCommand, fuzzy_match
|
|
|
|
|
|
def _cmd(id: str, title: str) -> Command:
|
|
return Command(id=id, title=title, category="test")
|
|
|
|
|
|
def test_fuzzy_match_prefix_ranks_first():
|
|
candidates = [
|
|
_cmd("find", "Find in Selection"),
|
|
_cmd("fold", "Fold All"),
|
|
_cmd("config", "Configure Settings"),
|
|
]
|
|
results = fuzzy_match("fin", candidates, top_n=10)
|
|
assert len(results) > 0
|
|
assert results[0].command.id == "find"
|
|
assert results[0].score > 0.5
|
|
|
|
|
|
def test_fuzzy_match_subsequence_match():
|
|
candidates = [_cmd("x", "Find")]
|
|
results = fuzzy_match("fd", candidates, top_n=10)
|
|
assert len(results) == 1
|
|
assert results[0].command.id == "x"
|
|
|
|
|
|
def test_fuzzy_match_no_match_returns_empty():
|
|
candidates = [_cmd("x", "foo bar")]
|
|
results = fuzzy_match("xyz", candidates, top_n=10)
|
|
assert results == []
|
|
|
|
|
|
def test_fuzzy_match_top_n_limits_results():
|
|
candidates = [_cmd(f"cmd_{i}", f"Command {i}") for i in range(50)]
|
|
results = fuzzy_match("cmd", candidates, top_n=10)
|
|
assert len(results) == 10
|
|
|
|
|
|
def test_fuzzy_match_score_higher_for_exact_prefix():
|
|
candidates = [
|
|
_cmd("a", "find"),
|
|
_cmd("b", "Configure Find Settings"),
|
|
]
|
|
results = fuzzy_match("fin", candidates, top_n=10)
|
|
assert results[0].command.id == "a"
|
|
|
|
|
|
def test_commands_registry_has_core_commands():
|
|
from src.commands import registry
|
|
all_ids = {c.id for c in registry.all()}
|
|
assert "reset_session" in all_ids
|
|
assert "clear_discussion" in all_ids
|
|
assert "trigger_hot_reload" in all_ids
|
|
assert "show_documentation" in all_ids
|
|
|
|
|
|
def test_commands_registry_has_view_toggles():
|
|
"""The palette should expose all window toggle commands for keyboard ergonomics."""
|
|
from src.commands import registry
|
|
all_ids = {c.id for c in registry.all()}
|
|
expected_toggles = [
|
|
"toggle_text_viewer",
|
|
"toggle_diagnostics",
|
|
"toggle_usage_analytics",
|
|
"toggle_context_preview",
|
|
"toggle_tier1_strategy",
|
|
"toggle_tier2_tech_lead",
|
|
"toggle_tier3_workers",
|
|
"toggle_tier4_qa",
|
|
"toggle_external_tools",
|
|
"toggle_shader_editor",
|
|
"toggle_undo_redo_history",
|
|
"toggle_command_palette",
|
|
]
|
|
for cmd_id in expected_toggles:
|
|
assert cmd_id in all_ids, f"Missing window toggle command: {cmd_id}"
|
|
|
|
|
|
def test_commands_registry_has_theme_commands():
|
|
from src.commands import registry
|
|
all_ids = {c.id for c in registry.all()}
|
|
assert "switch_to_dark_theme" in all_ids
|
|
assert "switch_to_light_theme" in all_ids
|
|
assert "switch_to_nerv_theme" in all_ids
|
|
assert "cycle_theme" in all_ids
|
|
|
|
|
|
def test_commands_registry_has_layout_commands():
|
|
from src.commands import registry
|
|
all_ids = {c.id for c in registry.all()}
|
|
assert "show_all_panels" in all_ids
|
|
assert "hide_all_panels" in all_ids
|
|
assert "save_workspace_profile" in all_ids
|
|
|
|
|
|
def test_commands_registry_has_undo_redo_commands():
|
|
from src.commands import registry
|
|
all_ids = {c.id for c in registry.all()}
|
|
assert "undo" in all_ids
|
|
assert "redo" in all_ids
|
|
|
|
|
|
def test_all_commands_have_actions():
|
|
"""Every registered command must have a callable action."""
|
|
from src.commands import registry
|
|
for cmd in registry.all():
|
|
assert cmd.id, f"Command missing id: {cmd}"
|
|
assert cmd.title, f"Command {cmd.id} missing title"
|
|
assert cmd.category, f"Command {cmd.id} missing category"
|
|
assert cmd.action is not None, f"Command {cmd.id} missing action"
|
|
assert callable(cmd.action), f"Command {cmd.id} action is not callable"
|
|
|
|
|
|
def test_toggle_helpers_are_safe_with_missing_state():
|
|
"""The _toggle_window and _toggle_attr helpers must not raise on missing state."""
|
|
from unittest.mock import MagicMock
|
|
from src.commands import _toggle_window, _toggle_attr
|
|
|
|
bare_app = MagicMock(spec=[])
|
|
_toggle_window(bare_app, "Anything")
|
|
_toggle_attr(bare_app, "anything")
|
|
|
|
partial_app = MagicMock()
|
|
partial_app.show_windows = {}
|
|
_toggle_window(partial_app, "NewPanel")
|
|
assert partial_app.show_windows["NewPanel"] is True
|
|
|
|
|
|
def test_undo_command_routes_to_handler():
|
|
from unittest.mock import MagicMock
|
|
from src.commands import registry
|
|
|
|
app = MagicMock()
|
|
app._handle_undo = MagicMock()
|
|
|
|
undo_cmd = registry.get("undo")
|
|
assert undo_cmd is not None
|
|
undo_cmd.action(app)
|
|
app._handle_undo.assert_called_once()
|