Files
sub2api/frontend/src/views/admin/__tests__/ChannelMonitorView.grok.spec.ts
T
Randark 5cbd0c96a2 fix(monitor-ui): 关联账号选择器改服务端搜索+回填,OpenAI 配额模式加消耗提示
- 选择器改为 /admin/accounts?platform=&search= 服务端搜索(50 条/页,
  300ms 防抖由 Select 承担),AbortController + 序列守卫防乱序覆盖
- 已绑定账号不在结果页时用 getById 回填为固定选项,删除「不在列表即
  静默清空绑定」;绑定仅在显式切换 provider 或账号确实失效时清空(带提示)
- OpenAI 平台配额模式显示 Codex 探测额度消耗提示;checkModeQuotaHint
  修正为「不发送探活请求」不再宣称「不产生 LLM 请求」
- grok spec 补齐缺失的 accounts mock;新增账号选择器 7 用例
  (加载/防抖搜索/回填保留/失效清空/提示门控/固定选项/切平台清空)
2026-08-18 09:14:44 +00:00

156 lines
5.3 KiB
TypeScript

import { defineComponent } from 'vue'
import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import MonitorFormDialog from '@/components/admin/monitor/MonitorFormDialog.vue'
import {
DEFAULT_GROK_ENDPOINT,
DEFAULT_GROK_MODEL,
PROVIDERS,
PROVIDER_GROK,
} from '@/constants/channelMonitor'
const { listTemplates, accountsList, accountsGetById } = vi.hoisted(() => ({
listTemplates: vi.fn(),
accountsList: vi.fn(),
accountsGetById: vi.fn(),
}))
vi.mock('@/utils/featureFlags', () => ({
isChannelMonitorV1Mode: () => true,
isChannelMonitorV2Mode: () => false,
getChannelMonitorMode: () => 'v1' as const,
}))
vi.mock('@/features/channel-monitor-v2/MonitorSettingsPanel.vue', () => ({
default: { name: 'MonitorSettingsPanel', template: '<div data-testid="v2-settings" />' },
}))
vi.mock('@/api/admin', () => ({
adminAPI: {
channelMonitor: {
create: vi.fn(),
update: vi.fn(),
},
channelMonitorTemplate: {
list: listTemplates,
},
accounts: {
list: (...args: unknown[]) => accountsList(...args),
getById: (...args: unknown[]) => accountsGetById(...args),
},
},
}))
vi.mock('@/api/keys', () => ({
keysAPI: { list: vi.fn() },
}))
vi.mock('@/api/groups', () => ({
userGroupsAPI: { getUserGroupRates: vi.fn() },
}))
vi.mock('@/stores/app', () => ({
useAppStore: () => ({
cachedPublicSettings: null,
showError: vi.fn(),
showSuccess: vi.fn(),
}),
}))
vi.mock('vue-i18n', async () => {
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
return {
...actual,
useI18n: () => ({ t: (key: string) => key }),
}
})
const BaseDialogStub = defineComponent({
props: { show: { type: Boolean, default: false } },
template: '<div v-if="show"><slot /><slot name="footer" /></div>',
})
function mountDialog() {
return mount(MonitorFormDialog, {
props: { show: true, monitor: null },
global: {
stubs: {
BaseDialog: BaseDialogStub,
Toggle: true,
Select: true,
ModelTagInput: true,
MonitorKeyPickerDialog: true,
MonitorAdvancedRequestConfig: true,
},
},
})
}
describe('channel monitor Grok provider', () => {
beforeEach(() => {
listTemplates.mockReset().mockResolvedValue({ items: [] })
accountsList.mockReset().mockResolvedValue({ items: [] })
accountsGetById.mockReset()
})
it('offers Grok in the responsive provider grid and prefills its official defaults', async () => {
const wrapper = mountDialog()
await flushPromises()
expect(PROVIDERS).toContain(PROVIDER_GROK)
const providerButtons = wrapper.findAll('[data-testid^="monitor-provider-"]')
expect(providerButtons).toHaveLength(8)
expect(providerButtons[0].element.parentElement?.className).toContain('grid-cols-2')
expect(providerButtons[0].element.parentElement?.className).toContain('sm:grid-cols-4')
const grokButton = wrapper.get('[data-testid="monitor-provider-grok"]')
expect(grokButton.find('svg').exists()).toBe(true)
expect(grokButton.text()).toContain('monitorCommon.providers.grok')
await grokButton.trigger('click')
expect(grokButton.classes().join(' ')).toContain('zinc')
const endpoint = wrapper.get('[data-testid="monitor-endpoint"]')
const model = wrapper.get('[data-testid="monitor-primary-model"]')
expect((endpoint.element as HTMLInputElement).value).toBe(DEFAULT_GROK_ENDPOINT)
expect((model.element as HTMLInputElement).value).toBe(DEFAULT_GROK_MODEL)
await wrapper.get('[data-testid="monitor-provider-anthropic"]').trigger('click')
expect((endpoint.element as HTMLInputElement).value).toBe('')
expect((model.element as HTMLInputElement).value).toBe('')
await grokButton.trigger('click')
await endpoint.setValue('https://gateway.example.com')
await model.setValue('grok-custom')
await wrapper.get('[data-testid="monitor-provider-openai"]').trigger('click')
expect((endpoint.element as HTMLInputElement).value).toBe('https://gateway.example.com')
expect((model.element as HTMLInputElement).value).toBe('grok-custom')
})
it('prefills only empty Grok fields and preserves existing provider values', async () => {
const wrapper = mountDialog()
await flushPromises()
const endpoint = wrapper.get('[data-testid="monitor-endpoint"]')
const model = wrapper.get('[data-testid="monitor-primary-model"]')
const grokButton = wrapper.get('[data-testid="monitor-provider-grok"]')
const anthropicButton = wrapper.get('[data-testid="monitor-provider-anthropic"]')
await endpoint.setValue('https://gateway.example.com')
await grokButton.trigger('click')
expect((endpoint.element as HTMLInputElement).value).toBe('https://gateway.example.com')
expect((model.element as HTMLInputElement).value).toBe(DEFAULT_GROK_MODEL)
await anthropicButton.trigger('click')
expect((endpoint.element as HTMLInputElement).value).toBe('https://gateway.example.com')
expect((model.element as HTMLInputElement).value).toBe('')
await endpoint.setValue('')
await model.setValue('grok-custom')
await grokButton.trigger('click')
expect((endpoint.element as HTMLInputElement).value).toBe(DEFAULT_GROK_ENDPOINT)
expect((model.element as HTMLInputElement).value).toBe('grok-custom')
})
})