优化
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
import ElementPlus, { ElMessageBox } from 'element-plus'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import {
|
||||
getInboundMeasurement,
|
||||
saveInboundMeasurementDraft,
|
||||
submitInboundMeasurement,
|
||||
} from './api'
|
||||
import type { InboundMeasurementDetail } from './types'
|
||||
import InboundMeasurementPanel from './InboundMeasurementPanel.vue'
|
||||
|
||||
vi.mock('./api', () => ({
|
||||
getInboundMeasurement: vi.fn(),
|
||||
saveInboundMeasurementDraft: vi.fn(),
|
||||
submitInboundMeasurement: vi.fn(),
|
||||
}))
|
||||
|
||||
class ResizeObserverStub implements ResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
function createMemoryStorage(): 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 measurement(overrides: Partial<InboundMeasurementDetail> = {}): InboundMeasurementDetail {
|
||||
return {
|
||||
inboundId: '7001',
|
||||
inboundCode: 'RK202608040001',
|
||||
companyName: '测试企业',
|
||||
channelCode: 'SEA-FBA',
|
||||
channelName: '美西海运',
|
||||
processStatus: 'MEASURING',
|
||||
measureReviewStatus: null,
|
||||
recommendedPalletType: 'STANDARD',
|
||||
reviewReason: null,
|
||||
reviewerId: null,
|
||||
reviewerName: null,
|
||||
measurementId: '9001',
|
||||
sourceType: 'THIRD_PARTY',
|
||||
measurementStatus: 'RECEIVED',
|
||||
version: 2,
|
||||
originalDocumentNo: 'DOC-001',
|
||||
originalGrossWeightKg: 118.5,
|
||||
originalVolumeCbm: 0.8,
|
||||
actualPackageCount: 10,
|
||||
actualGrossWeightKg: 120,
|
||||
actualVolumeCbm: 0.6,
|
||||
volumeWeightKg: 100.2,
|
||||
actualGoodsName: '蓝牙音箱',
|
||||
items: [{
|
||||
id: '9101',
|
||||
itemNo: '001',
|
||||
boxSpecification: '50×40×30cm',
|
||||
packageCount: 10,
|
||||
lengthCm: 50,
|
||||
widthCm: 40,
|
||||
heightCm: 30,
|
||||
girthCm: 190,
|
||||
unitWeightKg: 12,
|
||||
unitVolumeCbm: 0.06,
|
||||
totalWeightKg: 120,
|
||||
totalVolumeCbm: 0.6,
|
||||
volumeWeightKg: 100.2,
|
||||
intelligentImageFileId: '88001',
|
||||
overLimitWarning: null,
|
||||
outerLengthCm: null,
|
||||
outerWidthCm: null,
|
||||
outerHeightCm: null,
|
||||
remark: '设备自动回传',
|
||||
}],
|
||||
weightResults: [],
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
async function mountPanel(permissions = [
|
||||
'platform:firstleg:inbound:measure:view',
|
||||
'platform:firstleg:inbound:measure:edit',
|
||||
'platform:firstleg:inbound:measure:submit',
|
||||
]) {
|
||||
const pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
const auth = useAuthStore()
|
||||
auth.token = 'token'
|
||||
auth.permissions = permissions
|
||||
useAccessPreviewStore().initializeAfterLogin()
|
||||
const wrapper = mount(InboundMeasurementPanel, {
|
||||
props: { customerId: '10001', inboundId: '7001' },
|
||||
attachTo: document.body,
|
||||
global: { plugins: [pinia, ElementPlus] },
|
||||
})
|
||||
await flushPromises()
|
||||
return wrapper
|
||||
}
|
||||
|
||||
enableAutoUnmount(afterEach)
|
||||
afterAll(() => vi.unstubAllGlobals())
|
||||
|
||||
describe('InboundMeasurementPanel', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
|
||||
vi.stubGlobal('localStorage', createMemoryStorage())
|
||||
vi.stubGlobal('sessionStorage', createMemoryStorage())
|
||||
document.body.innerHTML = ''
|
||||
vi.mocked(getInboundMeasurement).mockReset().mockResolvedValue(measurement())
|
||||
vi.mocked(saveInboundMeasurementDraft).mockReset().mockResolvedValue({ measurementId: '9001', version: 3, status: 'DRAFT' })
|
||||
vi.mocked(submitInboundMeasurement).mockReset().mockResolvedValue({
|
||||
measurementId: '9001', version: 2, processStatus: 'MEASURE_PENDING', measureReviewStatus: 'PENDING',
|
||||
recommendedPalletType: 'STANDARD', feeId: null, feeNo: null, feeStatus: null,
|
||||
})
|
||||
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
|
||||
})
|
||||
|
||||
it('loads third-party facts and renders server-calculated values separately', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
|
||||
expect(getInboundMeasurement).toHaveBeenCalledWith('10001', '7001')
|
||||
expect(wrapper.text()).toContain('第三方设备')
|
||||
expect(wrapper.text()).toContain('RK202608040001')
|
||||
expect(wrapper.text()).toContain('箱规周长')
|
||||
expect(wrapper.text()).toContain('190 cm')
|
||||
expect(wrapper.text()).toContain('0.6 CBM')
|
||||
expect((wrapper.find('input[placeholder="请输入本箱规的补充说明"]').element as HTMLInputElement).value).toBe('设备自动回传')
|
||||
})
|
||||
|
||||
it('follows the prototype grouping, labels, and editable field order', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
const text = wrapper.text()
|
||||
|
||||
expect(text).toContain('原单数据')
|
||||
expect(text).toContain('入库单测量数据明细')
|
||||
expect(text).toContain('实测总箱数')
|
||||
expect(text).toContain('实测总毛重')
|
||||
expect(text).toContain('实测总体积')
|
||||
expect(text).toContain('实测总材积重')
|
||||
expect(text).not.toContain('租户')
|
||||
|
||||
const labels = wrapper.findAll('.measurement-item__grid label > span').map((label) => label.text())
|
||||
expect(labels.slice(0, 8)).toEqual([
|
||||
'明细编号 *',
|
||||
'箱规 *',
|
||||
'箱规长(cm)*',
|
||||
'箱规宽(cm)*',
|
||||
'箱规高(cm)*',
|
||||
'箱数 *',
|
||||
'每箱毛重(kg)*',
|
||||
'智能图片文件 ID',
|
||||
])
|
||||
})
|
||||
|
||||
it('reuses the established first-leg detail surface and status patterns', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
|
||||
expect(wrapper.classes()).toContain('surface-panel')
|
||||
expect(wrapper.find('.measurement-panel__heading').classes()).toContain('panel-heading')
|
||||
expect(wrapper.find('.status-badge').text()).toBe('测量中')
|
||||
})
|
||||
|
||||
it('saves only editable raw fields with the current measurement version', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
await wrapper.find('input[placeholder="请输入实际货物名称"]').setValue('智能音箱')
|
||||
const save = wrapper.findAll('button').find((button) => button.text().includes('保存草稿'))
|
||||
await save?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(saveInboundMeasurementDraft).toHaveBeenCalledOnce()
|
||||
expect(saveInboundMeasurementDraft).toHaveBeenCalledWith('10001', '7001', expect.objectContaining({
|
||||
measurementId: '9001',
|
||||
version: 2,
|
||||
actualGoodsName: '智能音箱',
|
||||
items: [expect.objectContaining({
|
||||
itemNo: '001', packageCount: 10, lengthCm: 50, intelligentImageFileId: '88001',
|
||||
})],
|
||||
}))
|
||||
const payload = vi.mocked(saveInboundMeasurementDraft).mock.calls[0][2]
|
||||
expect(payload.items[0]).not.toHaveProperty('totalVolumeCbm')
|
||||
})
|
||||
|
||||
it('preserves the edited form when the backend reports a version conflict', async () => {
|
||||
vi.mocked(saveInboundMeasurementDraft).mockRejectedValueOnce(
|
||||
new ApiResponseError('数据版本已变化,请刷新后重试', 'A0409', 200, null, 'req-conflict', 'trace-conflict'),
|
||||
)
|
||||
const wrapper = await mountPanel()
|
||||
const goodsInput = wrapper.find('input[placeholder="请输入实际货物名称"]')
|
||||
await goodsInput.setValue('保留我的修改')
|
||||
const save = wrapper.findAll('button').find((button) => button.text().includes('保存草稿'))
|
||||
await save?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).toContain('数据版本已变化,当前修改尚未丢失')
|
||||
expect(wrapper.text()).toContain('req-conflict')
|
||||
expect((goodsInput.element as HTMLInputElement).value).toBe('保留我的修改')
|
||||
expect(getInboundMeasurement).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('requires the dirty draft to be saved before submitting for review', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
await wrapper.find('input[placeholder="请输入实际货物名称"]').setValue('尚未保存')
|
||||
const submit = wrapper.findAll('button').find((button) => button.text().includes('提交审核'))
|
||||
await submit?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(submitInboundMeasurement).not.toHaveBeenCalled()
|
||||
expect(ElMessageBox.confirm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('submits the saved version and emits changed after confirmation', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
const submit = wrapper.findAll('button').find((button) => button.text().includes('提交审核'))
|
||||
await submit?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(ElMessageBox.confirm).toHaveBeenCalledWith(
|
||||
expect.stringContaining('准备托盘与库位占用'),
|
||||
'提交测量审核',
|
||||
expect.objectContaining({ confirmButtonText: '确认提交' }),
|
||||
)
|
||||
expect(submitInboundMeasurement).toHaveBeenCalledWith('10001', '7001', { measurementId: '9001', version: 2 })
|
||||
expect(wrapper.emitted('changed')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('clears the previous customer context before loading the next one', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
vi.mocked(getInboundMeasurement).mockResolvedValueOnce(measurement({ inboundId: '7002', inboundCode: 'RK202608040002', companyName: '第二企业' }))
|
||||
|
||||
await wrapper.setProps({ customerId: '10002', inboundId: '7002' })
|
||||
await flushPromises()
|
||||
|
||||
expect(getInboundMeasurement).toHaveBeenLastCalledWith('10002', '7002')
|
||||
expect(wrapper.text()).toContain('RK202608040002')
|
||||
expect(wrapper.text()).not.toContain('测试企业')
|
||||
})
|
||||
|
||||
it('renders a permission-specific read-only state without requesting data', async () => {
|
||||
const wrapper = await mountPanel([])
|
||||
|
||||
expect(wrapper.text()).toContain('无测量查看权限')
|
||||
expect(getInboundMeasurement).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user