fix: streamline quick start onboarding flow

Maintainer edit: continue the wizard simplification pass by making Quick Start save after the API key path, hiding save/summary actions until they are needed, removing failed-key side effects, and aligning beginner docs with the local WebUI path.
This commit is contained in:
chengyongru
2026-06-22 13:04:05 +08:00
committed by Xubin Ren
parent fa5f7f5b88
commit 0e9b136315
5 changed files with 146 additions and 118 deletions
+51 -9
View File
@@ -858,13 +858,29 @@ class TestApiServerRegistration:
class TestMainMenuUpdate:
"""Tests for main menu including new Channel Common and API Server items."""
def test_main_menu_hides_save_actions_until_needed(self):
"""The first screen should not show save or summary actions before edits."""
from nanobot.cli.onboard import _get_main_menu_choices
clean_choices = _get_main_menu_choices(False)
dirty_choices = _get_main_menu_choices(True)
assert clean_choices == [
"[Q] Quick Start (API key only)",
"[A] Advanced Settings",
"[X] Exit",
]
assert "[S] Save and Exit" not in clean_choices
assert "[V] View Configuration Summary" not in clean_choices
assert "[S] Save and Exit" in dirty_choices
assert "[X] Exit Without Saving" in dirty_choices
def test_run_onboard_quick_start_edit(self, monkeypatch):
"""run_onboard should route [Q] to Quick Start."""
initial_config = Config()
responses = iter([
"[Q] Quick Start (API key only)",
"[S] Save and Exit",
])
class FakePrompt:
@@ -879,6 +895,7 @@ class TestMainMenuUpdate:
def fake_quick_start(config):
config.agents.defaults.bot_name = "quickbot"
return True
monkeypatch.setattr(onboard_wizard, "_show_main_menu_header", lambda: None)
monkeypatch.setattr(onboard_wizard, "questionary", SimpleNamespace(select=fake_select))
@@ -899,16 +916,19 @@ class TestMainMenuUpdate:
def fail_websocket_config(*_args, **_kwargs):
raise AssertionError("recommended Quick Start should not open WebSocket settings")
pause_messages: list[str] = []
monkeypatch.setattr(onboard_wizard.console, "clear", lambda: None)
monkeypatch.setattr(onboard_wizard, "_show_section_header", lambda *a, **kw: None)
monkeypatch.setattr(onboard_wizard, "_input_with_existing", lambda *a, **kw: "sk-or-test")
monkeypatch.setattr(onboard_wizard, "_input_model_with_autocomplete", fail_model_input)
monkeypatch.setattr(onboard_wizard, "_configure_pydantic_model", fail_websocket_config)
monkeypatch.setattr(onboard_wizard, "_print_summary_panel", lambda *a, **kw: None)
monkeypatch.setattr(onboard_wizard, "_pause", lambda: None)
monkeypatch.setattr(onboard_wizard, "_pause", lambda message="": pause_messages.append(message))
onboard_wizard._configure_quick_start(config)
assert onboard_wizard._configure_quick_start(config) is True
assert pause_messages == ["Press Enter to save and exit..."]
assert config.providers.openrouter.api_key == "sk-or-test"
assert config.providers.openrouter.api_base == "https://openrouter.ai/api/v1"
assert config.agents.defaults.model_preset == "primary"
@@ -918,6 +938,19 @@ class TestMainMenuUpdate:
assert websocket["enabled"] is True
assert websocket["websocketRequiresToken"] is True
def test_quick_start_requires_api_key_before_setting_defaults(self, monkeypatch):
"""Quick Start should not create a ready-looking config without an API key."""
config = Config()
monkeypatch.setattr(onboard_wizard, "_show_quick_start_progress", lambda *_args: None)
monkeypatch.setattr(onboard_wizard, "_input_with_existing", lambda *a, **kw: "")
assert onboard_wizard._configure_recommended_provider(config) is False
assert config.providers.openrouter.api_key is None
assert config.providers.openrouter.api_base is None
assert "primary" not in config.model_presets
def test_quick_start_summary_calls_out_missing_api_key(self, monkeypatch):
"""Quick Start summary should not tell users to run gateway before adding a key."""
config = Config()
@@ -935,11 +968,18 @@ class TestMainMenuUpdate:
lambda rows, _title: captured.setdefault("rows", rows),
)
onboard_wizard._show_quick_start_summary(config, "websocket")
onboard_wizard._show_quick_start_summary(config)
labels = [label for label, _value in captured["rows"]]
rows = dict(captured["rows"])
assert rows["API key"] == "add later"
assert "add your API key" in rows["Next"]
assert rows["Status"] == "OpenRouter API key missing"
assert "API key" in rows["Next"]
assert "nanobot gateway" in rows["Next"]
assert labels.index("Next") < labels.index("Open")
assert "Model" not in rows
assert "Entry point" not in rows
assert "API key" not in rows
assert "Defaults" not in rows
def test_configure_login_channel_defaults_to_login(self, monkeypatch):
"""The channel wizard should start login before exposing advanced fields."""
@@ -1088,13 +1128,15 @@ class TestMainMenuUpdate:
assert result.config.api.port == 9999
def test_view_summary_calls_pause(self, monkeypatch):
"""[V] View Summary should pause before returning to main menu."""
"""Advanced [V] View Summary should pause before returning to the menu."""
initial_config = Config()
pause_called = {"n": 0}
responses = iter([
"[A] Advanced Settings",
"[V] View Configuration Summary",
"[S] Save and Exit",
KeyboardInterrupt(),
"[X] Exit",
])
class FakePrompt:
@@ -1123,7 +1165,7 @@ class TestMainMenuUpdate:
result = run_onboard(initial_config=initial_config)
assert result.should_save is True
assert result.should_save is False
assert pause_called["n"] == 1