cmux/tests_v2/test_command_palette_fuzzy_ranking.py
Lawrence Chen 5d63c5f035
Add command palette (Cmd+Shift+P) (#358)
Implements a VS Code-style command palette with fuzzy search,
workspace/surface switching, rename mode, and keyboard navigation.

Closes https://github.com/manaflow-ai/cmux/issues/133
2026-02-23 03:26:36 -08:00

133 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
Regression test: command palette fuzzy ranking for rename commands.
Validates:
- Typing `rename` is captured by the palette query.
- The top-ranked command is a rename command.
- Pressing Enter opens rename input (instead of running an unrelated command).
"""
import os
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from cmux import cmux, cmuxError
SOCKET_PATH = os.environ.get("CMUX_SOCKET", "/tmp/cmux-debug.sock")
RENAME_COMMAND_IDS = {"palette.renameTab", "palette.renameWorkspace"}
def _wait_until(predicate, timeout_s=5.0, interval_s=0.05, message="timeout"):
start = time.time()
while time.time() - start < timeout_s:
if predicate():
return
time.sleep(interval_s)
raise cmuxError(message)
def _palette_visible(client: cmux, window_id: str) -> bool:
payload = client._call("debug.command_palette.visible", {"window_id": window_id}) or {}
return bool(payload.get("visible"))
def _rename_input_selection(client: cmux, window_id: str) -> dict:
return client._call("debug.command_palette.rename_input.selection", {"window_id": window_id}) or {}
def _palette_results(client: cmux, window_id: str, limit: int = 10) -> dict:
return client.command_palette_results(window_id=window_id, limit=limit)
def _set_palette_visible(client: cmux, window_id: str, visible: bool) -> None:
if _palette_visible(client, window_id) == visible:
return
client._call("debug.command_palette.toggle", {"window_id": window_id})
_wait_until(
lambda: _palette_visible(client, window_id) == visible,
message=f"palette visibility did not become {visible}",
)
def main() -> int:
with cmux(SOCKET_PATH) as client:
client.activate_app()
time.sleep(0.2)
window_id = client.current_window()
for row in client.list_windows():
other_id = str(row.get("id") or "")
if other_id and other_id != window_id:
client.close_window(other_id)
time.sleep(0.2)
client.focus_window(window_id)
client.activate_app()
time.sleep(0.2)
workspace_id = client.new_workspace(window_id=window_id)
client.select_workspace(workspace_id)
time.sleep(0.2)
_set_palette_visible(client, window_id, False)
_set_palette_visible(client, window_id, True)
# Force command mode query regardless transient field-editor selection state.
time.sleep(0.2)
client.simulate_shortcut("cmd+a")
client.simulate_type(">rename")
_wait_until(
lambda: "rename" in str(_palette_results(client, window_id).get("query") or "").strip().lower(),
message="palette query did not update to 'rename'",
)
payload = _palette_results(client, window_id, limit=12)
rows = payload.get("results") or []
if not rows:
raise cmuxError(f"palette returned no results for rename query: {payload}")
top = rows[0] or {}
top_id = str(top.get("command_id") or "")
top_title = str(top.get("title") or "")
if top_id not in RENAME_COMMAND_IDS:
titles = [str(row.get("title") or "") for row in rows]
raise cmuxError(
f"unexpected top result for 'rename': id={top_id!r} title={top_title!r} results={titles}"
)
client.simulate_shortcut("cmd+a")
client.simulate_type(">retab")
_wait_until(
lambda: "retab" in str(_palette_results(client, window_id).get("query") or "").strip().lower(),
message="palette query did not update to 'retab'",
)
retab_payload = _palette_results(client, window_id, limit=12)
retab_rows = retab_payload.get("results") or []
if not retab_rows:
raise cmuxError(f"palette returned no results for retab query: {retab_payload}")
top_retabs = [str(row.get("command_id") or "") for row in retab_rows[:3]]
if "palette.renameTab" not in top_retabs:
raise cmuxError(
f"'retab' did not rank Rename Tab near top: top3={top_retabs} rows={retab_rows}"
)
client.simulate_shortcut("enter")
_wait_until(
lambda: _palette_visible(client, window_id)
and bool(_rename_input_selection(client, window_id).get("focused")),
message="Enter did not open rename input for top rename result",
)
_set_palette_visible(client, window_id, False)
print("PASS: command palette fuzzy ranking prioritizes rename commands")
return 0
if __name__ == "__main__":
raise SystemExit(main())