import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { SettingsView } from "@/components/settings/SettingsView"; import { ClientProvider } from "@/providers/ClientProvider"; import type { SettingsPayload } from "@/lib/types"; function jsonResponse(body: unknown): Response { return { ok: true, status: 200, json: async () => body, } as Response; } function settingsPayload(): SettingsPayload { return { agent: { model: "openai/gpt-4o", provider: "auto", resolved_provider: "openai", has_api_key: true, model_preset: "default", max_tokens: 8192, context_window_tokens: 65536, temperature: 0.1, reasoning_effort: null, timezone: "UTC", bot_name: "nanobot", bot_icon: "nb", tool_hint_max_length: 40, }, model_presets: [{ name: "default", label: "Default", active: true, is_default: true, model: "openai/gpt-4o", provider: "auto", max_tokens: 8192, context_window_tokens: 65536, temperature: 0.1, reasoning_effort: null, }], providers: [], web_search: { provider: "duckduckgo", api_key_hint: null, base_url: null, max_results: 5, timeout: 30, providers: [{ name: "duckduckgo", label: "DuckDuckGo", credential: "none" }], }, web: { enable: true, proxy: null, user_agent: null, search: { max_results: 5, timeout: 30 }, fetch: { use_jina_reader: true }, }, image_generation: { enabled: false, provider: "openrouter", provider_configured: false, model: "openai/gpt-5.4-image-2", default_aspect_ratio: "1:1", default_image_size: "1K", max_images_per_turn: 4, save_dir: "generated", providers: [], }, runtime: { config_path: "/tmp/config.json", workspace_path: "/tmp/workspace", gateway_host: "127.0.0.1", gateway_port: 18790, heartbeat: { enabled: true, interval_s: 1800, keep_recent_messages: 8, }, dream: { schedule: "every 2h", }, unified_session: false, }, advanced: { restrict_to_workspace: false, webui_allow_local_service_access: true, webui_default_access_mode: "default", private_service_protection_enabled: true, ssrf_whitelist_count: 0, mcp_server_count: 0, exec_enabled: true, exec_sandbox: null, exec_path_prepend_set: false, exec_path_append_set: false, }, requires_restart: false, }; } function autoDynamicProviderPayload( options: { configured: boolean; hasApiKey: boolean; apiBase: string | null; apiKeyHint: string | null; }, ): SettingsPayload { const base = settingsPayload(); return { ...base, agent: { ...base.agent, model: "companyProxy/gpt-4o", provider: "companyProxy", resolved_provider: "companyProxy", has_api_key: options.hasApiKey, }, model_presets: [ { ...base.model_presets[0], model: "companyProxy/gpt-4o", provider: "auto", }, ], providers: [ { name: "companyProxy", label: "Company Proxy", configured: options.configured, auth_type: "api_key", api_key_required: false, api_key_hint: options.apiKeyHint, api_base: options.apiBase, default_api_base: null, }, ], }; } const installedAnyGen = { name: "anygen", display_name: "AnyGen", category: "generation", description: "Generate docs, slides, websites and more via AnyGen cloud API", requires: "ANYGEN_API_KEY", source: "harness", entry_point: "cli-anything-anygen", install_supported: true, installed: true, available: true, status: "installed", logo_url: "https://www.google.com/s2/favicons?domain=anygen.io&sz=64", brand_color: "#111827", skill_installed: true, }; function renderSettingsView( options: { initialSection?: "overview" | "apps" | "automations" | "advanced" | "models"; initialSettings?: SettingsPayload; showSidebar?: boolean; onSettingsChange?: (payload: SettingsPayload) => void; onNativeEngineRestart?: () => Promise; } = {}, ) { render( {}} onBackToChat={() => {}} onModelNameChange={() => {}} onSettingsChange={options.onSettingsChange} onNativeEngineRestart={options.onNativeEngineRestart} /> , ); } describe("SettingsView Apps catalog", () => { afterEach(() => { vi.useRealTimers(); vi.unstubAllGlobals(); }); it("does not show the Settings kicker on the standalone Automations surface", async () => { vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(settingsPayload()); if (url === "/api/webui/automations") return jsonResponse({ jobs: [] }); return jsonResponse({}); })); renderSettingsView({ initialSection: "automations", initialSettings: settingsPayload(), showSidebar: false, }); expect(screen.getByRole("heading", { name: "Automations" })).toBeInTheDocument(); expect(await screen.findByText("No automations yet.")).toBeInTheDocument(); expect(screen.queryByText("Settings")).not.toBeInTheDocument(); }); it("shows a visible uninstall button for installed CLI apps and calls uninstall", async () => { const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") { return jsonResponse(settingsPayload()); } if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [installedAnyGen], installed_count: 1, catalog_updated_at: "2026-04-18", }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } if (url === "/api/settings/cli-apps/uninstall?name=anygen") { return jsonResponse({ apps: [{ ...installedAnyGen, installed: false, status: "available" }], installed_count: 0, catalog_updated_at: "2026-04-18", last_action: { ok: true, message: "Uninstalled CLI for AnyGen.", still_available: false, }, }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView(); expect(await screen.findByRole("heading", { name: "Apps" })).toBeInTheDocument(); expect(await screen.findByText("AnyGen")).toBeInTheDocument(); const uninstall = screen.getByRole("button", { name: "Uninstall CLI" }); fireEvent.click(uninstall); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( "/api/settings/cli-apps/uninstall?name=anygen", expect.objectContaining({ headers: { Authorization: "Bearer tok" }, }), ), ); expect(await screen.findByText("Uninstalled CLI for AnyGen.")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Dismiss" })); expect(screen.queryByText("Uninstalled CLI for AnyGen.")).not.toBeInTheDocument(); }); it("publishes the latest settings payload to the shell", async () => { const payload = settingsPayload(); const onSettingsChange = vi.fn(); vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ onSettingsChange }); await waitFor(() => expect(onSettingsChange).toHaveBeenCalledWith(payload)); }); it("shows token activity on the overview", async () => { const payload: SettingsPayload = { ...settingsPayload(), usage: { days: [ { date: "2026-06-03", prompt_tokens: 1200, completion_tokens: 300, cached_tokens: 500, total_tokens: 1500, requests: 2, }, ], total_tokens: 1500, total_tokens_30d: 1500, total_tokens_365d: 1500, peak_day_tokens: 1500, current_streak_days: 1, longest_streak_days: 1, active_days_30d: 1, requests_30d: 2, updated_at: "2026-06-03T00:00:00Z", }, }; vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "overview" }); expect(await screen.findByLabelText("Token activity")).toBeInTheDocument(); expect(screen.getByText("Token Usage")).toBeInTheDocument(); expect(screen.queryByText("Token activity")).not.toBeInTheDocument(); expect(screen.queryByText("Total tokens")).not.toBeInTheDocument(); expect(screen.queryByText("Peak tokens")).not.toBeInTheDocument(); }); it("aligns token activity days with the configured timezone", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-06-02T18:00:00Z")); const basePayload = settingsPayload(); const payload: SettingsPayload = { ...basePayload, agent: { ...basePayload.agent, timezone: "Asia/Shanghai", }, usage: { days: [ { date: "2026-06-03", prompt_tokens: 1200, completion_tokens: 300, cached_tokens: 500, total_tokens: 1500, requests: 2, }, ], total_tokens: 1500, total_tokens_30d: 1500, total_tokens_365d: 1500, peak_day_tokens: 1500, current_streak_days: 1, longest_streak_days: 1, active_days_30d: 1, requests_30d: 2, updated_at: "2026-06-03T00:00:00Z", }, }; vi.stubGlobal("fetch", vi.fn(() => new Promise(() => {}))); renderSettingsView({ initialSection: "overview", initialSettings: payload }); expect(screen.getByLabelText("2026-06-03: 1.5K tokens, 2 requests")).toBeInTheDocument(); }); it("shows context window options in model settings", async () => { vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(settingsPayload()); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "models" }); expect(await screen.findByText("Context window")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "64K" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "256K" })).toBeInTheDocument(); }); it("uses the resolved provider row for auto dynamic providers without api keys", async () => { vi.stubGlobal("fetch", vi.fn(() => new Promise(() => {}))); renderSettingsView({ initialSection: "models", initialSettings: autoDynamicProviderPayload({ configured: true, hasApiKey: false, apiBase: "https://proxy.example.test/v1", apiKeyHint: null, }), }); const configurationButton = await screen.findByRole("button", { name: "Current configuration", }); expect(configurationButton).toHaveTextContent("companyProxy/gpt-4o"); expect(configurationButton).toHaveTextContent("Company Proxy"); expect(configurationButton).not.toHaveTextContent("Not configured"); }); it("does not treat auto dynamic provider api keys as configured without apiBase", async () => { vi.stubGlobal("fetch", vi.fn(() => new Promise(() => {}))); renderSettingsView({ initialSection: "models", initialSettings: autoDynamicProviderPayload({ configured: false, hasApiKey: true, apiBase: null, apiKeyHint: "sk-...", }), }); const configurationButton = await screen.findByRole("button", { name: "Current configuration", }); expect(configurationButton).toHaveTextContent("Not configured"); expect(configurationButton).toHaveTextContent("Company Proxy · companyProxy/gpt-4o"); }); it("marks the current model as unconfigured when its provider needs setup", async () => { const payload: SettingsPayload = { ...settingsPayload(), agent: { ...settingsPayload().agent, model: "openai-codex/gpt-5.1-codex", provider: "openai_codex", resolved_provider: "openai_codex", has_api_key: false, }, model_presets: [ { ...settingsPayload().model_presets[0], model: "openai-codex/gpt-5.1-codex", provider: "openai_codex", }, ], providers: [ { name: "openai_codex", label: "OpenAI Codex", configured: false, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: null, oauth_account: null, oauth_expires_at: null, oauth_login_supported: true, }, ], }; vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "models" }); const configurationButton = await screen.findByRole("button", { name: "Current configuration", }); expect(configurationButton).toHaveTextContent("Not configured"); expect(configurationButton).toHaveTextContent("OpenAI Codex · openai-codex/gpt-5.1-codex"); expect(await screen.findByRole("button", { name: "Sign in" })).toBeInTheDocument(); }); it("keeps unsigned OAuth providers out of the active provider picker", async () => { const payload: SettingsPayload = { ...settingsPayload(), agent: { ...settingsPayload().agent, model: "deepseek-chat", provider: "deepseek", resolved_provider: "deepseek", }, model_presets: [ { ...settingsPayload().model_presets[0], model: "deepseek-chat", provider: "deepseek", }, ], providers: [ { name: "deepseek", label: "DeepSeek", configured: true, auth_type: "api_key", api_key_required: true, api_key_hint: "sk-...", api_base: "https://api.deepseek.com", default_api_base: "https://api.deepseek.com", }, { name: "openai_codex", label: "OpenAI Codex", configured: false, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: null, oauth_account: null, oauth_expires_at: null, oauth_login_supported: true, }, { name: "github_copilot", label: "GitHub Copilot", configured: false, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: "https://api.githubcopilot.com", oauth_account: null, oauth_expires_at: null, oauth_login_supported: true, }, ], }; vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "models" }); const deepseekButtons = await screen.findAllByRole("button", { name: /DeepSeek/ }); const providerPicker = deepseekButtons.find( (button) => button.getAttribute("aria-haspopup") === "menu", ); if (!providerPicker) throw new Error("provider picker was not found"); fireEvent.pointerDown(providerPicker); expect(await screen.findByRole("menuitem", { name: /DeepSeek/ })).toBeInTheDocument(); expect(screen.queryByRole("menuitem", { name: /OpenAI Codex/ })).not.toBeInTheDocument(); expect(screen.queryByRole("menuitem", { name: /GitHub Copilot/ })).not.toBeInTheDocument(); }); it("does not fetch model lists for unsigned OAuth providers", async () => { const payload: SettingsPayload = { ...settingsPayload(), agent: { ...settingsPayload().agent, model: "", provider: "openai_codex", resolved_provider: "openai_codex", }, model_presets: [ { ...settingsPayload().model_presets[0], model: "", provider: "openai_codex", }, ], providers: [ { name: "openai_codex", label: "OpenAI Codex", configured: false, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: null, oauth_account: null, oauth_expires_at: null, oauth_login_supported: true, }, { name: "github_copilot", label: "GitHub Copilot", configured: false, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: "https://api.githubcopilot.com", oauth_account: null, oauth_expires_at: null, oauth_login_supported: true, }, ], }; const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView({ initialSection: "models" }); fireEvent.pointerDown(await screen.findByRole("button", { name: /Select model/i })); expect( await screen.findByText("Configure this provider before loading models."), ).toBeInTheDocument(); expect( fetchMock.mock.calls.some(([input]) => String(input).startsWith("/api/settings/provider-models"), ), ).toBe(false); }); it("prefills manual model ids for configured OAuth providers", async () => { const payload: SettingsPayload = { ...settingsPayload(), agent: { ...settingsPayload().agent, model: "open-codex/gpt-5.5", provider: "openai_codex", resolved_provider: "openai_codex", }, model_presets: [ { ...settingsPayload().model_presets[0], model: "open-codex/gpt-5.5", provider: "openai_codex", }, ], providers: [ { name: "openai_codex", label: "OpenAI Codex", configured: true, auth_type: "oauth", api_key_required: false, api_key_hint: null, api_base: null, default_api_base: null, oauth_account: "acct-test", oauth_expires_at: null, oauth_login_supported: true, }, ], }; const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView({ initialSection: "models" }); const modelButtons = await screen.findAllByRole("button", { name: /open-codex\/gpt-5\.5/i }); fireEvent.pointerDown(modelButtons[modelButtons.length - 1]); const input = (await screen.findByPlaceholderText("Search or type model ID")) as HTMLInputElement; expect(input.value).toBe("open-codex/gpt-5.5"); fireEvent.change(input, { target: { value: "openai-codex/gpt-5.5" } }); expect(await screen.findByText("“openai-codex/gpt-5.5”")).toBeInTheDocument(); expect( fetchMock.mock.calls.some(([input]) => String(input).startsWith("/api/settings/provider-models"), ), ).toBe(false); }); it("can close the new configuration dialog without trapping the settings page", async () => { vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(settingsPayload()); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "models" }); const configurationButton = await screen.findByRole("button", { name: "Current configuration" }); fireEvent.pointerDown(configurationButton!); fireEvent.click(await screen.findByText("Add configuration")); expect(await screen.findByRole("heading", { name: "New model configuration" })).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Cancel" })); await waitFor(() => expect(screen.queryByRole("heading", { name: "New model configuration" })).not.toBeInTheDocument(), ); expect(document.body.style.pointerEvents).not.toBe("none"); fireEvent.pointerDown(configurationButton!); expect(await screen.findByText("Add configuration")).toBeInTheDocument(); }); it("loads provider models and lets users choose one without typing the id manually", async () => { const payload: SettingsPayload = { ...settingsPayload(), agent: { ...settingsPayload().agent, model: "deepseek-chat", provider: "deepseek", resolved_provider: "deepseek", }, model_presets: [ { ...settingsPayload().model_presets[0], model: "deepseek-chat", provider: "deepseek", }, ], providers: [ { name: "deepseek", label: "DeepSeek", configured: true, auth_type: "api_key", api_key_required: true, api_key_hint: "sk-...", api_base: "https://api.deepseek.com", default_api_base: "https://api.deepseek.com", }, ], }; const updatedPayload: SettingsPayload = { ...payload, agent: { ...payload.agent, model: "deepseek-reasoner", }, model_presets: [ { ...payload.model_presets[0], model: "deepseek-reasoner", }, ], }; const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } if (url === "/api/settings/provider-models?provider=deepseek") { return jsonResponse({ provider: "deepseek", label: "DeepSeek", status: "available", catalog_kind: "official", models: [ { id: "deepseek-chat", owned_by: "deepseek", context_window: 65536 }, { id: "deepseek-reasoner", owned_by: "deepseek", context_window: 65536 }, ], model_count: 2, fetched_at: 1, }); } if (url === "/api/settings/update?model_preset=default&model=deepseek-reasoner") { return jsonResponse(updatedPayload); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView({ initialSection: "models" }); const modelButtons = await screen.findAllByRole("button", { name: /deepseek-chat/i }); fireEvent.pointerDown(modelButtons[modelButtons.length - 1]); await screen.findByText("deepseek-reasoner"); fireEvent.click(screen.getAllByText("deepseek-reasoner")[0]); fireEvent.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( "/api/settings/provider-models?provider=deepseek", expect.objectContaining({ headers: { Authorization: "Bearer tok" }, }), ), ); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( "/api/settings/update?model_preset=default&model=deepseek-reasoner", expect.objectContaining({ headers: { Authorization: "Bearer tok" }, }), ), ); }); it("saves network safety without exposing technical SSRF copy", async () => { const payload = settingsPayload(); const fetchMock = vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") { return jsonResponse({ apps: [], installed_count: 0 }); } if (url === "/api/settings/mcp-presets") { return jsonResponse({ presets: [], installed_count: 0 }); } if (url === "/api/settings/network-safety/update?webui_allow_local_service_access=false&webui_default_access_mode=default") { return jsonResponse({ ...payload, advanced: { ...payload.advanced, webui_allow_local_service_access: false }, requires_restart: true, restart_required_sections: ["runtime"], }); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView({ initialSection: "advanced" }); expect(await screen.findByText("Web safety")).toBeInTheDocument(); expect(screen.queryByText(/SSRF/i)).not.toBeInTheDocument(); expect(screen.queryByText("Private Service Protection")).not.toBeInTheDocument(); expect(screen.getByText("Default access")).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Restricted" })).not.toBeInTheDocument(); expect(screen.getByRole("button", { name: "Default Permission" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Full Access" })).toBeInTheDocument(); fireEvent.click(screen.getByRole("switch", { name: "Local services" })); fireEvent.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( "/api/settings/network-safety/update?webui_allow_local_service_access=false&webui_default_access_mode=default", expect.objectContaining({ headers: { Authorization: "Bearer tok" }, }), ), ); }); it("uses native host safety copy on the native surface", async () => { const payload = { ...settingsPayload(), surface: "native" as const, runtime_surface: "native" as const, }; vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") return jsonResponse({ apps: [], installed_count: 0 }); if (url === "/api/settings/mcp-presets") return jsonResponse({ presets: [], installed_count: 0 }); return { ok: false, status: 404, json: async () => ({}) } as Response; }), ); renderSettingsView({ initialSection: "advanced" }); expect(await screen.findByText("App safety")).toBeInTheDocument(); expect(screen.queryByText("Web safety")).not.toBeInTheDocument(); expect(screen.getByText("Allow Full Access shell commands to reach services on this Mac.")).toBeInTheDocument(); }); it("refreshes settings with a fresh token after native engine restart", async () => { const payload = { ...settingsPayload(), surface: "native" as const, runtime_surface: "native" as const, runtime_capabilities: { can_restart_engine: true, can_pick_folder: true, can_open_logs: true, can_export_diagnostics: true, }, }; const restartedPayload = { ...payload, advanced: { ...payload.advanced, webui_allow_local_service_access: false }, requires_restart: true, restart_required_sections: ["runtime"], }; const refreshedPayload = { ...restartedPayload, requires_restart: false, restart_required_sections: [], }; const restartEngine = vi.fn(async () => "fresh-token"); const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { const url = String(input); const auth = (init?.headers as Record | undefined)?.Authorization; if (url === "/api/settings" && auth === "Bearer fresh-token") { return jsonResponse(refreshedPayload); } if (url === "/api/settings") return jsonResponse(payload); if (url === "/api/settings/cli-apps") return jsonResponse({ apps: [], installed_count: 0 }); if (url === "/api/settings/mcp-presets") return jsonResponse({ presets: [], installed_count: 0 }); if (url === "/api/settings/network-safety/update?webui_allow_local_service_access=false&webui_default_access_mode=default") { return jsonResponse(restartedPayload); } return { ok: false, status: 404, json: async () => ({}) } as Response; }); vi.stubGlobal("fetch", fetchMock); renderSettingsView({ initialSection: "advanced", onNativeEngineRestart: restartEngine, }); expect(await screen.findByText("App safety")).toBeInTheDocument(); fireEvent.click(screen.getByRole("switch", { name: "Local services" })); fireEvent.click(screen.getByRole("button", { name: "Save" })); await waitFor(() => expect(restartEngine).toHaveBeenCalledTimes(1)); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( "/api/settings", expect.objectContaining({ headers: { Authorization: "Bearer fresh-token" }, }), ), ); }); });