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 { ApiResponseError } from '@/shared/api/http' import TruckAppointmentsView from './TruckAppointmentsView.vue' import { listTruckAppointments } from '../api/platformTrucking' import type { TruckAppointmentResource } from '../types' import { useAuthStore } from '@/core/auth/auth' import { readFileSync } from 'node:fs' import { resolve } from 'node:path' const source = readFileSync(resolve(process.cwd(), 'src/modules/trucking/pages/TruckAppointmentsView.vue'), 'utf8') vi.mock('../api/platformTrucking', () => ({ listTruckAppointments: vi.fn(), })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }), })) const row: TruckAppointmentResource = { id: '9001', appointmentNo: 'TA-9001', status: 'DISPATCHED', orderNo: 'ORD-1', oceanBookingNo: 'OB-1', billOfLadingNo: 'BL-1', warehouseName: '义乌仓', shippingCompanyName: '船公司A', bookingAgentName: '代理A', carrierName: '宁波运输', driverName: '王强', vehicleNo: '浙B12345', plannedLoadingAt: '2026-08-10T10:00:00', payableAmount: '3200.00', currency: 'RMB', attachmentCount: 2, attachments: [], } describe('TruckAppointmentsView', () => { beforeEach(() => { vi.mocked(listTruckAppointments).mockReset().mockResolvedValue({ records: [row], page: 1, size: 20, total: 1, pages: 1 }) setActivePinia(createPinia()) useAuthStore().permissions = [ 'platform:firstleg:truck-appointment:view', 'platform:firstleg:truck-appointment:edit', ] }) it('loads the real list and renders relationship snapshots', async () => { const wrapper = mount(TruckAppointmentsView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(listTruckAppointments).toHaveBeenCalledWith({ page: 1, size: 20, keyword: '', status: '' }) expect(wrapper.text()).toContain('TA-9001') expect(wrapper.text()).toContain('OB-1') expect(wrapper.text()).toContain('BL-1') expect(wrapper.text()).toContain('义乌仓') expect(wrapper.text()).toContain('船公司A') expect(wrapper.text()).toContain('代理A') expect(wrapper.text()).toContain('宁波运输') expect(wrapper.find('button[aria-label="查看 TA-9001"]').exists()).toBe(true) }) it('uses stable accessible labels when appointment identifiers are missing', async () => { vi.mocked(listTruckAppointments).mockResolvedValue({ records: [ { ...row, id: '9002', appointmentNo: '' }, { ...row, id: undefined, appointmentNo: null } as unknown as TruckAppointmentResource, ], page: 1, size: 20, total: 2, pages: 1, }) const wrapper = mount(TruckAppointmentsView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.find('button[aria-label="查看拖车预约详情"]').exists()).toBe(true) const labels = wrapper.findAll('button[aria-label]').map((button) => button.attributes('aria-label')) expect(labels.every((label) => Boolean(label) && !/undefined|null/.test(label ?? ''))).toBe(true) expect(labels.every((label) => !label?.includes('9002'))).toBe(true) expect(source).toContain("appointmentActionLabel('编辑', row as TruckAppointmentResource)") }) it('does not expose a technical carrier id when the business name is unavailable', async () => { vi.mocked(listTruckAppointments).mockResolvedValue({ records: [{ ...row, carrierName: '', carrierId: 'carrier-77' }], page: 1, size: 20, total: 1, pages: 1, }) const wrapper = mount(TruckAppointmentsView, { global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('已关联承运商') expect(wrapper.text()).not.toContain('carrier-77') }) it('uses the table toolbar for create and keeps a single empty state', () => { expect(source).toContain('