Files
deepseek-harness/packages/client/ui-model/tests/model-select.spec.tsx
T
Yichen Jiang fee12f1af0 refactor(llm-deepseek)!: rename the provider route to deepseek-official
The native adapter's route was named deepseek, colliding with pi-ai's
catalog provider of the same name, so the two DeepSeek paths could never
be mounted side by side. The web settings page needs both configurable at
once. Compositions, fixtures, goldens, scaffolding defaults, and docs all
move together (pre-release, no shim); TUI/session-query-spill/
missing-credential goldens re-recorded through their keyless refresh
modes because provider-name length shifts box padding and spill
truncation points.
2026-07-29 16:36:07 +08:00

96 lines
3.2 KiB
TypeScript

// @vitest-environment jsdom
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { ModelTarget } from '@deepseek-ai/dsh-client-connection/client'
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
import type { ModelDirectoryState } from '../src/client/directory.ts'
import { ModelSelect } from '../src/client/ModelSelect.tsx'
const reasoning = {
efforts: [
{ id: 'off', name: 'Off' },
{ id: 'high', name: 'High' },
{ id: 'max', name: 'Max', description: 'Largest budget' },
],
defaultEffort: 'high',
}
function state(overrides: Partial<ModelDirectoryState> = {}): ModelDirectoryState {
return {
current: { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
groups: [{
id: 'deepseek-official',
name: 'DeepSeek',
models: [{ id: 'deepseek-v4-flash', name: 'DeepSeek-V4-Flash', reasoning }],
}],
failures: [],
status: 'ready',
error: null,
...overrides,
}
}
afterEach(cleanup)
describe('ModelSelect reasoning effort', () => {
it('renders adapter metadata and submits the effort as part of the session target', async () => {
const directory = createSnapshotStore(state())
const select = vi.fn(async (target: ModelTarget) => {
directory.update((snapshot) => { snapshot.current = target })
return true
})
render(<ModelSelect
locked={false}
directory={directory}
load={vi.fn()}
select={select}
/>)
const trigger = screen.getByRole('button', {
name: '选择模型,当前 DeepSeek-V4-Flash,推理等级 High',
})
fireEvent.click(trigger)
fireEvent.click(screen.getByRole('menuitem', { name: /Effort/ }))
expect(screen.getAllByRole('menuitemradio').map(item => item.textContent))
.toEqual(['Off', 'High', 'MaxLargest budget'])
fireEvent.click(screen.getByRole('menuitemradio', { name: /Max/ }))
await waitFor(() => {
expect(select).toHaveBeenCalledWith({
provider: 'deepseek-official',
model: 'deepseek-v4-flash',
reasoningEffort: 'max',
})
expect(trigger.getAttribute('aria-label')).toBe('选择模型,当前 DeepSeek-V4-Flash,推理等级 Max')
})
})
it('offers provider default only when the adapter does not configure a model default', () => {
const directory = createSnapshotStore(state({
groups: [{
id: 'provider',
name: 'Provider',
models: [{
id: 'model',
name: 'Model',
reasoning: { efforts: [{ id: 'standard', name: 'Standard' }] },
}],
}],
current: { provider: 'provider', model: 'model' },
}))
render(<ModelSelect
locked={false}
directory={directory}
load={vi.fn()}
select={vi.fn().mockResolvedValue(true)}
/>)
fireEvent.click(screen.getByRole('button', {
name: '选择模型,当前 Model,推理等级 Provider default',
}))
fireEvent.click(screen.getByRole('menuitem', { name: /Effort/ }))
expect(screen.getAllByRole('menuitemradio').map(item => item.textContent))
.toEqual(['Provider default', 'Standard'])
})
})