mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-20 00:18:55 +00:00
fix(usage): keep export filters consistent across pages
This commit is contained in:
@@ -641,9 +641,10 @@ const exportToCSV = async () => {
|
||||
try {
|
||||
const allLogs: UsageLog[] = []
|
||||
const pageSize = 100
|
||||
const exportParams = buildUsageListParams(1, pageSize)
|
||||
const totalPages = Math.ceil(pagination.total / pageSize)
|
||||
for (let page = 1; page <= totalPages; page++) {
|
||||
const response = await usageAPI.query(buildUsageListParams(page, pageSize))
|
||||
const response = await usageAPI.query({ ...exportParams, page })
|
||||
allLogs.push(...response.items)
|
||||
}
|
||||
if (allLogs.length === 0) {
|
||||
@@ -696,7 +697,7 @@ const exportToCSV = async () => {
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `usage_${startDate.value}_to_${endDate.value}.csv`
|
||||
link.download = `usage_${exportParams.start_date}_to_${exportParams.end_date}.csv`
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
appStore.showSuccess(t('usage.exportSuccess'))
|
||||
|
||||
@@ -3,6 +3,8 @@ import { flushPromises, mount } from '@vue/test-utils'
|
||||
|
||||
import UsageView from '../UsageView.vue'
|
||||
import Select, { type SelectOption } from '@/components/common/Select.vue'
|
||||
import DateRangePicker from '@/components/common/DateRangePicker.vue'
|
||||
import UsageTable from '@/components/admin/usage/UsageTable.vue'
|
||||
|
||||
const {
|
||||
query,
|
||||
@@ -413,6 +415,68 @@ describe('user UsageView', () => {
|
||||
clickSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('keeps the initial filters, sort, and filename while exporting multiple pages', async () => {
|
||||
const pageResponse = { items: [usageLog], total: 101, pages: 2 }
|
||||
query.mockResolvedValue(pageResponse)
|
||||
const wrapper = mountUsageView()
|
||||
await flushPromises()
|
||||
|
||||
const datePicker = wrapper.findComponent(DateRangePicker)
|
||||
datePicker.vm.$emit('change', { startDate: '2026-03-01', endDate: '2026-03-08', preset: null })
|
||||
await flushPromises()
|
||||
|
||||
let resolveFirstPage!: (value: typeof pageResponse) => void
|
||||
const firstPage = new Promise<typeof pageResponse>((resolve) => { resolveFirstPage = resolve })
|
||||
query.mockClear()
|
||||
query.mockImplementation((params, options) =>
|
||||
!options && params.page === 1 ? firstPage : Promise.resolve(pageResponse)
|
||||
)
|
||||
const originalCreateObjectURL = window.URL.createObjectURL
|
||||
const originalRevokeObjectURL = window.URL.revokeObjectURL
|
||||
window.URL.createObjectURL = vi.fn(() => 'blob:usage-export')
|
||||
window.URL.revokeObjectURL = vi.fn()
|
||||
let filename = ''
|
||||
const clickSpy = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(function () {
|
||||
filename = this.download
|
||||
})
|
||||
|
||||
try {
|
||||
await wrapper.findAll('button').find((button) => button.text() === 'Export CSV')!.trigger('click')
|
||||
const initialParams = { ...query.mock.calls[0][0] }
|
||||
expect(initialParams).toMatchObject({
|
||||
page: 1, page_size: 100, start_date: '2026-03-01', end_date: '2026-03-08',
|
||||
sort_by: 'created_at', sort_order: 'desc',
|
||||
})
|
||||
|
||||
const keySelect = wrapper.findAllComponents(Select).find((select) =>
|
||||
select.props('options').some((option: SelectOption) => option.label === 'All API Keys')
|
||||
)!
|
||||
keySelect.vm.$emit('update:modelValue', 1)
|
||||
keySelect.vm.$emit('change', 1)
|
||||
datePicker.vm.$emit('change', { startDate: '2026-04-01', endDate: '2026-04-08', preset: null })
|
||||
wrapper.findComponent(UsageTable).vm.$emit('sort', 'actual_cost', 'asc')
|
||||
await flushPromises()
|
||||
expect(query).toHaveBeenCalledWith(expect.objectContaining({
|
||||
api_key_id: 1, start_date: '2026-04-01', end_date: '2026-04-08',
|
||||
sort_by: 'actual_cost', sort_order: 'asc',
|
||||
}), expect.anything())
|
||||
|
||||
resolveFirstPage(pageResponse)
|
||||
await flushPromises()
|
||||
|
||||
const exportCalls = query.mock.calls.filter((call) => call.length === 1)
|
||||
expect.soft(exportCalls).toEqual([[initialParams], [{ ...initialParams, page: 2 }]])
|
||||
expect.soft(filename).toBe('usage_2026-03-01_to_2026-03-08.csv')
|
||||
expect(showSuccess).toHaveBeenCalledWith('Export success')
|
||||
expect(showError).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
window.URL.createObjectURL = originalCreateObjectURL
|
||||
window.URL.revokeObjectURL = originalRevokeObjectURL
|
||||
clickSpy.mockRestore()
|
||||
wrapper.unmount()
|
||||
}
|
||||
})
|
||||
|
||||
it('exports historical image rows with image billing mode derived from image_count', async () => {
|
||||
query.mockResolvedValue({
|
||||
items: [
|
||||
|
||||
Reference in New Issue
Block a user