import ElementPlus from 'element-plus' 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 OceanBookingsView from './OceanBookingsView.vue' import { listOceanBookings } from '../api/platformOceanBookings' const routerPush = vi.hoisted(() => vi.fn()) vi.mock('vue-router', () => ({ useRouter: () => ({ push: routerPush }) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn().mockResolvedValue({ records: [] }) })) vi.mock('../api/platformOceanBookings', () => ({ listOceanBookings: vi.fn() })) const source = readFileSync(resolve(process.cwd(), 'src/modules/ocean-bookings/pages/OceanBookingsView.vue'), 'utf8') describe('OceanBookingsView', () => { beforeEach(() => { vi.clearAllMocks() vi.mocked(listOceanBookings).mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 }) }) it('keeps the operation column first and left fixed', () => { expect(source.indexOf("key: 'actions'")).toBeLessThan(source.indexOf("prop: 'bookingNo'")) expect(source).toContain("fixed: 'left'") }) it('renders loading, empty and error states and keeps draft creation available', () => { expect(source).toContain('LoadErrorState') expect(source).toContain(':loading="loading"') expect(source).toContain('新增订舱') expect(source).toContain("router.push('/ocean-bookings/new')") }) it('edits DRAFT rows and views RELEASED rows', () => { expect(source).toContain(' { expect(source).toContain('options: oceanBookingStatusOptions') expect(source).toContain(':status="oceanBookingStatusLabel(row.status)"') expect(source).not.toContain('CONFIRMED') expect(source).not.toContain('CHANGE_PENDING') expect(source).not.toContain('CANCELLED') }) it('keeps the prototype business field order after actions', () => { const fields = ['bookingNo', 'customerName', 'billOfLadingNo', 'shipperName', 'consigneeName', 'containerCount', 'carrierName', 'bookingAgent', 'vesselName', 'pol', 'pod', 'etd', 'eta', 'status', 'updatedAt'] let previous = source.indexOf("key: 'actions'") for (const field of fields) { const current = source.indexOf(`prop: '${field}'`, previous + 1) expect(current, field).toBeGreaterThan(previous) previous = current } }) it('displays the server containerCount as the booking requirement total without detail N+1', async () => { vi.mocked(listOceanBookings).mockResolvedValueOnce({ records: [{ id: '183', bookingNo: 'OB-260825-0183', status: 'RELEASED', containerCount: 1, customerName: '回归客户', vesselName: null, voyageNo: null, etd: null, eta: null, updatedAt: null, }], page: 1, size: 20, total: 1, pages: 1 } as never) const wrapper = mount(OceanBookingsView, { global: { plugins: [ElementPlus] } }) await (wrapper.vm as unknown as { load: () => Promise }).load() await flushPromises() const tableText = wrapper.find('.el-table__body-wrapper').text() expect(tableText).toContain('OB-260825-0183') expect(tableText).toContain('1') expect(source).toContain("label: '订舱柜量'") expect(source).not.toContain('getOceanBooking') wrapper.unmount() }) it('loads active customers by name and stays usable on narrow screens', () => { expect(source).toContain("listPlatformTenants({ page: 1, size: 200, status: 'ACTIVE' })") expect(source).toContain('label: customer.tenantName') expect(source).not.toContain('customer.tenantCode') expect(source).toContain('@media(max-width:420px)') }) })