Files
admin-web/src/modules/loading-lists/pages/PreloadPlanDetailView.dialogs.test.ts
T

132 lines
9.2 KiB
TypeScript

import { createRequire } from 'node:module'
import { mount, flushPromises, enableAutoUnmount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import AppDialog from '@/shared/components/AppDialog.vue'
import { ApiResponseError, http } from '@/shared/api/http'
import View from './PreloadPlanDetailView.vue'
import { confirmPreloadPlan, getPreloadPlan, listDomesticPdaOperators, updateLoadingItemRemarks, updatePreloadTransport, type LoadingDetail, type PreloadSummary } from '../api/platformLoadingLists'
// Use the repository's supported CJS entry for the real async form validator.
const elementPlus = createRequire(import.meta.url)('element-plus') as typeof import('element-plus')
const ElementPlus = elementPlus.default
const { ElInput, ElSelect } = elementPlus
vi.mock('vue-router', () => ({
useRoute: () => ({ params: { planId: '1900000000000000001' }, query: { warehouseId: '1900000000000000002' } }),
useRouter: () => ({ replace: vi.fn(), push: vi.fn(), back: vi.fn() }),
}))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
vi.mock('@/core/workspace-tabs/workspaceTabs', () => ({ useWorkspaceTabsStore: () => ({ removeByIds: vi.fn() }) }))
vi.mock('../api/platformLoadingLists', () => ({
confirmPreloadPlan: vi.fn(), getConfirmedLoadingList: vi.fn(), getPreloadPlan: vi.fn(),
listDomesticPdaOperators: vi.fn(), updateLoadingItemRemarks: vi.fn(), updatePreloadTransport: vi.fn(),
}))
function fixture(containerNo: string | null = 'MSCU-OLD', version = 7, itemVersion = 3): LoadingDetail<PreloadSummary> {
return {
summary: {
planId: '1900000000000000001', containerBookingId: '2900000000000000001', preloadNo: 'PL-TEST-1', cabinetNo: 'CAB-TEST-1', containerNo,
customers: [{ accountId: '11', customerName: '客户甲' }], warehouseId: '1900000000000000002', warehouseName: '国内仓', vesselName: 'SHIP', voyageNo: 'V1', containerType: '40HQ',
status: 'EDITING', skuCount: '1', totalPackageCount: '10', totalVolumeCbm: 1, totalGrossWeightKg: 100, createdAt: '2026-10-19T10:00:00', version,
readiness: { containerNoReady: Boolean(containerNo), preloadLinkCount: 1, confirmedPreloadCount: 0, oceanBookingId: '31', oceanBookingStatus: 'RELEASED', pdaTaskId: null, pdaTaskStatus: null, confirmationStatus: 'PENDING', canConfirm: Boolean(containerNo), readyForPda: false, blockingReasons: [] },
},
items: [{
itemId: '41', itemVersion, accountId: '11', customerName: '客户甲', customsTaxMode: 'TAX_REFUND', taxRefundFlag: true,
orderId: '51', orderNo: 'ORD-TEST-1', inboundId: '61', inboundDetailId: '62', inboundCode: 'IN-TEST-1', containerNo,
skuCode: null, destinationWarehouse: 'LAX9', chineseName: '测试货物', englishName: 'Goods', goodsImageUrl: null,
unitType: 'PACKAGE', palletId: null, palletNo: null, indivisible: false, packageCount: 10, palletCount: 0,
grossWeightKg: 100, netWeightKg: 90, volumeCbm: 1, dimensionalWeightKg: 167, productQuantity: '10', unitsPerPackage: 1,
fbaTrackingNo: null, shipmentNo: null, shippingMark: null, internalRemark: '原内部备注', customerRemark: '客户内容\n第二行', warehouseRemark: '原仓库备注', customerServiceRemark: '原客服备注', loadSequence: 1, frozenAt: null,
}], histories: [],
}
}
async function render(initial = fixture()) {
vi.mocked(getPreloadPlan).mockResolvedValue(initial)
const wrapper = mount(View, { global: { plugins: [ElementPlus], stubs: {
AdminPage: { template: '<main><slot /></main>' }, OperationErrorToast: true,
LoadingListDetailDrawer: { name: 'LoadingListDetailDrawer', template: '<section><slot name="pda-operator" /></section>' },
} } })
await flushPromises()
return wrapper
}
function dialog(wrapper: Awaited<ReturnType<typeof render>>, prefix: string) {
return wrapper.findAllComponents(AppDialog).find(item => item.props('title').startsWith(prefix))!
}
enableAutoUnmount(afterEach)
beforeEach(() => {
vi.resetAllMocks()
vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} })
vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'success', data: { planId: '1900000000000000001', detailResource: 'PRELOAD', planStatus: 'EDITING' } })
vi.mocked(listDomesticPdaOperators).mockResolvedValue([{ userId: '71', displayName: '操作员甲' }])
})
afterEach(() => { vi.restoreAllMocks(); document.body.innerHTML = '' })
describe('预装清单统一弹窗', () => {
it.each([null, 'MSCU-OLD'])('运输表保留规范化和纠错原因校验,原柜号=%s', async (original) => {
const wrapper = await render(fixture(original))
vi.mocked(updatePreloadTransport).mockResolvedValue(fixture('MSCU-NEW', 8))
wrapper.getComponent({ name: 'LoadingListDetailDrawer' }).vm.$emit('edit')
await flushPromises()
const popup = dialog(wrapper, original ? '纠正' : '补录')
expect(popup.props('title')).toContain('PL-TEST-1')
const number = popup.findAllComponents(ElInput).find(input => input.props('placeholder') === '请输入提柜后取得的真实集装箱号')!
number.vm.$emit('update:modelValue', ' mscu-new ')
number.vm.$emit('blur', new FocusEvent('blur'))
popup.findAllComponents(ElInput).find(input => input.props('placeholder') === '请输入航次')!.vm.$emit('update:modelValue', ' V2 ')
if (original) {
popup.vm.$emit('confirm'); await flushPromises()
expect(updatePreloadTransport).not.toHaveBeenCalled()
popup.findAllComponents(ElInput).find(input => input.props('type') === 'textarea')!.vm.$emit('update:modelValue', ' 更正柜号 ')
}
popup.vm.$emit('confirm'); await flushPromises()
expect(updatePreloadTransport).toHaveBeenCalledWith('1900000000000000001', '1900000000000000002', { containerNo: 'MSCU-NEW', voyageNo: 'V2', operationReason: original ? '更正柜号' : null, expectedVersion: 7 })
expect(popup.props('modelValue')).toBe(false)
})
it('客户备注只读,全屏及版本冲突后保留输入并按最新两层版本重试', async () => {
const initial = fixture()
const wrapper = await render(initial)
wrapper.getComponent({ name: 'LoadingListDetailDrawer' }).vm.$emit('edit-remarks', initial.items[0])
await flushPromises()
const popup = dialog(wrapper, '编辑备注')
expect(popup.props('title')).toBe('编辑备注 · IN-TEST-1')
const customer = popup.findAllComponents({ name: 'ElFormItem' }).find(field => field.props('label') === '客户备注(只读)')!
expect(customer.text()).toContain('第二行')
expect(customer.findComponent(ElInput).exists()).toBe(false)
const fields = popup.findAllComponents(ElInput)
fields[0]!.vm.$emit('update:modelValue', ' 新内部 ')
fields[1]!.vm.$emit('update:modelValue', '')
fields[2]!.vm.$emit('update:modelValue', '新客服')
await popup.get('[aria-label="展开全屏"]').trigger('click')
expect(fields[0]!.props('modelValue')).toBe(' 新内部 ')
vi.mocked(getPreloadPlan).mockResolvedValue(fixture('MSCU-OLD', 8, 4))
vi.mocked(updateLoadingItemRemarks).mockRejectedValueOnce(new ApiResponseError('版本变化', 'A0409')).mockResolvedValueOnce({ id: initial.summary.planId, version: 9, items: [{ id: '41', version: 5, internalRemark: '新内部', warehouseRemark: null, customerServiceRemark: '新客服' }] })
popup.vm.$emit('confirm'); await flushPromises()
expect(popup.props('modelValue')).toBe(true)
expect(fields[0]!.props('modelValue')).toBe(' 新内部 ')
popup.vm.$emit('confirm'); await flushPromises()
const calls = vi.mocked(updateLoadingItemRemarks).mock.calls
expect(calls[0]![2]).toMatchObject({ expectedPlanVersion: 7, expectedItemVersion: 3 })
expect(calls[1]).toEqual([initial.summary.planId, '41', { internalRemark: '新内部', warehouseRemark: null, customerServiceRemark: '新客服', expectedPlanVersion: 8, expectedItemVersion: 4, operationId: calls[0]![2].operationId }])
expect(popup.props('modelValue')).toBe(false)
})
it('确认资料显示已选作业员,保留必选门禁和原推送操作号', async () => {
const initial = fixture()
const wrapper = await render(initial)
const drawer = wrapper.getComponent({ name: 'LoadingListDetailDrawer' })
drawer.vm.$emit('confirm-preload'); await flushPromises()
expect(dialog(wrapper, '推送PDA').props('modelValue')).toBe(false)
wrapper.getComponent(ElSelect).vm.$emit('update:modelValue', '71')
await flushPromises()
drawer.vm.$emit('confirm-preload'); await flushPromises()
const popup = dialog(wrapper, '推送PDA')
expect(popup.get('table[aria-label="确认预装资料"]').text()).toContain('操作员甲')
vi.mocked(confirmPreloadPlan).mockResolvedValue({ planId: initial.summary.planId, containerBookingId: initial.summary.containerBookingId, confirmationStatus: 'CONFIRMED', version: 8, readiness: { ...initial.summary.readiness, confirmationStatus: 'CONFIRMED', confirmedPreloadCount: 1, pdaTaskId: '91', pdaTaskStatus: 'PENDING', canConfirm: false, readyForPda: true } })
popup.vm.$emit('confirm'); await flushPromises()
expect(confirmPreloadPlan).toHaveBeenCalledWith(initial.summary.planId, 7, '71', expect.stringContaining('admin-preload-confirm-'))
expect(popup.props('modelValue')).toBe(false)
})
})