OAuth/CLI-configured providers (openai-codex, copilot, nous) no longer blocked by onboarding wizard. 5 new tests, 758 total.
This commit is contained in:
@@ -6,6 +6,11 @@
|
||||
---
|
||||
|
||||
|
||||
## [v0.50.3] Onboarding completes gracefully for pre-configured providers (PR #323, fixes #322)
|
||||
|
||||
- **OAuth/CLI-configured providers no longer blocked by onboarding** (closes #322): Users with providers already set up via the CLI (`openai-codex`, `copilot`, `nous`, etc.) hit `Unsupported provider for WebUI onboarding` when clicking "Open Hermes" on the finish page. The wizard now marks onboarding complete and lets them through — the agent setup is already done, no wizard steps needed.
|
||||
- 5 new tests in `tests/test_sprint34.py`; 758 tests total (up from 753)
|
||||
|
||||
## [v0.50.2] Workspace panel state persists across refreshes
|
||||
|
||||
- **Workspace panel open/closed persists** (localStorage key `hermes-webui-workspace-panel`): Once you open the workspace/files pane, it stays open after a page refresh. Closing it explicitly saves the closed state, which also survives a refresh. The restore happens in the boot sequence before the first render, so there is no flash of the wrong state. Works for both desktop and mobile.
|
||||
|
||||
@@ -417,7 +417,11 @@ def apply_onboarding_setup(body: dict) -> dict:
|
||||
base_url = _normalize_base_url(str(body.get("base_url") or ""))
|
||||
|
||||
if provider not in _SUPPORTED_PROVIDER_SETUPS:
|
||||
raise ValueError("Unsupported provider for WebUI onboarding.")
|
||||
# Unsupported providers (openai-codex, copilot, nous, etc.) are already
|
||||
# configured via the CLI. Just mark onboarding as complete and let the
|
||||
# user through — the agent is already set up, no further setup needed.
|
||||
save_settings({"onboarding_completed": True})
|
||||
return get_onboarding_status()
|
||||
if not model:
|
||||
raise ValueError("model is required")
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@
|
||||
<div class="settings-section-title">System</div>
|
||||
<div class="settings-section-meta">Instance version and access controls.</div>
|
||||
</div>
|
||||
<span class="settings-version-badge">v0.50.2</span>
|
||||
<span class="settings-version-badge">v0.50.3</span>
|
||||
</div>
|
||||
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
||||
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
|
||||
|
||||
@@ -242,3 +242,59 @@ def test_control_center_tab_highlight_on_open():
|
||||
"""Opening the control center must use settings-tabs for section navigation."""
|
||||
css = open('static/style.css').read()
|
||||
assert 'settings-tabs' in css, 'settings-tabs CSS class for control center tabs missing from style.css'
|
||||
|
||||
|
||||
# ── apply_onboarding_setup: unsupported/OAuth providers complete gracefully ──
|
||||
|
||||
class TestApplyOnboardingSetupUnsupportedProvider:
|
||||
"""PR #323 / Issue #322: apply_onboarding_setup must not raise ValueError for
|
||||
providers already configured via CLI (openai-codex, copilot, nous, etc.).
|
||||
Instead it marks onboarding complete and returns current status.
|
||||
"""
|
||||
|
||||
def _call(self, provider: str) -> dict:
|
||||
import sys, pathlib, unittest.mock, tempfile, os
|
||||
repo = pathlib.Path(__file__).parent.parent
|
||||
if str(repo) not in sys.path:
|
||||
sys.path.insert(0, str(repo))
|
||||
|
||||
from api.onboarding import apply_onboarding_setup
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with unittest.mock.patch("api.onboarding._get_active_hermes_home",
|
||||
return_value=pathlib.Path(tmp)), \
|
||||
unittest.mock.patch("api.onboarding._get_config_path",
|
||||
return_value=pathlib.Path(tmp) / "config.yaml"), \
|
||||
unittest.mock.patch("api.onboarding.save_settings") as mock_save, \
|
||||
unittest.mock.patch("api.onboarding.get_onboarding_status",
|
||||
return_value={"completed": True, "system": {}}):
|
||||
result = apply_onboarding_setup({"provider": provider, "model": "", "api_key": ""})
|
||||
return result, mock_save
|
||||
|
||||
def test_openai_codex_does_not_raise(self):
|
||||
"""apply_onboarding_setup with openai-codex must not raise ValueError."""
|
||||
result, _ = self._call("openai-codex")
|
||||
assert result is not None
|
||||
|
||||
def test_copilot_does_not_raise(self):
|
||||
"""apply_onboarding_setup with copilot must not raise ValueError."""
|
||||
result, _ = self._call("copilot")
|
||||
assert result is not None
|
||||
|
||||
def test_nous_does_not_raise(self):
|
||||
"""apply_onboarding_setup with nous must not raise ValueError."""
|
||||
result, _ = self._call("nous")
|
||||
assert result is not None
|
||||
|
||||
def test_unsupported_provider_marks_onboarding_complete(self):
|
||||
"""apply_onboarding_setup with an unsupported provider must save onboarding_completed=True."""
|
||||
_, mock_save = self._call("openai-codex")
|
||||
calls = [str(c) for c in mock_save.call_args_list]
|
||||
assert any("onboarding_completed" in c for c in calls), \
|
||||
"save_settings must be called with onboarding_completed=True for unsupported providers"
|
||||
|
||||
def test_unsupported_provider_returns_status_dict(self):
|
||||
"""apply_onboarding_setup with an unsupported provider must return a status dict (not raise)."""
|
||||
result, _ = self._call("openai-codex")
|
||||
assert isinstance(result, dict), \
|
||||
"apply_onboarding_setup must return a dict for unsupported providers, not raise"
|
||||
|
||||
Reference in New Issue
Block a user