style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -1,17 +1,22 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||||
import QueryToolbar from '@/shared/components/QueryToolbar.vue'
|
||||
import { getPlatformAuditLog, listPlatformAuditLogs } from '../api/platformAudit'
|
||||
import { getPlatformAuditLog, listPlatformAuditLogs, type PlatformAuditLogDetail } from '../api/platformAudit'
|
||||
import CustomerAuditLogsPanel from './CustomerAuditLogsPanel.vue'
|
||||
|
||||
vi.mock('../api/platformAudit', () => ({ getPlatformAuditLog: vi.fn(), listPlatformAuditLogs: vi.fn() }))
|
||||
|
||||
const row = { id: '1', eventId: 'evt', eventType: 'PLATFORM_TENANT_UPDATE', resourceType: 'TENANT', resourceId: '8', description: '编辑客户', accountId: '8', operatorTenantId: '0', userId: '9', username: 'platform_admin', appCode: 'ADMIN', clientType: 'WEB', clientIp: '127.0.0.1', requestId: 'req', traceId: 'trace', serviceName: 'iam-service', success: true, errorCode: null, errorMessage: null, occurredAt: '2026-08-13T01:00:00Z', createdAt: '2026-08-13T09:00:00' }
|
||||
|
||||
const detail: PlatformAuditLogDetail = { ...row, resourceId: '9007199254740993123', httpMethod: 'PUT', requestUri: '/tenants/8', durationMs: 12, beforeData: '{"name":"旧名称"}', afterData: '{"name":"新名称"}' }
|
||||
|
||||
enableAutoUnmount(afterEach)
|
||||
|
||||
function mountPanel(permissions: string[]) {
|
||||
const pinia = createPinia(); setActivePinia(pinia)
|
||||
const auth = useAuthStore(); auth.token = 'token'; auth.permissions = permissions
|
||||
@@ -20,7 +25,7 @@ function mountPanel(permissions: string[]) {
|
||||
}
|
||||
|
||||
describe('CustomerAuditLogsPanel', () => {
|
||||
beforeEach(() => { vi.clearAllMocks(); vi.mocked(listPlatformAuditLogs).mockResolvedValue({ records: [row], page: 1, size: 20, total: 1, pages: 1 }) })
|
||||
beforeEach(() => { vi.resetAllMocks(); vi.mocked(getPlatformAuditLog).mockResolvedValue(detail); vi.mocked(listPlatformAuditLogs).mockResolvedValue({ records: [row], page: 1, size: 20, total: 1, pages: 1 }) })
|
||||
|
||||
it('queries logs with the current customer id', async () => {
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
@@ -91,17 +96,24 @@ describe('CustomerAuditLogsPanel', () => {
|
||||
})
|
||||
|
||||
it('loads detail and formats json snapshots', async () => {
|
||||
vi.mocked(getPlatformAuditLog).mockResolvedValue({ ...row, eventType: 'INBOUND_MEASURE_APPROVE', resourceType: 'INBOUND_MEASUREMENT', serviceName: 'firstleg-service', httpMethod: 'PUT', requestUri: '/tenants/8', clientIp: '127.0.0.1', durationMs: 12, beforeData: '{"name":"旧名称"}', afterData: '{"name":"新名称"}' })
|
||||
const privateId = '9007199254740993123'
|
||||
vi.mocked(listPlatformAuditLogs).mockResolvedValue({ records: [{ ...row, id: privateId }], page: 1, size: 20, total: 1, pages: 1 })
|
||||
vi.mocked(getPlatformAuditLog).mockResolvedValue({ ...detail, id: privateId, eventType: 'INBOUND_MEASURE_APPROVE', resourceType: 'INBOUND_MEASUREMENT', serviceName: 'firstleg-service', httpMethod: 'PUT', requestUri: '/tenants/8', clientIp: '127.0.0.1', durationMs: 12, beforeData: '{"name":"旧名称"}', afterData: '{"name":"新名称"}' })
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('button[aria-label="查看操作日志1详情"]').trigger('click'); await flushPromises()
|
||||
expect(getPlatformAuditLog).toHaveBeenCalledWith('1')
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
expect(getPlatformAuditLog).toHaveBeenCalledWith(privateId)
|
||||
expect(wrapper.text()).toContain('旧名称')
|
||||
expect(wrapper.text()).toContain('新名称')
|
||||
expect(wrapper.text()).toContain('入库测量审核通过')
|
||||
expect(wrapper.text()).toContain('入库测量')
|
||||
expect(wrapper.text()).toContain('头程履约服务')
|
||||
expect(wrapper.text()).toContain('更新')
|
||||
expect(wrapper.find('.audit-detail-grid').exists()).toBe(true)
|
||||
expect(wrapper.find('.audit-detail-table').exists()).toBe(true)
|
||||
expect(wrapper.findComponent(AppDrawer).props()).toMatchObject({ title: '操作日志详情 · 柏悦物流', size: 'min(920px, 96vw)', showFooter: false })
|
||||
expect(wrapper.find('.audit-detail-table').text()).toContain('req')
|
||||
expect(wrapper.find('.audit-detail-table').text()).toContain('trace')
|
||||
expect(wrapper.find('.audit-detail-table').text()).not.toContain(privateId)
|
||||
expect(wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').attributes('aria-label')).not.toContain(privateId)
|
||||
})
|
||||
|
||||
it('loads the selected audit page', async () => {
|
||||
@@ -131,4 +143,100 @@ describe('CustomerAuditLogsPanel', () => {
|
||||
expect(listPlatformAuditLogs).toHaveBeenCalledTimes(1)
|
||||
expect(listPlatformAuditLogs).toHaveBeenCalledWith({ accountId: '9', page: 1, size: 20, keyword: undefined, success: undefined, occurredFrom: undefined, occurredTo: undefined })
|
||||
})
|
||||
it('preserves failure details and complete long or non-json snapshots as text', async () => {
|
||||
const longText = '长内容'.repeat(1200)
|
||||
const raw = '<script>原始内容</script>\n不是JSON'
|
||||
vi.mocked(getPlatformAuditLog).mockResolvedValue({ ...detail, success: false, errorMessage: '数据版本冲突', beforeData: JSON.stringify({ note: longText }), afterData: raw })
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
const drawer = wrapper.findComponent(AppDrawer)
|
||||
expect(drawer.text()).toContain('操作失败')
|
||||
expect(drawer.get('[role="alert"]').text()).toContain('数据版本冲突')
|
||||
const snapshots = drawer.findAll('.density-code-block')
|
||||
expect(snapshots[0].text()).toBe(JSON.stringify({ note: longText }, null, 2))
|
||||
expect(snapshots[1].text()).toBe(raw)
|
||||
expect(drawer.find('script').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps missing snapshots compact and a zero duration visible', async () => {
|
||||
vi.mocked(getPlatformAuditLog).mockResolvedValue({ ...detail, success: false, errorMessage: null, errorCode: 'OPERATION_CONFLICT', durationMs: 0, beforeData: '', afterData: null, requestId: null, traceId: null })
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
expect(wrapper.findAll('.density-code-block').map(snapshot => snapshot.text())).toEqual(['无', '无'])
|
||||
expect(wrapper.find('.audit-detail-table').text()).toContain('0 ms')
|
||||
expect(wrapper.find('.audit-detail-table').text()).not.toContain('链路标识')
|
||||
expect(wrapper.find('.audit-detail-table').text()).toContain('OPERATION_CONFLICT')
|
||||
expect(wrapper.findComponent(AppDrawer).get('[role="alert"]').text()).toContain('操作失败,请查看诊断信息')
|
||||
expect(wrapper.findComponent(AppDrawer).get('[role="alert"]').text()).not.toContain('OPERATION_CONFLICT')
|
||||
})
|
||||
|
||||
it('retries the selected log after a detail request failure', async () => {
|
||||
vi.mocked(getPlatformAuditLog).mockRejectedValueOnce(new Error('network')).mockResolvedValueOnce(detail)
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDrawer).text()).toContain('详情加载失败')
|
||||
await wrapper.findComponent(AppDrawer).get('.load-error-state button').trigger('click'); await flushPromises()
|
||||
expect(vi.mocked(getPlatformAuditLog).mock.calls).toEqual([['1'], ['1']])
|
||||
expect(wrapper.findComponent(AppDrawer).text()).toContain('新名称')
|
||||
expect(wrapper.findComponent(AppDrawer).find('.load-error-state').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it.each(['resolve', 'reject'] as const)('ignores a late %s from the previously selected log', async outcome => {
|
||||
let finish!: (value: PlatformAuditLogDetail) => void
|
||||
let fail!: (reason: Error) => void
|
||||
const previous = new Promise<PlatformAuditLogDetail>((resolve, reject) => { finish = resolve; fail = reject })
|
||||
vi.mocked(listPlatformAuditLogs).mockResolvedValue({ records: [row, { ...row, id: '2', description: '第二条记录' }], page: 1, size: 20, total: 2, pages: 1 })
|
||||
vi.mocked(getPlatformAuditLog).mockImplementation(id => id === '1' ? previous : Promise.resolve({ ...detail, id: '2', description: '第二条详情' }))
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
const actions = wrapper.findAll('.el-table__body button[aria-label^="查看操作日志:"]')
|
||||
await actions[0].trigger('click'); await flushPromises()
|
||||
await actions[1].trigger('click'); await flushPromises()
|
||||
if (outcome === 'resolve') finish({ ...detail, description: '不应重新展示的旧详情' })
|
||||
else fail(new Error('旧请求失败'))
|
||||
await flushPromises()
|
||||
expect(wrapper.findComponent(AppDrawer).props('loading')).toBe(false)
|
||||
expect(wrapper.findComponent(AppDrawer).text()).toContain('第二条详情')
|
||||
expect(wrapper.findComponent(AppDrawer).text()).not.toContain('不应重新展示的旧详情')
|
||||
expect(wrapper.findComponent(AppDrawer).find('.load-error-state').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('closes and clears pending detail when switching customers', async () => {
|
||||
let finish!: (value: PlatformAuditLogDetail) => void
|
||||
vi.mocked(getPlatformAuditLog).mockReturnValueOnce(new Promise(resolve => { finish = resolve }))
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
await wrapper.setProps({ customerId: '9', customerName: '另一客户' }); await flushPromises()
|
||||
finish(detail); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDrawer).props('modelValue')).toBe(false)
|
||||
expect(wrapper.find('.audit-detail-table').exists()).toBe(false)
|
||||
expect(listPlatformAuditLogs).toHaveBeenLastCalledWith(expect.objectContaining({ accountId: '9' }))
|
||||
})
|
||||
|
||||
it('ignores pending responses after closing and permits a fresh reopen', async () => {
|
||||
let finish!: (value: PlatformAuditLogDetail) => void
|
||||
vi.mocked(getPlatformAuditLog).mockReturnValueOnce(new Promise(resolve => { finish = resolve }))
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
wrapper.findComponent(AppDrawer).vm.$emit('update:modelValue', false)
|
||||
await flushPromises()
|
||||
finish({ ...detail, description: '过期详情' }); await flushPromises()
|
||||
expect(wrapper.find('.audit-detail-table').exists()).toBe(false)
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
expect(getPlatformAuditLog).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.findComponent(AppDrawer).text()).toContain('新名称')
|
||||
expect(wrapper.findComponent(AppDrawer).text()).not.toContain('过期详情')
|
||||
})
|
||||
|
||||
it('clears detail when audit permission is revoked', async () => {
|
||||
const wrapper = mountPanel(['platform:audit:list']); await flushPromises()
|
||||
await wrapper.get('.el-table__body button[aria-label^="查看操作日志:"]').trigger('click'); await flushPromises()
|
||||
useAuthStore().permissions = []
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
await flushPromises()
|
||||
expect(wrapper.findComponent(AppDrawer).props('modelValue')).toBe(false)
|
||||
expect(wrapper.find('.audit-detail-table').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('暂无操作日志查看权限')
|
||||
expect(listPlatformAuditLogs).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user