import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { I18nProvider } from '@/i18n/context' import type { CodexLinkStatus } from '../../../electron/codex-link-types' import { CodexLinkSettings } from './codex-link-settings' const notifyError = vi.hoisted(() => vi.fn()) vi.mock('@/store/notifications', () => ({ notifyError })) const RUNNING: CodexLinkStatus = { enabled: true, codexFound: true, venvReady: true, foreign: false, bridgeRunning: true, ideDurum: 'tamam', ideModel: 12, ozet: true, lastError: null } const get = vi.fn<() => Promise>() const set = vi.fn<(patch: { enabled?: boolean; sync?: boolean }) => Promise>() function renderPage(locale: 'en' | 'tr' = 'tr') { return render( ) } function statusTexts() { return screen.getAllByRole('listitem').map(item => item.textContent) } beforeEach(() => { ;(window as unknown as { hermesDesktop: unknown }).hermesDesktop = { codexLink: { get, set } } }) afterEach(() => { cleanup() vi.resetAllMocks() delete (window as unknown as { hermesDesktop?: unknown }).hermesDesktop }) describe('Codex Link settings', () => { it('explains the feature and shows a healthy link in Turkish', async () => { get.mockResolvedValue(RUNNING) renderPage() expect(await screen.findByText('Köprü çalışıyor.')).toBeTruthy() expect(statusTexts()).toEqual(['Bu bilgisayarda Codex bulundu.', 'Köprü çalışıyor.', "Codex'e 12 model eklendi."]) expect( screen.getByText( "AITURK IDE'deki modelleri ve comboları bu bilgisayardaki Codex'e ekler; modeller arasında geçerken sohbet bağlamını korur." ) ).toBeTruthy() expect(screen.getByText("Codex'te yeni modelleri görmek için Codex'i yeniden açın.")).toBeTruthy() expect(screen.getByRole('switch', { name: 'Codex Bağlama' }).getAttribute('aria-checked')).toBe('true') expect(set).not.toHaveBeenCalled() }) it('renders in English for other locales', async () => { get.mockResolvedValue(RUNNING) renderPage('en') expect(await screen.findByText('The bridge is running.')).toBeTruthy() expect(statusTexts()).toEqual([ 'Codex was found on this computer.', 'The bridge is running.', '12 models added to Codex.' ]) expect(screen.getByRole('button', { name: 'Sync now' })).toBeTruthy() }) it('says only that Codex is missing when it is not installed', async () => { get.mockResolvedValue({ ...RUNNING, codexFound: false, bridgeRunning: false, ideDurum: null, ideModel: null }) renderPage() expect(await screen.findByText('Bu bilgisayarda Codex bulunamadı.')).toBeTruthy() expect(statusTexts()).toEqual(['Bu bilgisayarda Codex bulunamadı.']) }) it('says another bridge is in charge and does not claim ours is running', async () => { get.mockResolvedValue({ ...RUNNING, foreign: true }) renderPage() expect(await screen.findByText(/başka bir köprü kullanıyor/)).toBeTruthy() expect(statusTexts()).toEqual([ 'Bu bilgisayarda Codex bulundu.', 'Bu bilgisayarda Codex başka bir köprü kullanıyor; ayarına dokunulmadı.' ]) }) it('says it is waiting for the IDE setup', async () => { get.mockResolvedValue({ ...RUNNING, venvReady: false, bridgeRunning: false, ideDurum: null, ideModel: null }) renderPage() expect(await screen.findByText('IDE kurulumu tamamlanınca etkinleşecek.')).toBeTruthy() expect(screen.queryByText('Köprü çalışmıyor.')).toBeNull() }) it('asks for a sign-in when the bridge has no IDE session', async () => { get.mockResolvedValue({ ...RUNNING, ideDurum: 'anahtar-yok', ideModel: 0 }) renderPage() expect(await screen.findByText('IDE oturumu bulunamadı, giriş yapın.')).toBeTruthy() expect(screen.queryByText(/model eklendi/)).toBeNull() }) it('shows a stopped bridge together with the last error', async () => { get.mockResolvedValue({ ...RUNNING, bridgeRunning: false, ideDurum: null, ideModel: null, lastError: 'bridge-start-failed' }) renderPage() expect(await screen.findByText('Köprü çalışmıyor.')).toBeTruthy() expect(screen.getByText('Köprü başlatılamadı; Codex ayarına dokunulmadı.')).toBeTruthy() }) it('turns the link off through the bridge and adopts the reply', async () => { get.mockResolvedValue(RUNNING) set.mockResolvedValue({ ...RUNNING, enabled: false, bridgeRunning: false, ideDurum: null, ideModel: null }) renderPage() await screen.findByText('Köprü çalışıyor.') fireEvent.click(screen.getByRole('switch', { name: 'Codex Bağlama' })) expect(set).toHaveBeenCalledWith({ enabled: false }) expect(await screen.findByText('Codex Bağlama kapalı. Codex kendi ayarlarıyla çalışıyor.')).toBeTruthy() expect(screen.getByRole('switch', { name: 'Codex Bağlama' }).getAttribute('aria-checked')).toBe('false') }) it('rolls the switch back and reports when the change fails', async () => { get.mockResolvedValue(RUNNING) set.mockRejectedValue(new Error('codex-link:unavailable')) renderPage() await screen.findByText('Köprü çalışıyor.') fireEvent.click(screen.getByRole('switch', { name: 'Codex Bağlama' })) await waitFor(() => expect(notifyError).toHaveBeenCalledWith(expect.any(Error), 'Codex Bağlama değiştirilemedi.')) expect(screen.getByRole('switch', { name: 'Codex Bağlama' }).getAttribute('aria-checked')).toBe('true') expect(screen.getByText('Köprü çalışıyor.')).toBeTruthy() }) it('syncs on demand and keeps the button disabled while it runs', async () => { get.mockResolvedValue({ ...RUNNING, bridgeRunning: false, ideDurum: null, ideModel: null }) let finish: (status: CodexLinkStatus) => void = () => undefined set.mockReturnValue( new Promise(resolve => { finish = resolve }) ) renderPage() await screen.findByText('Köprü çalışmıyor.') fireEvent.click(screen.getByRole('button', { name: 'Şimdi eşitle' })) expect(set).toHaveBeenCalledTimes(1) expect(set).toHaveBeenCalledWith({ sync: true }) const pending = screen.getByRole('button', { name: 'Eşitleniyor…' }) as HTMLButtonElement expect(pending.disabled).toBe(true) fireEvent.click(pending) expect(set).toHaveBeenCalledTimes(1) finish(RUNNING) expect(await screen.findByText('Köprü çalışıyor.')).toBeTruthy() expect((screen.getByRole('button', { name: 'Şimdi eşitle' }) as HTMLButtonElement).disabled).toBe(false) }) it('offers nothing to click where the desktop bridge is absent', () => { delete (window as unknown as { hermesDesktop?: unknown }).hermesDesktop renderPage() expect(screen.getByText('Codex Bağlama yalnızca Windows masaüstü uygulamasında kullanılabilir.')).toBeTruthy() expect(screen.queryByRole('switch')).toBeNull() expect(get).not.toHaveBeenCalled() }) })