import { afterEach, expect, it, vi } from 'vitest' import { shallowMount, flushPromises, type VueWrapper } from '@vue/test-utils' import FleetQuotesView from './FleetQuotesView.vue' import TripOperationsView from './TripOperationsView.vue' import * as quotes from '../api/fleetQuotes' import * as reports from '../api/tripOperations' vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (p: string) => p.endsWith(':list') }) })) vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) })) vi.mock('../api/fleetQuotes', () => ({ listFleetQuotes: vi.fn(), setFleetQuoteStatus: vi.fn(), deleteFleetQuote: vi.fn(), exportFleetQuotes: vi.fn(), saveFleetCsv: vi.fn() })) vi.mock('../api/tripOperations', () => ({ listTripOperations: vi.fn(), exportTripOperations: vi.fn() })) const wrappers: VueWrapper[] = [] const layout = { template: '
' } function mount(component: object) { const wrapper = shallowMount(component, { global: { stubs: { AdminPage: layout, ListPageLayout: layout, QueryToolbar: true, DataTable: { name: 'DataTable', props: ['data', 'columns'], template: '
' }, ElButton: { template: '' }, LoadErrorState: true, FleetQuoteEditor: true, FleetQuoteImport: true, ApiErrorMeta: true } } }); wrappers.push(wrapper); return wrapper } afterEach(() => { wrappers.splice(0).forEach(w => w.unmount()); vi.clearAllMocks() }) it('quotes read-only view fetches a page once and hides all write/export tools', async () => { vi.mocked(quotes.listFleetQuotes).mockResolvedValue({ records: [], total: 0, page: 1, size: 20, pages: 0 }) const wrapper = mount(FleetQuotesView); await flushPromises() expect(quotes.listFleetQuotes).toHaveBeenCalledTimes(1); expect(wrapper.text()).not.toContain('新增报价'); expect(wrapper.text()).not.toContain('批量导入') expect(wrapper.findComponent({ name: 'DataTable' }).props('columns')[0]).toMatchObject({ key: 'actions', fixed: 'left' }) }) it('operations uses server totals rather than displayed page size', async () => { vi.mocked(reports.listTripOperations).mockResolvedValue({ page: { records: [], total: 21, page: 1, size: 20, pages: 2 }, summary: { trips: 21, inTransit: 12, completed: 9, physicalPallets: 40, palletSpaces: 40, ownPackages: 70, externalPackages: 10, externalAgreedAmounts: [], costs: { visible: false, unrecordedTrips: null, unallocatedTrips: null, totals: [] } }, charts: { physicalPallets: [], destinationTrips: [], externalAgreedAmounts: [], externalPalletRates: [] } }) const wrapper = mount(TripOperationsView); await flushPromises() expect(reports.listTripOperations).toHaveBeenCalledTimes(1); expect(wrapper.text()).toContain('实际出车 21 次'); expect(wrapper.text()).not.toContain('导出CSV') })