优化优化
This commit is contained in:
@@ -1,5 +1,138 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import ElementPlus from 'element-plus'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue'), 'utf8')
|
||||
describe('OutboundDispatchesView', () => { it('uses standard list components and left actions', () => { expect(source).toContain('QueryToolbar'); expect(source).toContain('DataTable'); expect(source).toContain("fixed: 'left'") }); it('waits for a business id before querying', () => { expect(source).not.toContain('onMounted(load)'); expect(source).toContain("String(filters.orderId || '').trim() || undefined"); expect(source).toContain('if (!params.orderId && !params.inboundId && !params.containerBookingId)'); expect(source).toContain('请输入订单 ID、入库单 ID 或柜次 ID 后查询'); expect(source).toContain('@reset="resetSearch"') }); it('shows gate, timeline and guarded dispatch actions', () => { expect(source).toContain('aggregate.gate?.items'); expect(source).toContain('detail.timeline'); expect(source).toContain("'confirm-dispatch'"); expect(source).toContain("'reverse-dispatch'"); expect(source).toContain("error.value?.code === 'A0409'") }) })
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import QueryToolbar from '@/shared/components/QueryToolbar.vue'
|
||||
import DataTable from '@/shared/components/DataTable.vue'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { listPlatformUnifiedOrders, type UnifiedOrderResource } from '@/modules/unified-orders'
|
||||
import { listContainers, type ContainerBookingResource } from '@/modules/container-loading'
|
||||
import { getOutboundDispatchDetail, listOutboundDispatches } from '../api/platformOutboundDispatch'
|
||||
import type { OutboundDispatchResource } from '../types'
|
||||
import OutboundDispatchesView from './OutboundDispatchesView.vue'
|
||||
|
||||
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() }))
|
||||
vi.mock('@/modules/unified-orders', () => ({ listPlatformUnifiedOrders: vi.fn() }))
|
||||
vi.mock('@/modules/container-loading', () => ({ listContainers: vi.fn() }))
|
||||
vi.mock('../api/platformOutboundDispatch', () => ({ listOutboundDispatches: vi.fn(), getOutboundDispatchDetail: vi.fn(), changeOutboundDispatch: vi.fn() }))
|
||||
|
||||
const customer = { id: '10001', tenantCode: 'CUS-1', tenantName: '示例客户', legalName: '示例客户有限公司', status: 'ACTIVE' } as PlatformTenantResource
|
||||
const order = { orderId: '20001', orderNo: 'ORD-001', inboundId: '30001', inboundCode: 'IN-001', customerId: '10001', customerName: '示例客户' } as UnifiedOrderResource
|
||||
const secondOrder = { ...order, orderId: '20002', orderNo: 'ORD-002', inboundId: '30002', inboundCode: 'IN-002' } as UnifiedOrderResource
|
||||
const container = { id: '40001', bookingNo: 'BK-001', cabinetNo: 'CAB-001', containerNo: 'MSCU-001' } as ContainerBookingResource
|
||||
const row = { orderId: '20001', orderNo: 'ORD-001', inboundId: '30001', inboundCode: 'IN-001', customerId: '10001', customerName: '示例客户', containerBookingId: '40001', containerNo: 'MSCU-001', dispatchStatus: 'WAITING', version: 1, gate: { passed: true, items: [], checkedAt: '2026-08-11T10:00:00' } } as OutboundDispatchResource
|
||||
|
||||
enableAutoUnmount(afterEach)
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void
|
||||
let reject!: (reason?: unknown) => void
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => { resolve = resolvePromise; reject = rejectPromise })
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
|
||||
function mountPage() {
|
||||
return mount(OutboundDispatchesView, { attachTo: document.body, global: { plugins: [ElementPlus, createPinia()] } })
|
||||
}
|
||||
|
||||
async function setFilters(wrapper: ReturnType<typeof mountPage>, values: Record<string, string>) {
|
||||
const toolbar = wrapper.findComponent(QueryToolbar)
|
||||
toolbar.vm.$emit('update:modelValue', { ...toolbar.props('modelValue'), ...values })
|
||||
await flushPromises()
|
||||
return toolbar
|
||||
}
|
||||
|
||||
describe('OutboundDispatchesView', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
setActivePinia(createPinia())
|
||||
vi.mocked(listPlatformTenants).mockResolvedValue({ records: [customer], page: 1, size: 200, total: 1, pages: 1 })
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: [order, secondOrder], page: 1, size: 200, total: 2, pages: 1 })
|
||||
vi.mocked(listContainers).mockResolvedValue({ records: [container], page: 1, size: 200, total: 1, pages: 1 })
|
||||
vi.mocked(listOutboundDispatches).mockResolvedValue([row])
|
||||
})
|
||||
|
||||
it('starts idle and queries real ids selected through business labels', async () => {
|
||||
const wrapper = mountPage()
|
||||
await flushPromises()
|
||||
expect(listOutboundDispatches).not.toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('请选择订单 / 入库单或柜次后查询')
|
||||
const toolbar = await setFilters(wrapper, { orderId: '20001' })
|
||||
toolbar.vm.$emit('search')
|
||||
await flushPromises()
|
||||
expect(listOutboundDispatches).toHaveBeenCalledOnce()
|
||||
expect(listOutboundDispatches).toHaveBeenCalledWith({ orderId: '20001', inboundId: '30001', containerBookingId: undefined })
|
||||
expect(wrapper.text()).toContain('ORD-001')
|
||||
})
|
||||
|
||||
it('does not issue a search when reset returns to the initial state', async () => {
|
||||
const wrapper = mountPage()
|
||||
await flushPromises()
|
||||
const toolbar = await setFilters(wrapper, { containerBookingId: '40001' })
|
||||
toolbar.vm.$emit('search')
|
||||
await flushPromises()
|
||||
expect(listOutboundDispatches).toHaveBeenCalledOnce()
|
||||
const resetButton = wrapper.findAll('button').find((button) => button.text().includes('重置'))
|
||||
await resetButton?.trigger('click')
|
||||
await flushPromises()
|
||||
expect(listOutboundDispatches).toHaveBeenCalledOnce()
|
||||
expect(wrapper.text()).toContain('请选择订单 / 入库单或柜次后查询')
|
||||
})
|
||||
|
||||
it('shows loading, empty and recoverable error states', async () => {
|
||||
const pending = deferred<OutboundDispatchResource[]>()
|
||||
vi.mocked(listOutboundDispatches).mockReturnValueOnce(pending.promise)
|
||||
const wrapper = mountPage()
|
||||
await flushPromises()
|
||||
const toolbar = await setFilters(wrapper, { containerBookingId: '40001' })
|
||||
toolbar.vm.$emit('search')
|
||||
await flushPromises()
|
||||
expect(wrapper.findComponent(DataTable).props('loading')).toBe(true)
|
||||
pending.resolve([])
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('暂无符合条件的出仓记录')
|
||||
|
||||
vi.mocked(listOutboundDispatches).mockRejectedValueOnce(new Error('network down')).mockResolvedValueOnce([row])
|
||||
toolbar.vm.$emit('search')
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('国内出仓加载失败')
|
||||
const retry = wrapper.findAll('button').find((button) => button.text().includes('重新加载'))
|
||||
await retry?.trigger('click')
|
||||
await flushPromises()
|
||||
expect(listOutboundDispatches).toHaveBeenCalledTimes(3)
|
||||
expect(wrapper.text()).toContain('ORD-001')
|
||||
})
|
||||
|
||||
it('ignores stale list responses when a newer query wins', async () => {
|
||||
const first = deferred<OutboundDispatchResource[]>()
|
||||
const second = deferred<OutboundDispatchResource[]>()
|
||||
vi.mocked(listOutboundDispatches).mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise)
|
||||
const wrapper = mountPage()
|
||||
await flushPromises()
|
||||
let toolbar = await setFilters(wrapper, { orderId: '20001' })
|
||||
toolbar.vm.$emit('search')
|
||||
toolbar = await setFilters(wrapper, { orderId: '20002' })
|
||||
toolbar.vm.$emit('search')
|
||||
second.resolve([{ ...row, orderId: '20002', orderNo: 'ORD-002', inboundId: '30002', inboundCode: 'IN-002' }])
|
||||
await flushPromises()
|
||||
first.resolve([row])
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('ORD-002')
|
||||
expect(wrapper.text()).not.toContain('ORD-001')
|
||||
})
|
||||
|
||||
it('keeps the operation column first and guarded workflow actions', () => {
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue'), 'utf8')
|
||||
const columnSource = source.slice(source.indexOf('const columns'))
|
||||
expect(columnSource.indexOf("key: 'actions'")).toBeLessThan(columnSource.indexOf("prop: 'orderNo'"))
|
||||
expect(columnSource).toContain("fixed: 'left'")
|
||||
expect(source).toContain('aggregate.gate?.items')
|
||||
expect(source).toContain('detail.timeline')
|
||||
expect(source).toContain("'confirm-dispatch'")
|
||||
expect(source).toContain("'reverse-dispatch'")
|
||||
expect(source).toContain("error.value?.code === 'A0409'")
|
||||
expect(getOutboundDispatchDetail).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user