Keep TUI cursor stable after plugin toggle

This commit is contained in:
2026-07-05 14:05:35 -07:00
parent 400e69289c
commit 74ac0a1c1d
3 changed files with 94 additions and 1 deletions
+88
View File
@@ -1471,6 +1471,75 @@ sha256 = "{sha256_file(asset)}"
return PluginHelperTui(choices=[choice], repo_root=repo), instance, state
def _make_two_plugin_tui_fixture(root: Path) -> tuple[PluginHelperTui, Path, Path]:
repo = root / "repo"
instance_root = root / "instances"
instance = instance_root / "1.40.8"
state = root / "state"
(repo / "registry").mkdir(parents=True)
(repo / "locks").mkdir()
(instance / "Beat Saber_Data").mkdir(parents=True)
(instance / "Plugins").mkdir()
plugins_state: dict[str, dict] = {}
lock_entries: list[str] = []
registry_entries: list[str] = []
for plugin_id, name, filename in (
("alpha", "Alpha", "Alpha.dll"),
("beta", "Beta", "Beta.dll"),
):
asset = plugin_downloads_dir(state, "1.40.8", plugin_id) / filename
asset.write_bytes(f"{plugin_id} dll".encode())
(instance / "Plugins" / filename).write_bytes(f"{plugin_id} dll".encode())
registry_entries.append(
f"""
[[plugins]]
id = "{plugin_id}"
name = "{name}"
repo = "owner/{plugin_id}"
asset_patterns = ["*.dll"]
install_strategy = "dll-to-plugins"
""".lstrip()
)
lock_entries.append(
f"""
[[plugins]]
id = "{plugin_id}"
repo = "owner/{plugin_id}"
tag = "v1.0.0"
asset = "{filename}"
sha256 = "{sha256_file(asset)}"
""".lstrip()
)
plugins_state[plugin_id] = {
"installedAt": "2026-06-14T17:18:40Z",
"files": [{"path": f"Plugins/{filename}", "sha256": sha256_file(asset), "size": asset.stat().st_size}],
}
(repo / "registry" / "plugins.toml").write_text("".join(registry_entries), encoding="utf-8")
(repo / "locks" / "1.40.8.lock.toml").write_text(
f"""
beat_saber_version = "1.40.8"
instance = "1.40.8"
{"".join(lock_entries)}
""".lstrip(),
encoding="utf-8",
)
save_installed_state(
state,
"1.40.8",
{"instance": "1.40.8", "plugins": plugins_state, "disabledPlugins": {}},
)
choice = InstallationChoice(
install_id="test",
install_label="Test Install",
instance_name="1.40.8",
instance_path=instance,
state_root=state,
)
return PluginHelperTui(choices=[choice], repo_root=repo), instance, state
class PluginHelperTuiTests(unittest.IsolatedAsyncioTestCase):
async def test_installation_picker_shows_duplicate_instances_with_state_dirs(self) -> None:
choices = [
@@ -1523,6 +1592,25 @@ class PluginHelperTuiTests(unittest.IsolatedAsyncioTestCase):
self.assertNotIn("example", updated["plugins"])
self.assertIn("example", updated["disabledPlugins"])
async def test_space_toggle_keeps_cursor_on_selected_plugin(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
app, instance, _state = _make_two_plugin_tui_fixture(Path(tmp))
async with app.run_test() as pilot:
table = app.query_one(DataTable)
self.assertEqual(table.row_count, 2)
await pilot.press("down")
await pilot.pause()
self.assertEqual(table.cursor_row, 1)
self.assertEqual(app.plugin_rows[table.cursor_row]["id"], "beta")
await pilot.press("space")
await pilot.pause()
self.assertEqual(table.cursor_row, 1)
self.assertEqual(app.plugin_rows[table.cursor_row]["id"], "beta")
self.assertFalse((instance / "Plugins" / "Beta.dll").exists())
self.assertTrue((instance / "Plugins" / "Alpha.dll").exists())
async def test_space_enables_disabled_plugin_from_asset(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
app, instance, state = _make_tui_fixture(Path(tmp), disabled=True)