175 lines
4.3 KiB
TypeScript
175 lines
4.3 KiB
TypeScript
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
import { flushPromises, mount } from '@vue/test-utils'
|
|
|
|
import UsageView from '../UsageView.vue'
|
|
|
|
const { list, getStats, getSnapshotV2, getById } = vi.hoisted(() => {
|
|
vi.stubGlobal('localStorage', {
|
|
getItem: vi.fn(() => null),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn(),
|
|
})
|
|
|
|
return {
|
|
list: vi.fn(),
|
|
getStats: vi.fn(),
|
|
getSnapshotV2: vi.fn(),
|
|
getById: vi.fn(),
|
|
}
|
|
})
|
|
|
|
const messages: Record<string, string> = {
|
|
'admin.dashboard.day': 'Day',
|
|
'admin.dashboard.hour': 'Hour',
|
|
'admin.usage.failedToLoadUser': 'Failed to load user',
|
|
}
|
|
|
|
vi.mock('@/api/admin', () => ({
|
|
adminAPI: {
|
|
usage: {
|
|
list,
|
|
getStats,
|
|
},
|
|
dashboard: {
|
|
getSnapshotV2,
|
|
},
|
|
users: {
|
|
getById,
|
|
},
|
|
},
|
|
}))
|
|
|
|
vi.mock('@/api/admin/usage', () => ({
|
|
adminUsageAPI: {
|
|
list: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock('@/stores/app', () => ({
|
|
useAppStore: () => ({
|
|
showError: vi.fn(),
|
|
showWarning: vi.fn(),
|
|
showSuccess: vi.fn(),
|
|
showInfo: vi.fn(),
|
|
}),
|
|
}))
|
|
|
|
vi.mock('@/utils/format', () => ({
|
|
formatReasoningEffort: (value: string | null | undefined) => value ?? '-',
|
|
}))
|
|
|
|
vi.mock('vue-i18n', async () => {
|
|
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
|
|
return {
|
|
...actual,
|
|
useI18n: () => ({
|
|
t: (key: string) => messages[key] ?? key,
|
|
}),
|
|
}
|
|
})
|
|
|
|
const AppLayoutStub = { template: '<div><slot /></div>' }
|
|
const UsageFiltersStub = { template: '<div><slot name="after-reset" /></div>' }
|
|
const ModelDistributionChartStub = {
|
|
props: ['metric'],
|
|
emits: ['update:metric'],
|
|
template: `
|
|
<div data-test="model-chart">
|
|
<span class="metric">{{ metric }}</span>
|
|
<button class="switch-metric" @click="$emit('update:metric', 'actual_cost')">switch</button>
|
|
</div>
|
|
`,
|
|
}
|
|
const GroupDistributionChartStub = {
|
|
props: ['metric'],
|
|
emits: ['update:metric'],
|
|
template: `
|
|
<div data-test="group-chart">
|
|
<span class="metric">{{ metric }}</span>
|
|
<button class="switch-metric" @click="$emit('update:metric', 'actual_cost')">switch</button>
|
|
</div>
|
|
`,
|
|
}
|
|
|
|
describe('admin UsageView distribution metric toggles', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
list.mockReset()
|
|
getStats.mockReset()
|
|
getSnapshotV2.mockReset()
|
|
getById.mockReset()
|
|
|
|
list.mockResolvedValue({
|
|
items: [],
|
|
total: 0,
|
|
pages: 0,
|
|
})
|
|
getStats.mockResolvedValue({
|
|
total_requests: 0,
|
|
total_input_tokens: 0,
|
|
total_output_tokens: 0,
|
|
total_cache_tokens: 0,
|
|
total_tokens: 0,
|
|
total_cost: 0,
|
|
total_actual_cost: 0,
|
|
average_duration_ms: 0,
|
|
})
|
|
getSnapshotV2.mockResolvedValue({
|
|
trend: [],
|
|
models: [],
|
|
groups: [],
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('keeps model and group metric toggles independent without refetching chart data', async () => {
|
|
const wrapper = mount(UsageView, {
|
|
global: {
|
|
stubs: {
|
|
AppLayout: AppLayoutStub,
|
|
UsageStatsCards: true,
|
|
UsageFilters: UsageFiltersStub,
|
|
UsageTable: true,
|
|
UsageExportProgress: true,
|
|
UsageCleanupDialog: true,
|
|
UserBalanceHistoryModal: true,
|
|
Pagination: true,
|
|
Select: true,
|
|
Icon: true,
|
|
TokenUsageTrend: true,
|
|
ModelDistributionChart: ModelDistributionChartStub,
|
|
GroupDistributionChart: GroupDistributionChartStub,
|
|
},
|
|
},
|
|
})
|
|
|
|
vi.advanceTimersByTime(120)
|
|
await flushPromises()
|
|
|
|
expect(getSnapshotV2).toHaveBeenCalledTimes(1)
|
|
|
|
const modelChart = wrapper.find('[data-test="model-chart"]')
|
|
const groupChart = wrapper.find('[data-test="group-chart"]')
|
|
|
|
expect(modelChart.find('.metric').text()).toBe('tokens')
|
|
expect(groupChart.find('.metric').text()).toBe('tokens')
|
|
|
|
await modelChart.find('.switch-metric').trigger('click')
|
|
await flushPromises()
|
|
|
|
expect(modelChart.find('.metric').text()).toBe('actual_cost')
|
|
expect(groupChart.find('.metric').text()).toBe('tokens')
|
|
expect(getSnapshotV2).toHaveBeenCalledTimes(1)
|
|
|
|
await groupChart.find('.switch-metric').trigger('click')
|
|
await flushPromises()
|
|
|
|
expect(modelChart.find('.metric').text()).toBe('actual_cost')
|
|
expect(groupChart.find('.metric').text()).toBe('actual_cost')
|
|
expect(getSnapshotV2).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|