mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-06 08:49:24 +00:00
- 选择器改为 /admin/accounts?platform=&search= 服务端搜索(50 条/页, 300ms 防抖由 Select 承担),AbortController + 序列守卫防乱序覆盖 - 已绑定账号不在结果页时用 getById 回填为固定选项,删除「不在列表即 静默清空绑定」;绑定仅在显式切换 provider 或账号确实失效时清空(带提示) - OpenAI 平台配额模式显示 Codex 探测额度消耗提示;checkModeQuotaHint 修正为「不发送探活请求」不再宣称「不产生 LLM 请求」 - grok spec 补齐缺失的 accounts mock;新增账号选择器 7 用例 (加载/防抖搜索/回填保留/失效清空/提示门控/固定选项/切平台清空)
156 lines
5.3 KiB
TypeScript
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')
|
|
})
|
|
})
|