Files
admin-web/src/modules/overseas-warehouse/pages/RelabelOrdersView.test.ts
T

148 lines
8.6 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 ElementPlus from 'element-plus'
import { enableAutoUnmount, flushPromises, mount, type VueWrapper } from '@vue/test-utils'
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { listPlatformTenants } from '@/modules/iam'
import AppDialog from '@/shared/components/AppDialog.vue'
import {
changeRelabelOrderStatus,
createRelabelOrder,
listRelabelOrders,
listRelabelWarehouseOptions,
} from '../api/relabelOrders'
import RelabelOrdersView from './RelabelOrdersView.vue'
vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() }))
vi.mock('../api/relabelOrders', () => ({
changeRelabelOrderStatus: vi.fn(),
createRelabelOrder: vi.fn(),
listRelabelOrders: vi.fn(),
listRelabelWarehouseOptions: vi.fn(),
}))
class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} }
function memoryStorage(): Storage { const values = new Map<string, string>(); return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value) } }
function formItem(wrapper: VueWrapper<any>, label: string) {
const item = wrapper.findAllComponents({ name: 'ElFormItem' }).find((candidate) => candidate.props('label') === label)
if (!item) throw new Error(`Missing form item: ${label}`)
return item
}
function select(wrapper: VueWrapper<any>, placeholder: string) {
const target = wrapper.findAllComponents({ name: 'ElSelect' }).find((candidate) => candidate.props('placeholder') === placeholder)
if (!target) throw new Error(`Missing select: ${placeholder}`)
return target
}
enableAutoUnmount(afterEach)
afterAll(() => vi.unstubAllGlobals())
describe('RelabelOrdersView', () => {
beforeEach(() => {
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
vi.stubGlobal('localStorage', memoryStorage())
vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [{ id: '10001', tenantName: '客户甲', status: 'ACTIVE' } as never], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(listRelabelOrders).mockReset().mockResolvedValue({ records: [], page: 1, size: 10, total: 0, pages: 0 })
vi.mocked(listRelabelWarehouseOptions).mockReset()
vi.mocked(createRelabelOrder).mockReset()
vi.mocked(changeRelabelOrderStatus).mockReset()
})
it('loads shared receiving warehouse options independently from the selected customer', async () => {
vi.mocked(listRelabelWarehouseOptions).mockResolvedValue({ records: [{ id: 'WH-B', customerId: '0', warehouseCode: 'B', warehouseName: 'B仓', warehouseType: 'OVERSEAS', status: 'ACTIVE' }], page: 1, size: 200, total: 1, pages: 1 })
const wrapper = mount(RelabelOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.findAll('button').find((button) => button.text().includes('新增换标单'))!.trigger('click')
await flushPromises()
const customer = select(wrapper, '请选择 ACTIVE 客户')
customer.vm.$emit('update:modelValue', '10001'); customer.vm.$emit('change', '10001')
await flushPromises()
const labels = wrapper.findAllComponents({ name: 'ElOption' }).map((option) => option.props('label'))
expect(listRelabelWarehouseOptions).toHaveBeenCalledOnce()
expect(labels).toContain('B仓 · B')
})
it('reuses one idempotency key after a failed create retry', async () => {
vi.mocked(listRelabelWarehouseOptions).mockResolvedValue({ records: [{ id: 'WH-1', customerId: '10001', warehouseCode: 'LAX', warehouseName: '洛杉矶仓', warehouseType: 'OVERSEAS', status: 'ACTIVE' }], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(createRelabelOrder).mockRejectedValueOnce(new Error('temporary')).mockResolvedValueOnce({ application: { id: '1', relabelNo: 'RL-1' }, items: [], history: [] })
const wrapper = mount(RelabelOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.findAll('button').find((button) => button.text().includes('新增换标单'))!.trigger('click')
await flushPromises()
const customer = select(wrapper, '请选择 ACTIVE 客户')
customer.vm.$emit('update:modelValue', '10001'); customer.vm.$emit('change', '10001')
await flushPromises()
const warehouse = select(wrapper, '请选择 ACTIVE+RECEIVING 海外仓')
warehouse.vm.$emit('update:modelValue', 'WH-1')
formItem(wrapper, '关联订单').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', 'CO-1')
formItem(wrapper, '商品SKU').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', 'SKU-1')
formItem(wrapper, '中文品名').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '商品一')
formItem(wrapper, '箱数').findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 2)
formItem(wrapper, '换标费用(CNY)').findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 20)
formItem(wrapper, '截止时间').findComponent({ name: 'ElDatePicker' }).vm.$emit('update:modelValue', '2026-08-30T10:00:00')
formItem(wrapper, '联系人').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '张三')
formItem(wrapper, '联系电话').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '13800000000')
formItem(wrapper, '贴标要求').findComponent({ name: 'ElSelect' }).vm.$emit('update:modelValue', '换SKU标')
formItem(wrapper, '货件号').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', 'SHIP-1')
formItem(wrapper, '新SKU').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', 'SKU-2')
await flushPromises()
const dialog = wrapper.findComponent(AppDialog)
dialog.vm.$emit('confirm')
await flushPromises()
expect(dialog.props('modelValue')).toBe(true)
dialog.vm.$emit('confirm')
await flushPromises()
expect(createRelabelOrder).toHaveBeenCalledTimes(2)
const firstKey = vi.mocked(createRelabelOrder).mock.calls[0]![1].idempotencyKey
const retryKey = vi.mocked(createRelabelOrder).mock.calls[1]![1].idempotencyKey
expect(firstKey).toBeTruthy()
expect(retryKey).toBe(firstKey)
expect(createRelabelOrder).toHaveBeenLastCalledWith('10001', expect.objectContaining({
customerOrderNo: 'CO-1', overseasWarehouseId: 'WH-1', idempotencyKey: firstKey,
items: [expect.objectContaining({ skuCode: 'SKU-1', newSkuCode: 'SKU-2', labelRequirement: '换SKU标' })],
}))
})
it('hides the audit action after approval', async () => {
vi.mocked(listRelabelOrders).mockResolvedValueOnce({
records: [{ id: '1', relabelNo: 'RL-1', customerOrderNo: 'CO-1', skuCodes: 'SKU-1', totalPackageCount: 2, status: 'PENDING_PROCESSING' }],
page: 1, size: 10, total: 1, pages: 1,
})
const wrapper = mount(RelabelOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
expect(wrapper.findAll('button').some((button) => button.text().trim() === '审核')).toBe(false)
})
it('shows the audit action only while pending review', async () => {
vi.mocked(listRelabelOrders).mockResolvedValueOnce({
records: [{ id: '1', relabelNo: 'RL-1', customerOrderNo: 'CO-1', skuCodes: 'SKU-1', totalPackageCount: 2, status: 'PENDING_REVIEW' }],
page: 1, size: 10, total: 1, pages: 1,
})
const wrapper = mount(RelabelOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.findAll('button').find((button) => button.text().trim() === '审核')!.trigger('click')
await flushPromises()
const labels = formItem(wrapper, '审核动作').findAllComponents({ name: 'ElOption' }).map((option) => option.props('label'))
expect(labels).toEqual(['审核通过', '已驳回'])
})
it('passes the relabel order number search to the backend', async () => {
const wrapper = mount(RelabelOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
const input = wrapper.find('input[placeholder="请输入换标单号"]')
expect(input.exists()).toBe(true)
await input.setValue('FBR202608272092853218068373506')
await wrapper.findAll('button').find((button) => button.text().includes('查询'))!.trigger('click')
await flushPromises()
expect(listRelabelOrders).toHaveBeenLastCalledWith(expect.objectContaining({
relabelNo: 'FBR202608272092853218068373506',
}))
})
})