import { flushPromises, shallowMount, type VueWrapper } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' import { getVehicle } from '../api/overseasMasterData' import ExchangeRatesView from './ExchangeRatesView.vue' import OverseasQuotesView from './OverseasQuotesView.vue' import VehicleDetailView from './VehicleDetailView.vue' import VehiclesView from './VehiclesView.vue' const push = vi.fn() vi.mock('vue-router', () => ({ useRoute: () => ({ params: { vehicleId: 'VH-TEST-001' } }), useRouter: () => ({ push }) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) })) vi.mock('../api/overseasMasterData', () => ({ listQuotes: vi.fn().mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 }), listQuoteWarehouseOptions: vi.fn().mockResolvedValue([]), getQuote: vi.fn(), createQuote: vi.fn(), updateQuote: vi.fn(), changeQuoteStatus: vi.fn(), exportQuotes: vi.fn(), listExchangeRates: vi.fn().mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 }), getExchangeRateSummary: vi.fn().mockResolvedValue({ latestMonth: null, latestUsdToCnyAverage: null, updatedBy: null, updatedAt: null }), getExchangeRate: vi.fn(), createExchangeRate: vi.fn(), updateExchangeRate: vi.fn(), changeExchangeRateStatus: vi.fn(), exportExchangeRates: vi.fn(), listVehicles: vi.fn().mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 }), getVehicle: vi.fn().mockResolvedValue({ id: 'VH-TEST-001', plateNumber: 'CA-TEST', model: 'Truck', maxPalletSpaces: 30, maxLoadKg: 18000, maxVolumeCbm: 90, fuelConsumptionPer100Km: 12, mileageKm: 100, driverOrDispatchCompany: null, phone: null, region: 'OVERSEAS', ownership: 'OWNED', status: 'IN_SERVICE', remark: null, recordVersion: 0, updatedBy: 'tester', updatedAt: '2026-08-20T10:00:00' }), createVehicle: vi.fn(), updateVehicle: vi.fn(), changeVehicleStatus: vi.fn(), exportVehicles: vi.fn(), })) const slot = { template: '
' } const stubs = { AdminPage: slot, ListPageLayout: slot, DetailPageLayout: slot, QueryToolbar: true, DataTable: { template: '
' }, FormSection: slot, LoadErrorState: true, AppDialog: slot, ElTooltip: slot, ElButton: slot, ElTag: slot, ElTable: slot, ElTableColumn: true, ElForm: slot, ElFormItem: slot, ElInput: true, ElSelect: slot, ElOption: true, ElDatePicker: true, ElInputNumber: true } function mountView(component: object): VueWrapper { return shallowMount(component, { global: { stubs, directives: { loading: () => undefined } } }) } describe('overseas master data real page mounts', () => { beforeEach(() => push.mockReset()) it.each([OverseasQuotesView, ExchangeRatesView, VehiclesView])('mounts a real API-backed list page', async (component) => { const wrapper = mountView(component); await flushPromises(); expect(wrapper.find('[data-test="data-table"]').exists()).toBe(true) }) it('keeps the business title empty on detail failure instead of exposing the route ID', async () => { vi.mocked(getVehicle).mockRejectedValueOnce(new Error('加载失败')) const wrapper = mountView(VehicleDetailView) await flushPromises() expect(wrapper.get('.density-detail-title').text()).toBe('—') expect(wrapper.text()).not.toContain('VH-TEST-001') expect(wrapper.find('.density-fields').exists()).toBe(false) expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(true) }) it('mounts vehicle detail and scopes both real fulfillment projections to the loaded vehicle', async () => { const wrapper = mountView(VehicleDetailView); await flushPromises() expect(wrapper.get('.density-detail-title').text()).toBe('CA-TEST') expect(wrapper.get('.density-fields').findAll('dt')).toHaveLength(14) expect(wrapper.get('.density-fields').text()).toContain('在役') expect(wrapper.text()).not.toContain('IN_SERVICE') const tables = wrapper.findAllComponents({ name: 'VehicleFulfillmentTable' }) expect(tables.map(table => table.props())).toEqual([ { vehicleId: 'VH-TEST-001', kind: 'deliveries' }, { vehicleId: 'VH-TEST-001', kind: 'changes' }, ]) }) })