Files
admin-web/src/modules/unified-orders/pages/UnifiedOrdersView.requests.test.ts
T
2026-09-15 17:46:44 +08:00

259 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { flushPromises, shallowMount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { listPlatformUnifiedOrders, updatePlatformUnifiedOrderPlanning } from '@/modules/unified-orders/api/unifiedOrders'
import { createPlatformExportTask } from '@/modules/downloads'
import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog'
import { ApiResponseError } from '@/shared/api/http'
import { ElMessage } from 'element-plus'
import UnifiedOrdersView from './UnifiedOrdersView.vue'
vi.mock('vue-router', () => ({
useRoute: () => ({ params: {}, query: {} }),
useRouter: () => ({ push: vi.fn() }),
}))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }) }))
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn().mockResolvedValue([]) }))
vi.mock('@/modules/inbounds', () => ({ downloadWeightImage: vi.fn(), getInboundMeasurement: vi.fn() }))
vi.mock('@/modules/downloads', () => ({ createPlatformExportTask: vi.fn() }))
vi.mock('@/shared/components/exportTaskCreatedDialog', () => ({ showExportTaskCreatedDialog: vi.fn() }))
vi.mock('@/modules/unified-orders/workflows', () => ({ UnifiedOrderWorkflowPanel: { template: '<div />' } }))
vi.mock('@/modules/unified-orders/api/coLoadGroups', () => ({
cancelPlatformCoLoadGroup: vi.fn(),
createPlatformCoLoadGroup: vi.fn(),
getPlatformCoLoadGroupByInboundCode: vi.fn(),
}))
vi.mock('@/modules/unified-orders/api/unifiedOrders', () => ({
updatePlatformUnifiedOrderRemarks: vi.fn(),
approvePlatformUnifiedOrder: vi.fn(),
getPlatformUnifiedOrder: vi.fn(),
listPlatformUnifiedOrderFeeDetails: vi.fn(),
listPlatformUnifiedOrders: vi.fn(),
updatePlatformUnifiedOrderPlanning: vi.fn(),
unifiedOrderStatusOptions: [
{ value: 'PENDING_REVIEW', label: '待审核' },
{ value: 'WAIT_MEASURE', label: '待测量数据' },
{ value: 'WAIT_SPLIT', label: '待分柜' },
{ value: 'LOADING', label: '装柜中' },
{ value: 'LOADED', label: '装柜完成' },
{ value: 'DELIVERED', label: '派送完成' },
{ value: 'COMPLETED', label: '已完成' },
],
}))
function emptyPage() {
return { records: [], page: 1, size: 20, total: 0, pages: 0 }
}
function mountView() {
return shallowMount(UnifiedOrdersView, {
global: {
directives: { loading: () => undefined },
stubs: {
AdminPage: { template: '<main><slot /></main>' },
ListPageLayout: { template: '<section><slot name="toolbar" /><slot name="state" /><slot /></section>' },
QueryToolbar: { name: 'QueryToolbar', props: ['modelValue'], emits: ['update:modelValue', 'search', 'reset'], template: '<div data-testid="query-toolbar" />' },
DataTable: { name: 'DataTable', props: ['page', 'pageSize', 'data'], emits: ['update:page', 'update:pageSize', 'selection-change'], template: '<div data-testid="data-table"><slot name="toolbar-actions" /><div v-for="row in data" :key="row.orderId" class="test-row"><slot name="estimatedVolume" :row="row" /><slot name="expectedArrival" :row="row" /></div></div>' },
'el-button': { template: '<button><slot /></button>' },
'el-input-number': { name: 'ElInputNumber', props: ['modelValue', 'disabled'], emits: ['update:modelValue', 'change'], template: '<input class="number-input" :value="modelValue" :disabled="disabled" @change="$emit(\'change\')" />' },
'el-date-picker': { name: 'ElDatePicker', props: ['modelValue', 'disabled'], emits: ['update:modelValue', 'change'], template: '<input class="date-input" :value="modelValue" :disabled="disabled" @change="$emit(\'change\')" />' },
ApiErrorMeta: { props: ['details'], template: '<code class="api-meta">{{ details.code }} {{ details.requestId }} {{ details.traceId }}</code>' },
},
},
})
}
describe('UnifiedOrdersView list request coordination', () => {
beforeEach(() => {
vi.mocked(listPlatformUnifiedOrders).mockReset().mockResolvedValue(emptyPage())
vi.mocked(updatePlatformUnifiedOrderPlanning).mockReset()
vi.mocked(createPlatformExportTask).mockReset().mockResolvedValue({ taskName: '国内仓订单', taskNo: 'EXP-1' } as never)
vi.mocked(showExportTaskCreatedDialog).mockReset().mockResolvedValue(false)
})
it('loads once initially and once for query, reset, status tab and pagination', async () => {
const wrapper = mountView()
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(1)
const toolbar = wrapper.getComponent({ name: 'QueryToolbar' })
toolbar.vm.$emit('search')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(2)
toolbar.vm.$emit('reset')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3)
await wrapper.findAll('.status-tab')[1]!.trigger('click')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(4)
wrapper.getComponent({ name: 'DataTable' }).vm.$emit('update:page', 2)
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(5)
})
it('does not apply draft filters to pagination until query is submitted', async () => {
const wrapper = mountView()
await flushPromises()
const toolbar = wrapper.getComponent({ name: 'QueryToolbar' })
const draft = { ...toolbar.props('modelValue'), inboundCode: ' DRAFT-CODE ' }
toolbar.vm.$emit('update:modelValue', draft)
await wrapper.vm.$nextTick()
wrapper.getComponent({ name: 'DataTable' }).vm.$emit('update:page', 2)
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 2, inboundCode: undefined }))
toolbar.vm.$emit('search')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3)
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, inboundCode: 'DRAFT-CODE' }))
})
it('resets a later page and changes page size with one request per interaction', async () => {
const wrapper = mountView()
await flushPromises()
const table = wrapper.getComponent({ name: 'DataTable' })
const toolbar = wrapper.getComponent({ name: 'QueryToolbar' })
table.vm.$emit('update:page', 3)
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(2)
toolbar.vm.$emit('reset')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3)
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1 }))
table.vm.$emit('update:pageSize', 50)
table.vm.$emit('update:page', 1)
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(4)
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, size: 50 }))
})
it('does not turn a cleared full-container filter into false', async () => {
const wrapper = mountView()
await flushPromises()
const toolbar = wrapper.getComponent({ name: 'QueryToolbar' })
const selected = { ...toolbar.props('modelValue'), fullContainer: 'true' }
toolbar.vm.$emit('update:modelValue', selected)
toolbar.vm.$emit('search')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ fullContainer: true }))
toolbar.vm.$emit('update:modelValue', { ...selected, fullContainer: undefined })
toolbar.vm.$emit('search')
await flushPromises()
expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ fullContainer: undefined }))
})
it('exports the selected business order numbers without loading other pages', async () => {
const wrapper = mountView()
await flushPromises()
wrapper.getComponent({ name: 'DataTable' }).vm.$emit('selection-change', [
{ orderId: '1', orderNo: 'ORDER-1', tenantId: '8' },
{ orderId: '2', orderNo: 'ORDER-2', tenantId: '8' },
])
await wrapper.vm.$nextTick()
await wrapper.findAll('button').find((button) => button.text().includes('导出'))!.trigger('click')
await flushPromises()
expect(createPlatformExportTask).toHaveBeenCalledWith({
exportType: 'DOMESTIC_WAREHOUSE_ORDER_LIST', format: 'XLSX', taskName: '国内仓订单-已选2票',
parameters: { selectedOrderNos: ['ORDER-1', 'ORDER-2'] },
})
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(1)
})
it('exports every result matching the applied filters instead of the current page', async () => {
const wrapper = mountView()
await flushPromises()
const toolbar = wrapper.getComponent({ name: 'QueryToolbar' })
toolbar.vm.$emit('update:modelValue', {
...toolbar.props('modelValue'), inboundCode: ' IN-8 ', loaded: 'false', createdRange: ['2026-08-01', '2026-08-31'],
})
toolbar.vm.$emit('search')
await flushPromises()
await wrapper.findAll('button').find((button) => button.text() === '导出')!.trigger('click')
await flushPromises()
expect(createPlatformExportTask).toHaveBeenCalledWith(expect.objectContaining({
exportType: 'DOMESTIC_WAREHOUSE_ORDER_LIST', taskName: '国内仓订单-当前筛选',
parameters: expect.objectContaining({
inboundCode: 'IN-8', loaded: false,
createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59',
}),
}))
expect(createPlatformExportTask).not.toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({ page: expect.anything(), size: expect.anything() }),
}))
})
it('edits arrival and estimated volume and reports compact failure evidence through a message', async () => {
const errorMessage = vi.spyOn(ElMessage, 'error').mockImplementation(() => undefined as never)
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({
records: [{
orderId: '2089223263733518337', orderVersion: 7, tenantId: '2089176869176389634',
internalOrderStatus: 'SUBMITTED',
inboundCode: 'NYEXX2609030001', expectedArrivalAt: '2026-09-10T08:30:00', estimatedVolumeCbm: 0.25,
}], page: 1, size: 20, total: 1, pages: 1,
} as never)
vi.mocked(updatePlatformUnifiedOrderPlanning)
.mockResolvedValueOnce({ version: 8 } as never)
.mockRejectedValueOnce(new ApiResponseError('订单版本已变化', 'A0409', 200, undefined, 'req-planning', 'trace-planning'))
const wrapper = mountView()
await flushPromises()
const volume = wrapper.getComponent({ name: 'ElInputNumber' })
volume.vm.$emit('update:modelValue', 0.5)
volume.vm.$emit('change', 0.5)
await flushPromises()
expect(updatePlatformUnifiedOrderPlanning).toHaveBeenNthCalledWith(1,
'2089176869176389634', '2089223263733518337', {
expectedVersion: 7, expectedArrivalAt: '2026-09-10T08:30:00', estimatedVolumeCbm: 0.5,
})
const arrival = wrapper.getComponent({ name: 'ElDatePicker' })
arrival.vm.$emit('update:modelValue', '2026-09-12T16:00:00')
arrival.vm.$emit('change', '2026-09-12T16:00:00')
await flushPromises()
expect(updatePlatformUnifiedOrderPlanning).toHaveBeenNthCalledWith(2,
'2089176869176389634', '2089223263733518337', {
expectedVersion: 8, expectedArrivalAt: '2026-09-12T16:00:00', estimatedVolumeCbm: 0.5,
})
expect(wrapper.getComponent({ name: 'ElDatePicker' }).props('modelValue')).toBe('2026-09-12T16:00:00')
expect(wrapper.text()).not.toContain('订单版本已变化')
expect(wrapper.find('.planning-error').exists()).toBe(false)
expect(errorMessage).toHaveBeenCalledWith(expect.objectContaining({
message: '订单版本已变化(错误码 A0409;Request ID req-planning;Trace ID trace-planning)',
showClose: true,
}))
})
it('shows planning values without editors after the order enters irreversible fulfillment', async () => {
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({
records: [{
orderId: '2099751993783402497', orderVersion: 7, tenantId: '2097708157074210818',
internalOrderStatus: 'PROCESSING', mainStatus: 'LOADED', inboundCode: 'AT0E9B5BF2839C6944CFA3',
expectedArrivalAt: '2026-10-15T00:00:00', estimatedVolumeCbm: 220,
}], page: 1, size: 20, total: 1, pages: 1,
} as never)
const wrapper = mountView()
await flushPromises()
expect(wrapper.findComponent({ name: 'ElInputNumber' }).exists()).toBe(false)
expect(wrapper.findComponent({ name: 'ElDatePicker' }).exists()).toBe(false)
expect(wrapper.text()).toContain('220')
expect(wrapper.text()).toContain('2026-10-15 00:00')
expect(updatePlatformUnifiedOrderPlanning).not.toHaveBeenCalled()
})
})