import { beforeEach, describe, expect, it, vi } from 'vitest' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' import '@testing-library/jest-dom' import { ComputerUseSettings } from './ComputerUseSettings' import { useSettingsStore } from '../stores/settingsStore' import { browserHost } from '../lib/desktopHost/browserHost' const computerUseApiMock = vi.hoisted(() => ({ getStatus: vi.fn(), getInstalledApps: vi.fn(), getAuthorizedApps: vi.fn(), setAuthorizedApps: vi.fn(), runSetup: vi.fn(), openSettings: vi.fn(), })) vi.mock('../api/computerUse', () => ({ computerUseApi: computerUseApiMock, })) const readyStatus = { platform: 'darwin', supported: true, python: { installed: true, version: '3.12.0', path: '/usr/bin/python3', source: 'system', error: null, }, venv: { created: false, path: '/tmp/venv', }, dependencies: { installed: false, requirementsFound: true, }, permissions: { accessibility: null, screenRecording: null, }, } const enabledConfig = { enabled: true, authorizedApps: [], grantFlags: { clipboardRead: true, clipboardWrite: true, systemKeyCombos: true, }, pythonPath: null, } function deferred() { let resolve!: (value: T) => void const promise = new Promise(res => { resolve = res }) return { promise, resolve } } describe('ComputerUseSettings', () => { beforeEach(() => { useSettingsStore.setState({ locale: 'en' }) computerUseApiMock.getStatus.mockReset() computerUseApiMock.getInstalledApps.mockReset() computerUseApiMock.getAuthorizedApps.mockReset() computerUseApiMock.setAuthorizedApps.mockReset() computerUseApiMock.runSetup.mockReset() computerUseApiMock.openSettings.mockReset() Reflect.deleteProperty(window, 'desktopHost') computerUseApiMock.getStatus.mockResolvedValue(readyStatus) computerUseApiMock.getAuthorizedApps.mockResolvedValue(enabledConfig) computerUseApiMock.setAuthorizedApps.mockResolvedValue({ ok: true }) }) it('renders the stored disabled state with the MCP exposure hint', async () => { computerUseApiMock.getAuthorizedApps.mockResolvedValue({ ...enabledConfig, enabled: false, }) render() const toggle = await screen.findByLabelText('Enabled') await waitFor(() => expect(toggle).not.toBeChecked()) expect( screen.getByText(/will not inject the computer-use MCP server/i), ).toBeInTheDocument() }) it('saves the Computer Use enablement toggle independently', async () => { render() const toggle = await screen.findByLabelText('Enabled') await waitFor(() => expect(computerUseApiMock.getAuthorizedApps).toHaveBeenCalled()) await act(async () => { fireEvent.click(toggle) await Promise.resolve() }) expect(computerUseApiMock.setAuthorizedApps).toHaveBeenCalledWith({ enabled: false, }) }) it('saves a custom Python interpreter path and rechecks status', async () => { render() const input = await screen.findByLabelText('Python Interpreter Path') await act(async () => { fireEvent.change(input, { target: { value: ' C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe ' }, }) fireEvent.click(screen.getByText('Apply')) await Promise.resolve() }) expect(computerUseApiMock.setAuthorizedApps).toHaveBeenCalledWith({ pythonPath: 'C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe', }) expect(computerUseApiMock.getStatus).toHaveBeenCalledTimes(2) }) it('selects a custom Python interpreter through the injected desktop host', async () => { const open = vi.fn().mockResolvedValue('/opt/python/bin/python3') window.desktopHost = { ...browserHost, kind: 'electron', isDesktop: true, capabilities: { ...browserHost.capabilities, dialogs: true, }, dialogs: { ...browserHost.dialogs, open, }, } render() await screen.findByLabelText('Python Interpreter Path') await act(async () => { fireEvent.click(screen.getByText('Browse')) await Promise.resolve() }) expect(open).toHaveBeenCalledWith({ multiple: false, directory: false, title: 'Select Python Interpreter', }) expect(computerUseApiMock.setAuthorizedApps).toHaveBeenCalledWith({ pythonPath: '/opt/python/bin/python3', }) }) it('falls back to manual Python path entry when dialogs are unavailable', async () => { window.desktopHost = { ...browserHost, kind: 'browser', isDesktop: false, capabilities: { ...browserHost.capabilities, dialogs: false, }, } render() await screen.findByLabelText('Python Interpreter Path') await act(async () => { fireEvent.click(screen.getByText('Browse')) await Promise.resolve() }) expect(screen.getByText('Could not open the file picker. Paste the path manually.')).toBeInTheDocument() expect(computerUseApiMock.setAuthorizedApps).not.toHaveBeenCalled() }) it('keeps the user-selected enablement when a stale refresh resolves later', async () => { const staleRefresh = deferred() computerUseApiMock.getStatus.mockResolvedValue({ ...readyStatus, venv: { ...readyStatus.venv, created: true, }, dependencies: { ...readyStatus.dependencies, installed: true, }, }) computerUseApiMock.getInstalledApps.mockResolvedValue({ apps: [] }) computerUseApiMock.getAuthorizedApps .mockResolvedValueOnce({ ...enabledConfig, enabled: false, }) .mockReturnValueOnce(staleRefresh.promise) render() const toggle = await screen.findByLabelText('Enabled') await waitFor(() => expect(toggle).not.toBeChecked()) await waitFor(() => expect(computerUseApiMock.getInstalledApps).toHaveBeenCalled()) await act(async () => { fireEvent.click(toggle) await Promise.resolve() }) expect(toggle).toBeChecked() await act(async () => { staleRefresh.resolve({ ...enabledConfig, enabled: false, }) await staleRefresh.promise }) expect(toggle).toBeChecked() }) it('saves app and grant flag changes from the ready environment view', async () => { computerUseApiMock.getStatus.mockResolvedValue({ ...readyStatus, venv: { ...readyStatus.venv, created: true, }, dependencies: { ...readyStatus.dependencies, installed: true, }, }) computerUseApiMock.getInstalledApps.mockResolvedValue({ apps: [ { bundleId: 'com.example.Preview', displayName: 'Preview', path: '/Applications/Preview.app', }, ], }) render() await screen.findByText('Preview') await act(async () => { fireEvent.click(screen.getByText('Preview')) await Promise.resolve() }) expect(computerUseApiMock.setAuthorizedApps).toHaveBeenCalledWith({ authorizedApps: [ expect.objectContaining({ bundleId: 'com.example.Preview', displayName: 'Preview', }), ], grantFlags: { clipboardRead: true, clipboardWrite: true, systemKeyCombos: true, }, }) await act(async () => { fireEvent.click(screen.getByLabelText('Clipboard Access')) await Promise.resolve() }) expect(computerUseApiMock.setAuthorizedApps).toHaveBeenCalledWith({ authorizedApps: [ expect.objectContaining({ bundleId: 'com.example.Preview', displayName: 'Preview', }), ], grantFlags: { clipboardRead: false, clipboardWrite: false, systemKeyCombos: true, }, }) }) })