优化
This commit is contained in:
@@ -4,6 +4,7 @@ import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
downloadPlatformExportTask,
|
||||
getPlatformExportTask,
|
||||
listPlatformExportTasks,
|
||||
retryPlatformExportTask,
|
||||
type PlatformExportTask,
|
||||
@@ -14,6 +15,7 @@ import DownloadCenterView from './DownloadCenterView.vue'
|
||||
|
||||
vi.mock('@/modules/downloads/api/platformExportTasks', () => ({
|
||||
downloadPlatformExportTask: vi.fn(),
|
||||
getPlatformExportTask: vi.fn(),
|
||||
listPlatformExportTasks: vi.fn(),
|
||||
retryPlatformExportTask: vi.fn(),
|
||||
}))
|
||||
@@ -38,6 +40,7 @@ afterEach(() => vi.useRealTimers())
|
||||
describe('DownloadCenterView', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
document.body.innerHTML = ''
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
|
||||
Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: vi.fn(() => 'blob:export') })
|
||||
Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: vi.fn() })
|
||||
@@ -50,6 +53,7 @@ describe('DownloadCenterView', () => {
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
vi.mocked(listPlatformExportTasks).mockResolvedValue({ records: [task], page: 1, size: 20, total: 1, pages: 1 })
|
||||
vi.mocked(downloadPlatformExportTask).mockResolvedValue(new Blob(['xlsx']))
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue(task)
|
||||
vi.mocked(retryPlatformExportTask).mockResolvedValue({ ...task, status: 'PENDING' })
|
||||
})
|
||||
|
||||
@@ -70,6 +74,11 @@ describe('DownloadCenterView', () => {
|
||||
|
||||
expect(downloadPlatformExportTask).toHaveBeenCalledWith('9')
|
||||
expect(URL.createObjectURL).toHaveBeenCalled()
|
||||
expect(HTMLAnchorElement.prototype.click).toHaveBeenCalledOnce()
|
||||
const clickedAnchor = vi.mocked(HTMLAnchorElement.prototype.click).mock.instances[0] as HTMLAnchorElement
|
||||
expect(clickedAnchor.download).toBe('客户列表.xlsx')
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 0))
|
||||
expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:export')
|
||||
})
|
||||
|
||||
it('shows retry only for failed tasks with the source export permission', async () => {
|
||||
@@ -89,6 +98,83 @@ describe('DownloadCenterView', () => {
|
||||
expect(retryPlatformExportTask).toHaveBeenCalledWith('9')
|
||||
})
|
||||
|
||||
it('loads a fresh task detail from the backend', async () => {
|
||||
const wrapper = mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
const button = [...document.querySelectorAll<HTMLButtonElement>('button')]
|
||||
.find((item) => item.textContent?.trim() === '详情')
|
||||
button?.click()
|
||||
await flushPromises()
|
||||
|
||||
expect(getPlatformExportTask).toHaveBeenCalledWith('9')
|
||||
expect(document.body.textContent).toContain('任务详情 · EXP9')
|
||||
expect(document.body.textContent).toContain('客户列表.xlsx')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('renders an actionable list error and can recover', async () => {
|
||||
vi.mocked(listPlatformExportTasks)
|
||||
.mockRejectedValueOnce(new Error('network down'))
|
||||
.mockResolvedValueOnce({ records: [task], page: 1, size: 20, total: 1, pages: 1 })
|
||||
mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
expect(document.querySelector('[role="alert"]')?.textContent).toContain('下载任务暂时无法加载')
|
||||
const reload = [...document.querySelectorAll<HTMLButtonElement>('button')]
|
||||
.find((item) => item.textContent?.trim() === '重新加载')
|
||||
reload?.click()
|
||||
await flushPromises()
|
||||
|
||||
expect(listPlatformExportTasks).toHaveBeenCalledTimes(2)
|
||||
expect(document.body.textContent).toContain('共 1 个任务')
|
||||
})
|
||||
|
||||
it('shows a clear empty state and hides retry without the source export permission', async () => {
|
||||
vi.mocked(listPlatformExportTasks).mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 })
|
||||
const pinia = setActivePinia(createPinia())
|
||||
const auth = useAuthStore()
|
||||
auth.token = 'token'
|
||||
auth.permissions = ['platform:download:list']
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
expect(document.body.textContent).toContain('暂无符合筛选条件的下载任务')
|
||||
expect([...document.querySelectorAll<HTMLButtonElement>('button')]
|
||||
.some((item) => item.textContent?.trim() === '重试')).toBe(false)
|
||||
})
|
||||
|
||||
it('does not expose customer retry without the customer export permission', async () => {
|
||||
vi.mocked(listPlatformExportTasks).mockResolvedValue({
|
||||
records: [{ ...task, status: 'FAILED', failureMessage: 'IAM timeout' }],
|
||||
page: 1, size: 20, total: 1, pages: 1,
|
||||
})
|
||||
const pinia = setActivePinia(createPinia())
|
||||
const auth = useAuthStore()
|
||||
auth.token = 'token'
|
||||
auth.permissions = ['platform:download:list']
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
expect(document.body.textContent).toContain('IAM timeout')
|
||||
expect([...document.querySelectorAll<HTMLButtonElement>('button')]
|
||||
.some((item) => item.textContent?.trim() === '重试')).toBe(false)
|
||||
})
|
||||
|
||||
it('announces the initial loading state while the task request is pending', async () => {
|
||||
let resolveRequest!: (value: { records: PlatformExportTask[]; page: number; size: number; total: number; pages: number }) => void
|
||||
vi.mocked(listPlatformExportTasks).mockReturnValue(new Promise((resolve) => { resolveRequest = resolve }))
|
||||
const wrapper = mount(DownloadCenterView, { global: { plugins: [createPinia(), ElementPlus] } })
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.find('.table-panel').attributes('aria-busy')).toBe('true')
|
||||
resolveRequest({ records: [], page: 1, size: 20, total: 0, pages: 0 })
|
||||
await flushPromises()
|
||||
expect(wrapper.find('.table-panel').attributes('aria-busy')).toBe('false')
|
||||
})
|
||||
|
||||
it('does not schedule another refresh after an in-flight load finishes on an unmounted page', async () => {
|
||||
vi.useFakeTimers()
|
||||
let resolveRequest!: (value: { records: PlatformExportTask[]; page: number; size: number; total: number; pages: number }) => void
|
||||
@@ -102,4 +188,42 @@ describe('DownloadCenterView', () => {
|
||||
|
||||
expect(listPlatformExportTasks).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('keeps the last successful task list and continues polling after a background refresh failure', async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.mocked(listPlatformExportTasks)
|
||||
.mockResolvedValueOnce({ records: [{ ...task, status: 'RUNNING', processedRows: 3 }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
.mockRejectedValueOnce(new Error('temporary network failure'))
|
||||
.mockResolvedValueOnce({ records: [{ ...task, status: 'SUCCEEDED', processedRows: 12 }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(5000)
|
||||
await flushPromises()
|
||||
expect(document.body.textContent).toContain('自动刷新暂时失败')
|
||||
expect(document.body.textContent).toContain('客户列表')
|
||||
|
||||
await vi.advanceTimersByTimeAsync(5000)
|
||||
await flushPromises()
|
||||
expect(listPlatformExportTasks).toHaveBeenCalledTimes(3)
|
||||
expect(document.body.textContent).not.toContain('自动刷新暂时失败')
|
||||
})
|
||||
|
||||
it('updates an open task detail from the latest polling result', async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.mocked(listPlatformExportTasks)
|
||||
.mockResolvedValueOnce({ records: [{ ...task, status: 'RUNNING', processedRows: 3 }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
.mockResolvedValueOnce({ records: [{ ...task, status: 'SUCCEEDED', processedRows: 12 }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue({ ...task, status: 'RUNNING', processedRows: 3 })
|
||||
mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [createPinia(), ElementPlus] } })
|
||||
await flushPromises()
|
||||
;[...document.querySelectorAll<HTMLButtonElement>('button')]
|
||||
.find((item) => item.textContent?.trim() === '详情')?.click()
|
||||
await flushPromises()
|
||||
expect(document.body.textContent).toContain('3 / 12 行')
|
||||
|
||||
await vi.advanceTimersByTimeAsync(5000)
|
||||
await flushPromises()
|
||||
expect(document.body.textContent).toContain('12 / 12 行')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user