Files
admin-web/src/modules/customers/pages/CustomerDetailView.test.ts
T

153 lines
7.7 KiB
TypeScript

import ElementPlus 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 {
downloadTenantQualification,
getPlatformTenant,
listPlatformTenantMembers,
listTenantQualifications,
type PlatformTenantResource,
} from '@/modules/iam'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
import CustomerDetailView from './CustomerDetailView.vue'
const { routerPush, routeControl } = vi.hoisted(() => ({
routerPush: vi.fn(),
routeControl: { setTenantId: (_tenantId: string) => {} },
}))
vi.mock('vue-router', async (importOriginal) => ({
...await importOriginal<typeof import('vue-router')>(),
...await (async () => {
const { reactive } = await import('vue')
const route = reactive({ params: { tenantId: '8' } })
routeControl.setTenantId = (tenantId: string) => { route.params.tenantId = tenantId }
return { useRoute: () => route }
})(),
useRouter: () => ({ push: routerPush }),
}))
vi.mock('@/modules/iam', () => ({
changePlatformTenantStatus: vi.fn(),
downloadTenantQualification: vi.fn(),
getPlatformTenant: vi.fn(),
listPlatformTenantMembers: vi.fn(),
listTenantQualifications: vi.fn(),
qualificationFileName: vi.fn((documentUrl: string) => documentUrl.split('/').at(-1) || '资质文件'),
}))
class ResizeObserverStub implements ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
const tenant: PlatformTenantResource = {
id: '8', tenantCode: 'TEN8', tenantName: '柏悦物流', customerNickname: '柏', status: 'ACTIVE', legalName: '柏悦物流有限公司',
legalRepresentative: '张三', unifiedSocialCreditCode: '91440300MA5ABCDE12', contactName: '张三',
contactPhone: '13800138000', contactEmail: 'ops@example.com', servicePackage: '基础版', contactWechat: 'baiyue-ops',
salesOwnerName: '李四', paymentTermDays: 30, enterpriseType: '有限责任公司', establishedDate: '2020-01-01',
registeredCapital: 1000, registeredCapitalCurrency: 'CNY', registeredAddress: '深圳市宝安区', internalRemark: '重点客户',
createdAt: '2026-08-01T10:00:00', approvedAt: '2026-08-02T10:00:00',
}
enableAutoUnmount(afterEach)
describe('CustomerDetailView', () => {
let pinia: ReturnType<typeof createPinia>
beforeEach(() => {
vi.clearAllMocks()
routeControl.setTenantId('8')
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', { configurable: true, value: vi.fn() })
Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: vi.fn(() => 'blob:qualification-preview') })
Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: vi.fn() })
document.body.innerHTML = ''
pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
auth.token = 'token'
auth.permissions = ['platform:customer:list', 'platform:user:list', 'platform:order:list', 'platform:finance:customer-account:list', 'platform:firstleg:container:list']
useAccessPreviewStore().initializeAfterLogin()
vi.mocked(getPlatformTenant).mockResolvedValue(tenant)
vi.mocked(listTenantQualifications).mockResolvedValue([{
id: 'qualification-1', tenantId: tenant.id, qualificationType: 'BUSINESS_LICENSE', certificateNo: tenant.unifiedSocialCreditCode,
documentUrl: '/files/license.png', status: 'SUPERSEDED', reviewComment: '已替换', validTo: null,
}])
vi.mocked(listPlatformTenantMembers).mockResolvedValue([{
membershipId: 'member-1', userId: 'user-1', authUserId: 'auth-1', username: 'merchant_admin', displayName: '张三',
phone: '13800138000', email: 'ops@example.com', memberType: 'OWNER', memberNo: null, status: 'ACTIVE', roleIds: [], roleNames: ['企业管理员'],
}])
vi.mocked(downloadTenantQualification).mockResolvedValue(new Blob(['image'], { type: 'image/png' }))
})
it('hides the deferred operating metrics and keeps enterprise users in detail content', async () => {
mount(CustomerDetailView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
expect([...document.querySelectorAll('.detail-tabs button')].map((button) => button.getAttribute('aria-label'))).toEqual([
'基本信息', '计费规则', '操作日志',
])
expect(document.body.textContent).not.toContain('累计运单')
expect(document.body.textContent).not.toContain('累计应收')
expect(document.body.textContent).not.toContain('在途柜量')
expect(document.querySelector('.overview-strip')).toBeNull()
expect(document.body.textContent).toContain('merchant_admin')
expect(document.body.textContent).toContain('企业管理员')
expect(document.body.textContent).toContain('公司联系电话')
expect(document.body.textContent).toContain('登录手机号')
expect(document.body.textContent).toContain('修改会影响登录')
expect(document.querySelector('.contact-panel .info-grid--three')).not.toBeNull()
expect(listPlatformTenantMembers).toHaveBeenCalledWith(tenant.id)
expect(document.body.textContent).toContain('已替换')
const billingTab = document.querySelector<HTMLButtonElement>('.detail-tabs button[aria-label="计费规则"]')
billingTab?.click()
await flushPromises()
expect(document.body.textContent).toContain('暂无尺寸计算规则查看权限')
expect(document.body.textContent).not.toContain('计费规则尚未接入')
})
it('opens an authenticated qualification preview', async () => {
mount(CustomerDetailView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
const fileButton = document.querySelector<HTMLButtonElement>('.qualification-list button')
fileButton?.click()
await flushPromises()
expect(downloadTenantQualification).toHaveBeenCalledWith(tenant.id, '/files/license.png')
expect(document.querySelector('img[alt="license.png预览"]')).not.toBeNull()
expect(document.querySelector('.customer-qualification-preview .el-dialog__title')?.textContent).toBe('查看资质文件 · TEN8')
expect(document.querySelector('.customer-preview-filename')?.textContent).toBe('license.png')
document.querySelector<HTMLButtonElement>('.customer-qualification-preview button[aria-label="展开全屏"]')!.click()
await flushPromises()
expect(document.querySelector('.customer-qualification-preview.is-fullscreen img')?.getAttribute('src')).toBe('blob:qualification-preview')
})
it('reloads a reused detail view and ignores the previous customer response', async () => {
const secondTenant = { ...tenant, id: '9', tenantCode: 'TEN9', tenantName: '远航供应链', legalName: '远航供应链有限公司' }
let resolveFirstTenant!: (value: PlatformTenantResource) => void
const firstTenantRequest = new Promise<PlatformTenantResource>((resolve) => { resolveFirstTenant = resolve })
vi.mocked(getPlatformTenant).mockImplementation((tenantId) => tenantId === '8' ? firstTenantRequest : Promise.resolve(secondTenant))
vi.mocked(listTenantQualifications).mockResolvedValue([])
vi.mocked(listPlatformTenantMembers).mockResolvedValue([])
mount(CustomerDetailView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
routeControl.setTenantId('9')
await flushPromises()
expect(getPlatformTenant).toHaveBeenCalledWith('9')
expect(document.body.textContent).toContain('远航供应链')
resolveFirstTenant(tenant)
await flushPromises()
expect(document.body.textContent).toContain('远航供应链')
expect(document.body.textContent).not.toContain('柏悦物流')
})
})