Files
aiturk-hermes-ide/apps/desktop/src/app/settings/codex-link-settings.test.tsx
T
yilsemandClaude Opus 5.5 dea67fc153 feat(settings): Codex Bağlama, on by default
Settings → Codex Bağlama runs the bundled bridge for an OpenAI Codex install on
the same computer: the IDE's models and combos appear in Codex and switching
models keeps the conversation. The IDE points Codex's openai_base_url at the
bridge only after the bridge answers, keeps it running across logins, and
removes every trace when the setting is turned off. A Codex that already uses
another base URL is left untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HRDGW1LiKcDUZYUdKBoWYW
2026-10-09 15:59:39 +03:00

194 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<CodexLinkStatus>>()
const set = vi.fn<(patch: { enabled?: boolean; sync?: boolean }) => Promise<CodexLinkStatus>>()
function renderPage(locale: 'en' | 'tr' = 'tr') {
return render(
<I18nProvider configClient={null} initialLocale={locale}>
<CodexLinkSettings />
</I18nProvider>
)
}
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<CodexLinkStatus>(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()
})
})