style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import ElementPlus, { ElDialog } from 'element-plus'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
import DownloadCenterView from './DownloadCenterView.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
|
||||
vi.mock('@/modules/downloads/api/platformExportTasks', () => ({
|
||||
downloadPlatformExportTask: vi.fn(),
|
||||
@@ -58,6 +60,21 @@ describe('DownloadCenterView', () => {
|
||||
vi.mocked(retryPlatformExportTask).mockResolvedValue({ ...task, status: 'PENDING' })
|
||||
})
|
||||
|
||||
const dialogButton = (label: string) => {
|
||||
const found = Array.from(document.querySelectorAll<HTMLButtonElement>('.el-dialog__footer button')).find(button => button.textContent?.trim() === label)
|
||||
expect(found, label).toBeDefined(); return found!
|
||||
}
|
||||
const factValue = (label: string) => {
|
||||
const term = Array.from(document.querySelectorAll('[aria-label="下载任务资料"] dt')).find(node => node.textContent === label)
|
||||
expect(term, label).toBeDefined(); return term!.nextElementSibling!.textContent
|
||||
}
|
||||
async function openTask(value: PlatformExportTask = task) {
|
||||
vi.mocked(listPlatformExportTasks).mockResolvedValue({ records: [value], total: 1, page: 1, size: 20, pages: 1 })
|
||||
const wrapper = mount(DownloadCenterView, { attachTo: document.body, global: { plugins: [ElementPlus] } }); await flushPromises()
|
||||
const open = () => Array.from(document.querySelectorAll<HTMLButtonElement>('.el-table__body .table-actions button')).find(button => button.textContent?.trim() === '详情')!.click()
|
||||
open(); await flushPromises(); return { wrapper, open }
|
||||
}
|
||||
|
||||
it('lists completed tasks and downloads the backend-generated artifact', async () => {
|
||||
const pinia = setActivePinia(createPinia())
|
||||
const auth = useAuthStore()
|
||||
@@ -290,4 +307,99 @@ describe('DownloadCenterView', () => {
|
||||
await flushPromises()
|
||||
expect(document.body.textContent).toContain('12 / 12 行')
|
||||
})
|
||||
|
||||
it('keeps long facts and zero values complete through fullscreen and never uses a requester ID fallback', async () => {
|
||||
const value: PlatformExportTask = { ...task, id: '9223372036854775807', requestedBy: '9223372036854775806', requestedUsername: null, taskNo: 'EXP-FULL-001', taskName: '完整导出任务名称'.repeat(25), fileName: '完整导出文件名称'.repeat(25) + '.xlsx', processedRows: 0, totalRows: 0, sizeBytes: 0 }
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue(value)
|
||||
const { wrapper } = await openTask(value)
|
||||
const dialog = wrapper.findComponent(AppDialog)
|
||||
expect(dialog.props()).toMatchObject({ title: '任务详情 · EXP-FULL-001', width: 'min(720px, calc(100vw - 28px))', fullscreenable: true, showConfirm: false })
|
||||
expect(document.querySelector('[aria-label="下载任务资料"]')?.textContent).toContain(value.taskName)
|
||||
expect(factValue('文件名称')).toBe(value.fileName)
|
||||
expect(factValue('申请人')).toBe('—')
|
||||
expect(factValue('处理数据')).toBe('0 / 0 行')
|
||||
expect(factValue('文件大小')).toBe('0 B')
|
||||
expect(document.body.textContent).not.toContain(value.requestedBy)
|
||||
expect(document.body.textContent).not.toContain(value.id)
|
||||
document.querySelector<HTMLButtonElement>('[aria-label="展开全屏"]')!.click(); await flushPromises()
|
||||
expect(dialog.findComponent(ElDialog).props('fullscreen')).toBe(true)
|
||||
expect(factValue('文件名称')).toBe(value.fileName)
|
||||
dialogButton('下载文件').click(); await flushPromises()
|
||||
expect(downloadPlatformExportTask).toHaveBeenCalledWith(value.id)
|
||||
})
|
||||
|
||||
it.each([
|
||||
['RUNNING', '处理中', false], ['PENDING', '等待重试', true], ['EXPIRED', '已过期', false],
|
||||
] as const)('shows %s facts and original retry/download eligibility', async (status,label,retry) => {
|
||||
const value: PlatformExportTask = { ...task, status, attemptCount: 2, processedRows: 3, totalRows: null, sizeBytes: null, fileName: null, nextRetryAt: '2026-10-20T12:00:00' }
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue(value)
|
||||
await openTask(value)
|
||||
expect(document.querySelector('.task-summary-heading')?.textContent).toContain(label)
|
||||
expect(factValue('处理数据')).toBe('3 行'); expect(factValue('文件大小')).toBe('—'); expect(factValue('文件名称')).toBe('—')
|
||||
expect(dialogButton('下载文件').disabled).toBe(true)
|
||||
if (retry) {
|
||||
expect(factValue('下次重试')).not.toBe('—')
|
||||
dialogButton('立即重试').click(); await flushPromises()
|
||||
expect(retryPlatformExportTask).toHaveBeenCalledWith(value.id)
|
||||
} else {
|
||||
expect(document.querySelector('.el-dialog__footer')?.textContent).not.toContain('重试')
|
||||
expect(factValue('下次重试')).toBe('—')
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves the full failure reason when retry fails and refreshes the same task after retry succeeds', async () => {
|
||||
const failed: PlatformExportTask = { ...task, status: 'FAILED', failureMessage: '完整失败原因'.repeat(90) }
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue(failed)
|
||||
vi.mocked(retryPlatformExportTask).mockRejectedValueOnce(new Error('重试失败')).mockResolvedValueOnce({ ...failed, status: 'PENDING' })
|
||||
await openTask(failed)
|
||||
expect(document.querySelector('.task-failure')?.textContent).toContain(failed.failureMessage)
|
||||
dialogButton('重试').click(); await flushPromises()
|
||||
expect(document.querySelector('.task-failure')?.textContent).toContain(failed.failureMessage)
|
||||
expect(dialogButton('重试').disabled).toBe(false)
|
||||
vi.mocked(listPlatformExportTasks).mockResolvedValue({ records: [{ ...failed, status: 'PENDING', attemptCount: 0, failureMessage: null }], total: 1, page: 1, size: 20, pages: 1 })
|
||||
dialogButton('重试').click(); await flushPromises()
|
||||
expect(retryPlatformExportTask).toHaveBeenCalledTimes(2)
|
||||
expect(document.querySelector('.task-summary-heading')?.textContent).toContain('等待处理')
|
||||
expect(document.querySelector('.task-failure')).toBeNull()
|
||||
expect(dialogButton('下载文件').disabled).toBe(true)
|
||||
})
|
||||
|
||||
it('shows a compact detail error with diagnostics and restores actions only after reloading', async () => {
|
||||
vi.mocked(getPlatformExportTask).mockRejectedValueOnce(new ApiResponseError('详情读取失败', 'A0500', 500, null, 'req-download-detail')).mockResolvedValueOnce(task)
|
||||
await openTask()
|
||||
expect(document.querySelector('.el-dialog .load-error-state')?.textContent).toContain('req-download-detail')
|
||||
expect(document.querySelector('.el-dialog .load-error-state')?.classList.contains('is-compact')).toBe(true)
|
||||
expect(dialogButton('下载文件').disabled).toBe(true)
|
||||
const reload = Array.from(document.querySelectorAll<HTMLButtonElement>('.el-dialog button')).find(button => button.textContent?.trim() === '重新加载')!
|
||||
reload.click(); await flushPromises()
|
||||
expect(getPlatformExportTask).toHaveBeenCalledTimes(2)
|
||||
expect(document.querySelector('.el-dialog .load-error-state')).toBeNull()
|
||||
expect(dialogButton('下载文件').disabled).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps actions disabled while detail is loading and ignores an old response after close and reopen', async () => {
|
||||
let release!: (value: PlatformExportTask) => void
|
||||
vi.mocked(getPlatformExportTask).mockImplementationOnce(() => new Promise(resolve => { release = resolve })).mockResolvedValueOnce({ ...task, taskNo: 'EXP-NEW', taskName: '最新任务详情' })
|
||||
const { wrapper, open } = await openTask()
|
||||
expect(wrapper.findComponent(AppDialog).props('loading')).toBe(true)
|
||||
expect(dialogButton('下载文件').disabled).toBe(true)
|
||||
dialogButton('关闭').click(); await flushPromises()
|
||||
wrapper.findComponent(AppDialog).vm.$emit('closed'); await flushPromises()
|
||||
open(); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('title')).toBe('任务详情 · EXP-NEW')
|
||||
release({ ...task, taskName: '过期响应' }); await flushPromises()
|
||||
expect(document.querySelector('.task-summary-heading')?.textContent).toContain('最新任务详情')
|
||||
expect(document.querySelector('.task-summary-heading')?.textContent).not.toContain('过期响应')
|
||||
expect(wrapper.findComponent(AppDialog).props('loading')).toBe(false)
|
||||
})
|
||||
|
||||
it('shows a missing business number as — without exposing the internal task ID', async () => {
|
||||
const value: PlatformExportTask = { ...task, id: '9223372036854775807', taskNo: '' }
|
||||
vi.mocked(getPlatformExportTask).mockResolvedValue(value)
|
||||
const { wrapper } = await openTask(value)
|
||||
expect(wrapper.findComponent(AppDialog).props('title')).toBe('任务详情')
|
||||
expect(factValue('任务编号')).toBe('—')
|
||||
expect(document.querySelector('.el-dialog')?.textContent).not.toContain(value.id)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user