Download desktop/src/components/controls/ModelSelector.test.tsx from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 6.55 kB
-
https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/controls/ModelSelector.test.tsx
- Command line
-
hf download hf://chenbhao/codev@e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/controls/ModelSelector.test.tsx
-
curl -L -o ModelSelector.test.tsx https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/controls/ModelSelector.test.tsx
6.55 kB
| import { afterEach, describe, expect, it, vi } from 'vitest' | |
| import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' | |
| import '@testing-library/jest-dom' | |
| import { ModelSelector } from './ModelSelector' | |
| import { useChatStore } from '../../stores/chatStore' | |
| import { useProviderStore } from '../../stores/providerStore' | |
| import { useSessionRuntimeStore } from '../../stores/sessionRuntimeStore' | |
| import { useSettingsStore } from '../../stores/settingsStore' | |
| import { OPENAI_OFFICIAL_PROVIDER_ID } from '../../constants/openaiOfficialProvider' | |
| import type { ModelInfo } from '../../types/settings' | |
| const MODELS: ModelInfo[] = [ | |
| { id: 'alpha', name: 'Alpha', description: 'Fast model', context: '128k' }, | |
| { id: 'beta', name: 'Beta', description: 'Careful model', context: '200k' }, | |
| ] | |
| async function clickByRole(name: RegExp | string) { | |
| await act(async () => { | |
| fireEvent.click(screen.getByRole('button', { name })) | |
| await Promise.resolve() | |
| }) | |
| } | |
| afterEach(() => { | |
| cleanup() | |
| useSettingsStore.setState(useSettingsStore.getInitialState(), true) | |
| useProviderStore.setState(useProviderStore.getInitialState(), true) | |
| useSessionRuntimeStore.setState(useSessionRuntimeStore.getInitialState(), true) | |
| useChatStore.setState(useChatStore.getInitialState(), true) | |
| }) | |
| describe('ModelSelector', () => { | |
| it('uses controlled model selection without mutating settings directly', async () => { | |
| const onChange = vi.fn() | |
| useSettingsStore.setState({ | |
| locale: 'en', | |
| availableModels: MODELS, | |
| currentModel: MODELS[0], | |
| }) | |
| render(<ModelSelector value="alpha" onChange={onChange} />) | |
| await clickByRole(/alpha/i) | |
| await clickByRole(/Beta/) | |
| expect(onChange).toHaveBeenCalledWith('beta') | |
| }) | |
| it('routes uncontrolled model and effort changes through settings actions', async () => { | |
| const setModel = vi.fn(async () => {}) | |
| const setEffort = vi.fn(async () => {}) | |
| useSettingsStore.setState({ | |
| locale: 'en', | |
| availableModels: MODELS, | |
| currentModel: MODELS[0], | |
| effortLevel: 'medium', | |
| setModel, | |
| setEffort, | |
| }) | |
| render(<ModelSelector />) | |
| await clickByRole(/alpha/i) | |
| await clickByRole(/Beta/) | |
| expect(setModel).toHaveBeenCalledWith('beta') | |
| await clickByRole(/Alpha/) | |
| await clickByRole(/^High$/) | |
| expect(setEffort).toHaveBeenCalledWith('high') | |
| }) | |
| it('selects provider-scoped runtime models and mirrors session selections', async () => { | |
| const setSessionRuntime = vi.fn() | |
| useSettingsStore.setState({ | |
| locale: 'en', | |
| availableModels: MODELS, | |
| currentModel: MODELS[0], | |
| activeProviderName: 'Provider A', | |
| }) | |
| useProviderStore.setState({ | |
| providers: [{ | |
| id: 'provider-a', | |
| presetId: 'custom', | |
| name: 'Provider A', | |
| apiKey: '***', | |
| baseUrl: 'https://api.example.com', | |
| apiFormat: 'anthropic', | |
| models: { | |
| main: 'provider-main', | |
| haiku: 'provider-fast', | |
| sonnet: 'provider-main', | |
| opus: '', | |
| }, | |
| }], | |
| activeId: 'provider-a', | |
| hasLoadedProviders: true, | |
| isLoading: true, | |
| }) | |
| useChatStore.setState({ | |
| setSessionRuntime, | |
| } as Partial<ReturnType<typeof useChatStore.getState>>) | |
| render(<ModelSelector runtimeKey="session-1" />) | |
| await clickByRole(/alpha/i) | |
| await act(async () => { | |
| fireEvent.click(screen.getByRole('button', { name: /provider-fast/ })) | |
| await Promise.resolve() | |
| }) | |
| expect(useSessionRuntimeStore.getState().selections['session-1']).toEqual({ | |
| providerId: 'provider-a', | |
| modelId: 'provider-fast', | |
| }) | |
| expect(setSessionRuntime).toHaveBeenCalledWith('session-1', { | |
| providerId: 'provider-a', | |
| modelId: 'provider-fast', | |
| }) | |
| }) | |
| it('uses the ChatGPT Official catalog when that built-in provider is active', async () => { | |
| const openAIModels: ModelInfo[] = [ | |
| { | |
| id: 'gpt-5.3-codex', | |
| name: 'GPT-5.3 Codex', | |
| description: 'Best for coding and agentic work', | |
| context: '', | |
| }, | |
| { | |
| id: 'gpt-5.5', | |
| name: 'GPT-5.5', | |
| description: 'Latest general-purpose model', | |
| context: '', | |
| }, | |
| ] | |
| const setSessionRuntime = vi.fn() | |
| useSettingsStore.setState({ | |
| locale: 'en', | |
| availableModels: openAIModels, | |
| currentModel: openAIModels[0], | |
| activeProviderName: 'ChatGPT Official', | |
| }) | |
| useProviderStore.setState({ | |
| providers: [], | |
| activeId: OPENAI_OFFICIAL_PROVIDER_ID, | |
| hasLoadedProviders: true, | |
| isLoading: true, | |
| }) | |
| useChatStore.setState({ | |
| setSessionRuntime, | |
| } as Partial<ReturnType<typeof useChatStore.getState>>) | |
| render(<ModelSelector runtimeKey="session-openai" />) | |
| await clickByRole(/GPT-5\.3 Codex/i) | |
| await act(async () => { | |
| fireEvent.click(screen.getByRole('button', { name: /GPT-5\.5/ })) | |
| await Promise.resolve() | |
| }) | |
| expect(useSessionRuntimeStore.getState().selections['session-openai']).toEqual({ | |
| providerId: OPENAI_OFFICIAL_PROVIDER_ID, | |
| modelId: 'gpt-5.5', | |
| }) | |
| expect(setSessionRuntime).toHaveBeenCalledWith('session-openai', { | |
| providerId: OPENAI_OFFICIAL_PROVIDER_ID, | |
| modelId: 'gpt-5.5', | |
| }) | |
| }) | |
| it('portals the dropdown outside clipping containers and positions it below the trigger', async () => { | |
| useSettingsStore.setState({ | |
| locale: 'en', | |
| availableModels: MODELS, | |
| currentModel: MODELS[0], | |
| }) | |
| const { container } = render( | |
| <div data-testid="scroll-container" className="overflow-hidden"> | |
| <ModelSelector value="alpha" onChange={vi.fn()} /> | |
| </div>, | |
| ) | |
| const trigger = screen.getByRole('button', { name: /alpha/i }) | |
| Object.defineProperty(trigger.parentElement, 'getBoundingClientRect', { | |
| configurable: true, | |
| value: () => ({ | |
| top: 120, | |
| right: 520, | |
| bottom: 150, | |
| left: 240, | |
| width: 280, | |
| height: 30, | |
| x: 240, | |
| y: 120, | |
| toJSON: () => {}, | |
| }), | |
| }) | |
| await act(async () => { | |
| fireEvent.click(trigger) | |
| await Promise.resolve() | |
| }) | |
| const dropdown = screen.getByTestId('model-selector-dropdown') | |
| expect(container.contains(dropdown)).toBe(false) | |
| expect(document.body.contains(dropdown)).toBe(true) | |
| expect(dropdown.className).toContain('fixed') | |
| expect(dropdown.style.top).toBe('158px') | |
| expect(dropdown.style.left).toBe('160px') | |
| expect(dropdown.style.width).toBe('360px') | |
| }) | |
| }) | |