import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { shallowMount, flushPromises, type VueWrapper } from '@vue/test-utils' import Cell from './TripOperationsFinanceCell.vue' import * as api from '../api/tripOperationsFinance' const access = vi.hoisted(() => ({ read: true })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => access.read }) })) vi.mock('../api/tripOperationsFinance', () => ({ getTripOperationsFinance: vi.fn() })) const wrappers: VueWrapper[] = [] function mount() { const w = shallowMount(Cell, { props: { tripNo: 'TR-A' }, global: { stubs: { ElButton: { template: '' }, ElPopover: { template: '
' }, ApiErrorMeta: true } } }); wrappers.push(w); return w } const result: api.TripFinanceResult = { tripNo: 'TR-A', dispatchNos: ['DS-A'], receiptsVisible: true, attributionStatus: 'ATTRIBUTED', attributionReason: 'frozen', finance: { status: 'AVAILABLE', totals: [{ currency: 'CNY', receivableAmount: 20, receivedAmount: null }], sources: [], documents: [{ billNo: 'BILL-A', currency: 'CNY', receivedAmount: null, allocationStatus: 'PENDING_ALLOCATION', reason: '混合来源' }], reason: 'authority' } } beforeEach(() => { vi.resetAllMocks(); access.read = true; vi.mocked(api.getTripOperationsFinance).mockResolvedValue(structuredClone(result)) }) afterEach(() => wrappers.splice(0).forEach(w => w.unmount())) it('loads only on explicit click and displays pending without inventing zero', async () => { const w = mount(); await flushPromises(); expect(api.getTripOperationsFinance).not.toHaveBeenCalled() await w.find('button').trigger('click'); await flushPromises() expect(api.getTripOperationsFinance).toHaveBeenCalledExactlyOnceWith('TR-A') expect(w.text()).toContain('应收 20.00 / 实收 待分配'); expect(w.text()).toContain('BILL-A'); expect(w.text()).not.toContain('实收 0.00') }) it('does not fetch without existing financial permission', async () => { access.read = false; const w = mount(); await flushPromises(); expect(w.text()).toContain('无应收读取权限'); expect(api.getTripOperationsFinance).not.toHaveBeenCalled() }) it('ignores a response arriving after switching trip', async () => { let resolve!: (v: api.TripFinanceResult) => void vi.mocked(api.getTripOperationsFinance).mockImplementation(() => new Promise(r => { resolve = r })) const w = mount(); await w.find('button').trigger('click'); await w.setProps({ tripNo: 'TR-B' }); resolve(result); await flushPromises() expect(w.text()).not.toContain('20.00'); expect(w.text()).toContain('查看应收/实收') }) it('separates no receipt permission from unallocated values', async () => { vi.mocked(api.getTripOperationsFinance).mockResolvedValue({ ...result, receiptsVisible: false, finance: { ...result.finance, documents: [] } }) const w = mount(); await w.find('button').trigger('click'); await flushPromises(); expect(w.text()).toContain('实收 无读取权限') })