style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import ElementPlus, { ElMessageBox } 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'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import SupplierLicenseDialog from './SupplierLicenseDialog.vue'
|
||||
import SuppliersView from '../pages/SuppliersView.vue'
|
||||
import SupplierDetailView from '../pages/SupplierDetailView.vue'
|
||||
import { changeSupplierStatus, getSupplier, getSupplierStats, listSuppliers } from '../api/platformSuppliers'
|
||||
import type { Supplier } from '../types'
|
||||
|
||||
const { routerPush, routeControl } = vi.hoisted(() => ({ routerPush: vi.fn(), routeControl: { setId: (_id: string) => {} } }))
|
||||
vi.mock('vue-router', async importOriginal => ({
|
||||
...await importOriginal<typeof import('vue-router')>(),
|
||||
...await (async () => {
|
||||
const { reactive } = await import('vue')
|
||||
const route = reactive({ params: { id: '' } })
|
||||
routeControl.setId = id => { route.params.id = id }
|
||||
return { useRoute: () => route }
|
||||
})(),
|
||||
useRouter: () => ({ push: routerPush }),
|
||||
}))
|
||||
vi.mock('../api/platformSuppliers', () => ({ getSupplier: vi.fn(), getSupplierStats: vi.fn(), listSuppliers: vi.fn(), changeSupplierStatus: vi.fn() }))
|
||||
|
||||
const privateId = '9007199254740993123'
|
||||
let record: Supplier
|
||||
function supplier(): Supplier {
|
||||
return { id: privateId, tenantId: '0', name: '供应商测试公司', category: 'BOOKING', truckingScope: null, carrierCodes: ['MATSON'], region: '宁波', legalRepresentative: '测试法人', contactName: '联系人', phone: '13800000000', email: null, address: '供应商测试地址', businessLicenseNo: '91440300MA5ABCDE12', licenseFileId: 'private-file-id', status: 'ACTIVE', settlementPeriod: 'MONTHLY_30', remark: null, createdBy: null, createdAt: '', updatedBy: null, updatedAt: '', deleted: false, version: 2 }
|
||||
}
|
||||
function rowButton(text: string) {
|
||||
const found = Array.from(document.querySelectorAll<HTMLButtonElement>('.el-table__body button')).find(button => button.textContent?.trim() === text)
|
||||
expect(found, text).toBeDefined()
|
||||
return found!
|
||||
}
|
||||
function messageBoxButton(text: string) {
|
||||
const found = Array.from(document.querySelectorAll<HTMLButtonElement>('.el-message-box button')).find(button => button.textContent?.trim() === text)
|
||||
expect(found, text).toBeDefined()
|
||||
return found!
|
||||
}
|
||||
async function openList() {
|
||||
const wrapper = mount(SuppliersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
return wrapper
|
||||
}
|
||||
enableAutoUnmount(afterEach)
|
||||
afterEach(() => { ElMessageBox.close() })
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
document.body.innerHTML = ''
|
||||
setActivePinia(createPinia())
|
||||
const auth = useAuthStore()
|
||||
auth.token = 'test-session'
|
||||
auth.permissions = ['platform:supplier:list', 'platform:supplier:edit', 'platform:supplier:create', 'platform:supplier:status']
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
record = supplier()
|
||||
routeControl.setId(privateId)
|
||||
vi.mocked(getSupplier).mockImplementation(async () => record)
|
||||
vi.mocked(getSupplierStats).mockResolvedValue({ booking: 1, customs: 0, trucking: 0, certificate: 0 })
|
||||
vi.mocked(listSuppliers).mockImplementation(async () => ({ records: [record], page: 1, size: 20, total: 1, pages: 1 }))
|
||||
vi.mocked(changeSupplierStatus).mockImplementation(async (_id, status) => { record = { ...record, status }; return null })
|
||||
})
|
||||
|
||||
describe('supplier shared dialogs', () => {
|
||||
it('shows all linked license facts with a truthful image limitation and no synthetic certificate', async () => {
|
||||
const name = '供应商完整长名称'.repeat(12)
|
||||
const wrapper = mount(SupplierLicenseDialog, { attachTo: document.body, props: { modelValue: true, supplier: { ...record, name } }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
const dialog = wrapper.findComponent(AppDialog)
|
||||
expect(dialog.props()).toMatchObject({ title: `营业执照查看 · ${name}`, width: 'min(560px, calc(100vw - 24px))', showConfirm: false, cancelText: '关闭' })
|
||||
const facts = document.querySelector('[aria-label="营业执照信息"] table')!
|
||||
expect(facts.querySelectorAll('tr')).toHaveLength(4)
|
||||
expect(facts.textContent).toContain(name)
|
||||
expect(facts.textContent).toContain('测试法人')
|
||||
expect(facts.textContent).toContain(record.businessLicenseNo)
|
||||
expect(facts.textContent).toContain('已关联')
|
||||
expect(facts.textContent).not.toContain(privateId)
|
||||
expect(facts.textContent).not.toContain('private-file-id')
|
||||
expect(document.querySelector('.el-dialog [role="alert"]')?.textContent).toContain('暂无法展示原始影像')
|
||||
expect(document.querySelector('.el-dialog img, .el-dialog iframe')).toBeNull()
|
||||
expect(document.querySelector('.license-card')).toBeNull()
|
||||
expect(document.querySelector('.el-dialog')?.textContent).not.toContain('副本')
|
||||
})
|
||||
|
||||
it('keeps missing facts explicit and handles an absent supplier without an ID fallback', async () => {
|
||||
const wrapper = mount(SupplierLicenseDialog, { attachTo: document.body, props: { modelValue: true, supplier: { ...record, name: '', legalRepresentative: '', businessLicenseNo: null, licenseFileId: null } }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('title')).toBe('营业执照查看')
|
||||
expect(document.querySelector('[aria-label="营业执照信息"] table')?.textContent).toContain('未上传')
|
||||
expect(document.querySelectorAll('.license-info-table td')[2]?.textContent).toBe('—')
|
||||
expect(document.querySelector('.el-dialog [role="alert"]')).toBeNull()
|
||||
await wrapper.setProps({ supplier: null }); await flushPromises()
|
||||
expect(document.querySelector('.el-dialog')?.textContent).toContain('暂无营业执照资料')
|
||||
expect(document.querySelector('.license-info-table')).toBeNull()
|
||||
})
|
||||
|
||||
it('opens the list license entry and closes through its original footer', async () => {
|
||||
const wrapper = await openList()
|
||||
rowButton('查看').click(); await flushPromises()
|
||||
const dialog = wrapper.findComponent(AppDialog)
|
||||
expect(dialog.props('modelValue')).toBe(true)
|
||||
expect(document.querySelector('.license-info-table')?.textContent).toContain(record.businessLicenseNo)
|
||||
document.querySelector<HTMLButtonElement>('.el-dialog__footer button')!.click(); await flushPromises()
|
||||
expect(dialog.props('modelValue')).toBe(false)
|
||||
expect(changeSupplierStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('closes an existing license preview when the detail route changes', async () => {
|
||||
vi.mocked(getSupplier).mockImplementation(async id => id === privateId ? record : { ...record, id, name: '另一供应商' })
|
||||
const wrapper = mount(SupplierDetailView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
const preview = wrapper.findAll('button').find(button => button.text().includes('查看营业执照'))!
|
||||
await preview.trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(true)
|
||||
routeControl.setId('another-supplier'); await flushPromises()
|
||||
expect(getSupplier).toHaveBeenLastCalledWith('another-supplier')
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(false)
|
||||
expect(wrapper.get('.density-detail-title').text()).toBe('另一供应商')
|
||||
})
|
||||
|
||||
it.each(['ACTIVE', 'INACTIVE'] as const)('identifies the %s status confirmation and sends the original status request only after confirmation', async status => {
|
||||
record = { ...record, status }
|
||||
const verb = status === 'ACTIVE' ? '停用' : '启用'
|
||||
const next = status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE'
|
||||
await openList()
|
||||
rowButton(verb).click(); await flushPromises()
|
||||
expect(document.querySelector('.el-message-box__title')?.textContent).toBe(`${verb}供应商 · 供应商测试公司`)
|
||||
expect(document.querySelector('.el-message-box')?.classList.contains('admin-density-confirm')).toBe(true)
|
||||
expect(changeSupplierStatus).not.toHaveBeenCalled()
|
||||
messageBoxButton(`确认${verb}`).click(); await flushPromises()
|
||||
expect(changeSupplierStatus).toHaveBeenCalledWith(privateId, next)
|
||||
expect(listSuppliers).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('cancels a status change without sending a write request', async () => {
|
||||
await openList()
|
||||
rowButton('停用').click(); await flushPromises()
|
||||
messageBoxButton('取消').click(); await flushPromises()
|
||||
expect(changeSupplierStatus).not.toHaveBeenCalled()
|
||||
expect(listSuppliers).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('keeps status actions hidden when the current account has only read permission', async () => {
|
||||
useAuthStore().permissions = ['platform:supplier:list']
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
await openList()
|
||||
const labels = Array.from(document.querySelectorAll<HTMLButtonElement>('.el-table__body button')).map(button => button.textContent?.trim())
|
||||
expect(labels).not.toContain('停用')
|
||||
expect(labels).not.toContain('启用')
|
||||
expect(labels).not.toContain('编辑')
|
||||
expect(labels).toContain('查看')
|
||||
expect(changeSupplierStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('retains the list and resets action loading if the status update fails', async () => {
|
||||
vi.mocked(changeSupplierStatus).mockRejectedValueOnce(new Error('network'))
|
||||
await openList()
|
||||
rowButton('停用').click(); await flushPromises()
|
||||
messageBoxButton('确认停用').click(); await flushPromises()
|
||||
expect(changeSupplierStatus).toHaveBeenCalledWith(privateId, 'INACTIVE')
|
||||
expect(rowButton('停用').classList.contains('is-loading')).toBe(false)
|
||||
expect(document.body.textContent).toContain('停用供应商失败')
|
||||
expect(listSuppliers).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user