"""``_claude_setup_cta.jinja`` — shared "Setup a new Claude Code" partial. Both /dashboard and /home (not-onboarded) include the partial so the clipboard payload, error reporting, and fallback modal stay in lockstep. These tests pin that contract: a marker change on one page that doesn't appear on the other usually means the partial was forked instead of shared. """ 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"): 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]) return uid, create_access_token(user_id=uid, email=email) def _client(): from fastapi.testclient import TestClient from app.main import app return TestClient(app) # Markers from the shared partial that MUST appear on every page that # wires up the one-click setup flow. _PARTIAL_MARKERS = ( "SETUP_INSTRUCTIONS_TEMPLATE", # JS template array from _claude_setup_instructions.jinja "renderSetupInstructions", # JS renderer function "function setupNewClaude", # async fn the button click invokes "function showSetupFallback", # clipboard-blocked modal ".setup-fallback-modal", # modal CSS 'fetch(\'/auth/tokens\'', # token-mint endpoint "__setupCtaWired", # IIFE re-include guard ) def _assert_partial_present(body: str) -> None: missing = [m for m in _PARTIAL_MARKERS if m not in body] assert not missing, ( "Shared setup-CTA markers missing from rendered body: %r" % missing ) def test_dashboard_includes_setup_cta_partial(fresh_db): """Pre-existing dashboard CTA still renders after the JS extraction.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn) finally: conn.close() close_system_db() c = _client() resp = c.get("/dashboard", cookies={"access_token": sess}) assert resp.status_code == 200 _assert_partial_present(resp.text) # Dashboard's own CTA chrome still here too. assert 'id="setupClaudeBtn"' in resp.text assert "Setup a new Claude Code" in resp.text def test_home_not_onboarded_includes_setup_cta_partial(fresh_db): """The new /home wiring renders the same partial markers as dashboard.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn) # Default onboarded=FALSE → not-onboarded view. finally: conn.close() close_system_db() c = _client() resp = c.get("/home", cookies={"access_token": sess}) assert resp.status_code == 200 _assert_partial_present(resp.text) assert 'id="setupClaudeBtn"' in resp.text assert "Setup a new Claude Code" in resp.text def test_home_renders_preview_under_manual_fallback(fresh_db): """The collapsed 'Or paste manually' details exposes the same `setup-preview-pre` block that /setup uses, so users can read the payload without committing to the one-click flow.""" from src.db import get_system_db, close_system_db conn = get_system_db() try: _, sess = _make_user_and_session(conn) 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 "manual-fallback" in body assert "setup-preview-pre" in body assert "placeholder-token" in body # rendered placeholder span