"""GET /home — state-aware landing page. The boolean ``users.onboarded`` drives template selection. No auto-transition: the not-onboarded view stays put until the user reloads (the brainstorm called this out explicitly — quiet UI is preferable to a surprise redirect mid-setup). See origin: docs/brainstorms/home-page-requirements.md. """ from __future__ import annotations import tempfile import uuid import pytest @pytest.fixture def fresh_db(monkeypatch): with tempfile.TemporaryDirectory() as tmp: monkeypatch.setenv("DATA_DIR", tmp) monkeypatch.setenv("TESTING", "1") monkeypatch.setenv("JWT_SECRET_KEY", "test-jwt-secret-key-minimum-32-chars!!") yield tmp def _make_user_and_session(conn, email="u@example.com", onboarded=False): from src.repositories.users import UserRepository from app.auth.jwt import create_access_token uid = str(uuid.uuid4()) UserRepository(conn).create(id=uid, email=email, name=email.split("@")[0]) if onboarded: conn.execute("UPDATE users SET onboarded = TRUE WHERE id = ?", [uid]) return uid, create_access_token(user_id=uid, email=email) def _client(follow_redirects: bool = True): from fastapi.testclient import TestClient from app.main import app return TestClient(app, follow_redirects=follow_redirects) def test_home_unauth_redirects_to_login(fresh_db): """Non-API HTML routes redirect 401→/login per app.main's StarletteHTTPException handler. /home follows that contract.""" c = _client(follow_redirects=False) resp = c.get("/home") assert resp.status_code == 302 assert resp.headers["location"].startswith("/login") def test_home_not_onboarded_user_sees_setup_view(fresh_db): """A FALSE-onboarded user gets the install/setup template, identifiable by its 'Install Claude Code' heading and the self-mark button.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn, onboarded=False) finally: conn.close() close_system_db() c = _client() resp = c.get("/home", cookies={"access_token": sess}) assert resp.status_code == 200 body = resp.text assert "install Claude Code" in body # step 1 label assert "install Agnes" in body # step 2 label assert "self-mark-btn" in body # self-acknowledged escape hatch assert "setupClaudeBtn" in body # primary one-click CTA from shared partial def test_home_onboarded_user_sees_nav_hub(fresh_db): """A TRUE-onboarded user gets the post-onboarding view: the blue install-hero is gone entirely (no welcome banner, no completion badge, no inline step commands), the offboard escape strip is the only setup-flow remnant rendered, and the rest of /home (connector tiles, news, etc.) stays. PR #289 collapsed the dual-state hero into a single not-onboarded-only render — pre-PR the onboarded branch reused the same `.install-hero` shell with welcome copy and a "Steps 1–4 done" badge.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn, onboarded=True) finally: conn.close() close_system_db() c = _client() resp = c.get("/home", cookies={"access_token": sess}) assert resp.status_code == 200 body = resp.text # Install hero entirely absent for onboarded users. assert '
' not in body # Offboard escape strip + its button replace the in-hero self-mark control. assert '
' in body assert "Mark me as offboarded" in body # All six inline install-blocks are hidden post-onboarding — the # labels rendered inside the install-block divs go away. Labels # tracked with the CEO-mock-parity 6-step rename (Step 3 inserted # as "Open a terminal", Step 6 added as the optional shortcut # alias; the old Step 3 became Step 4). assert "Step 1 — Install Claude Code on your computer" not in body assert "Step 2 — Pick a folder for" not in body assert "Step 3 — Open a terminal inside that folder" not in body assert "Step 4 — Launch Claude with auto-approve on" not in body assert "Step 5 — Get the install script" not in body assert "Step 6 — Optional: create a one-word shortcut" not in body def test_connectors_section_removed_from_home(fresh_db): """The dedicated `
` block was dropped from `/home` — the install-hero's Step 4 clipboard payload (rendered via `_claude_setup_instructions.jinja` inside the manual fallback) already inlines the same Asana / GWS / Atlassian prompts from `app/web/connector_prompts.py` via `app/web/setup_instructions.py::_connectors_block`. Showing them twice on the same page was duplicate UX. The lead paragraph in the install-hero now mentions the connectors briefly so users still see the benefit before they hit the install. Co-asserts the auto-mode block removal that this test originally pinned — onboarded users still see neither the connectors block nor the legacy auto-mode peer section.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn, onboarded=True) finally: conn.close() close_system_db() c = _client() resp = c.get("/home", cookies={"access_token": sess}) assert resp.status_code == 200 body = resp.text # Auto-mode peer section still gone (legacy guard, not regressed). assert 'class="automode-card"' not in body assert 'data-section="step3"' not in body assert "Step 4 — Launch Claude with auto-approve on" not in body # Dedicated connectors block is gone from /home in BOTH states. assert 'class="connector-tiles"' not in body assert 'data-section="connectors"' not in body # Server-rendered HTML never carries the data-setup-minimized # attribute on the .home-mock root — that's a client-side # localStorage decision applied via JS on load. assert '
' in body # Not-onboarded path: same — the section disappears regardless of # state. Lead-paragraph still surfaces the connector names so users # know the benefit exists before they kick off the install. conn = get_system_db() try: _, sess2 = _make_user_and_session( conn, email="not-onboarded@example.com", onboarded=False ) finally: conn.close() close_system_db() body2 = _client().get("/home", cookies={"access_token": sess2}).text assert 'class="connector-tiles"' not in body2 assert 'data-section="connectors"' not in body2 # Lead-paragraph mentions the three connector families so the # benefit isn't lost when the dedicated section disappears. assert "Asana, Google Workspace, Atlassian" in body2 def test_minimize_toggle_no_longer_rendered(fresh_db): """The "Minimize setup view" toggle used to live inside the onboarded-branch of the install-hero. PR #289 hides the hero entirely once `users.onboarded=true`, so the minimize toggle has no rendering site anymore — verify the markup is absent from both states. (The localStorage `agnes_home_setup_minimized` flag and its applyMinimize() JS handler stay in the page so a stale flag from a pre-PR session no-ops cleanly.)""" from src.db import get_system_db, close_system_db for onboarded in (False, True): conn = get_system_db() try: _, sess = _make_user_and_session( conn, email=f"user-{onboarded}@example.com", onboarded=onboarded ) finally: conn.close() close_system_db() c = _client() resp = c.get("/home", cookies={"access_token": sess}) assert resp.status_code == 200 assert '