优化
This commit is contained in:
@@ -1,10 +1,39 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
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 { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
import { createContainerFee, listApplicableFeeItems, listContainerCostDetails, listContainerFees } from '../api/containerFees'
|
||||
import ContainerFeesPanel from './ContainerFeesPanel.vue'
|
||||
|
||||
vi.mock('../api/containerFees', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('../api/containerFees')>()
|
||||
return { ...actual, listContainerFees: vi.fn(), listApplicableFeeItems: vi.fn(), listContainerCostDetails: vi.fn(), createContainerFee: vi.fn(), adjustContainerFee: vi.fn(), approveContainerFee: vi.fn(), rejectContainerFee: vi.fn(), voidContainerFee: vi.fn() }
|
||||
})
|
||||
|
||||
const panel = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/components/ContainerFeesPanel.vue'), 'utf8')
|
||||
const detail = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/components/LoadingListDetailDrawer.vue'), 'utf8')
|
||||
|
||||
class ResizeObserverStub { observe() {}; unobserve() {}; disconnect() {} }
|
||||
function memoryStorage(): Storage { const values = new Map<string, string>(); return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value) } }
|
||||
async function mountPanel(permissions: string[], customers = [{ accountId: '11', customerName: '客户A' }]) {
|
||||
const pinia = createPinia(); setActivePinia(pinia)
|
||||
const auth = useAuthStore(); auth.token = 'token'; auth.permissions = permissions
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
const wrapper = mount(ContainerFeesPanel, { attachTo: document.body, props: { planId: '9', customers }, global: { plugins: [pinia, ElementPlus] } })
|
||||
await flushPromises(); return wrapper
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub); vi.stubGlobal('localStorage', memoryStorage()); vi.stubGlobal('sessionStorage', memoryStorage())
|
||||
vi.mocked(listContainerFees).mockReset().mockResolvedValue([]); vi.mocked(listApplicableFeeItems).mockReset().mockResolvedValue([])
|
||||
vi.mocked(listContainerCostDetails).mockReset().mockResolvedValue([]); vi.mocked(createContainerFee).mockReset()
|
||||
document.body.innerHTML = ''
|
||||
})
|
||||
|
||||
describe('ContainerFeesPanel contract', () => {
|
||||
it('is exposed from loading-list detail and derives customer context from real items', () => {
|
||||
expect(detail).toContain('<ContainerFeesPanel :plan-id="summary.planId" :customers="feeCustomers" />')
|
||||
@@ -31,4 +60,38 @@ describe('ContainerFeesPanel contract', () => {
|
||||
expect(panel).toContain('尚无与该柜次关联的财务分摊结果')
|
||||
expect(panel).not.toMatch(/allocatedAmount\s*=|allocationRate\s*=/)
|
||||
})
|
||||
|
||||
it('clears the previous customer context and ignores a late response', async () => {
|
||||
let resolveFirst!: (value: Awaited<ReturnType<typeof listContainerFees>>) => void
|
||||
vi.mocked(listContainerFees).mockImplementation((planId, accountId) => accountId === '11'
|
||||
? new Promise((resolve) => { resolveFirst = resolve })
|
||||
: Promise.resolve([]))
|
||||
const wrapper = await mountPanel(['firstleg:inbound:fee-detail:view'], [{ accountId: '11', customerName: '客户A' }, { accountId: '22', customerName: '客户B' }])
|
||||
const state = (wrapper.vm.$ as unknown as { setupState: { accountId: string; fees: unknown[]; costs: unknown[]; editorOpen: boolean } }).setupState
|
||||
state.accountId = '11'; await flushPromises(); state.editorOpen = true
|
||||
state.accountId = '22'; await flushPromises()
|
||||
expect(state.fees).toEqual([]); expect(state.costs).toEqual([]); expect(state.editorOpen).toBe(false)
|
||||
resolveFirst([{ id: 'old', loadingPlanId: '9', containerBookingId: '99', containerNo: null, feeItemId: '1', feeItemName: '旧客户费用', direction: 'RECEIVABLE', businessDate: '2026-08-12', amount: 10, currency: 'CNY', settlementIncluded: true, reviewStatus: 'PENDING', sourceStatus: 'ACTIVE', sourceVersion: 1, remark: null, reviewerName: null, reviewedAt: null, reviewReason: null }])
|
||||
await flushPromises()
|
||||
expect(state.fees).toEqual([])
|
||||
expect(listContainerCostDetails).not.toHaveBeenCalledWith('11', expect.anything())
|
||||
})
|
||||
|
||||
it('blocks write entry points without a selected customer or edit permission', async () => {
|
||||
const wrapper = await mountPanel(['firstleg:inbound:fee-detail:view'], [{ accountId: '11', customerName: '客户A' }, { accountId: '22', customerName: '客户B' }])
|
||||
const state = (wrapper.vm.$ as unknown as { setupState: { openCreate(): void; save(): Promise<void>; editorOpen: boolean } }).setupState
|
||||
state.openCreate(); await state.save()
|
||||
expect(state.editorOpen).toBe(false)
|
||||
expect(createContainerFee).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('hides all mutation buttons when permissions are missing', async () => {
|
||||
vi.mocked(listContainerFees).mockResolvedValue([
|
||||
{ id: '1', loadingPlanId: '9', containerBookingId: '99', containerNo: null, feeItemId: '1', feeItemName: '待审费用', direction: 'RECEIVABLE', businessDate: '2026-08-12', amount: 10, currency: 'CNY', settlementIncluded: true, reviewStatus: 'PENDING', sourceStatus: 'ACTIVE', sourceVersion: 1, remark: null, reviewerName: null, reviewedAt: null, reviewReason: null },
|
||||
{ id: '2', loadingPlanId: '9', containerBookingId: '99', containerNo: null, feeItemId: '2', feeItemName: '已审费用', direction: 'PAYABLE', businessDate: '2026-08-12', amount: 20, currency: 'CNY', settlementIncluded: true, reviewStatus: 'APPROVED', sourceStatus: 'ACTIVE', sourceVersion: 1, remark: null, reviewerName: null, reviewedAt: null, reviewReason: null },
|
||||
])
|
||||
const wrapper = await mountPanel(['firstleg:inbound:fee-detail:view'])
|
||||
expect(wrapper.text()).not.toContain('新增费用')
|
||||
for (const label of ['通过', '驳回', '调整', '作废']) expect(wrapper.findAll('button').some((button) => button.text() === label)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user