39 lines
3.5 KiB
TypeScript
39 lines
3.5 KiB
TypeScript
import { flushPromises, mount } from '@vue/test-utils'
|
|
import ElementPlus from 'element-plus'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import AppointmentImpactDialog from './AppointmentImpactDialog.vue'
|
|
import { getImpact, handleImpact, type ImpactDetail } from './impactApi'
|
|
vi.mock('./impactApi', async importOriginal => ({ ...await importOriginal<typeof import('./impactApi')>(), getImpact: vi.fn(), handleImpact: vi.fn() }))
|
|
vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) }))
|
|
const detail: ImpactDetail = { impact: { containerBookingId: '1', impactNo: 'AI-TEST', version: 1, status: 'OPEN', reason: 'FORECAST_CHANGED', ownerId: null, ownerName: null, baseline: { estimatedAt: '2026-10-15T09:00:00', actualAt: null, observedAt: null, providerCode: 'carrier', sourceReference: 'ref' }, latest: { estimatedAt: '2026-10-17T09:00:00', actualAt: null, observedAt: null, providerCode: 'carrier', sourceReference: 'ref' }, forecastToken: 'current', updatedAt: '2026-10-10T12:00:00' }, cargo: [{ inboundCode: 'IN-A', customerName: '客户甲', packages: 10, destination: 'ONT8' }], appointments: [{ appointmentNo: 'AP-1', status: 'SUCCESS', destination: 'ONT8', confirmedAt: '2026-10-16T09:00:00', timezone: 'America/Los_Angeles' }], transport: null, history: { records: [], total: 0 } }
|
|
const render = (canWrite = true) => mount(AppointmentImpactDialog, { props: { containerId: '1', canWrite }, global: { plugins: [ElementPlus], stubs: { AppDialog: { template: '<section><slot /><button data-close @click="$emit(\'update:model-value\',false)">关闭</button></section>' } } } })
|
|
describe('appointment impact handling', () => {
|
|
beforeEach(() => { vi.resetAllMocks(); vi.mocked(getImpact).mockResolvedValue(detail) })
|
|
it('shows separate forecasts, unchanged confirmed appointment and omitted transport permission', async () => {
|
|
const wrapper = render(false); await flushPromises()
|
|
expect(wrapper.text()).toContain('2026-10-15T09:00:00')
|
|
expect(wrapper.text()).toContain('2026-10-17T09:00:00')
|
|
expect(wrapper.text()).toContain('2026-10-16T09:00:00')
|
|
expect(wrapper.text()).toContain('无车次查看权限')
|
|
expect(wrapper.text()).not.toContain('认领跟进')
|
|
expect(handleImpact).not.toHaveBeenCalled(); wrapper.unmount()
|
|
})
|
|
it('freezes an uncertain command and retries exactly once with original version and key', async () => {
|
|
vi.mocked(handleImpact).mockRejectedValueOnce(new Error('connection lost')).mockResolvedValueOnce({ ...detail.impact, version: 2 })
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(b => b.text() === '认领跟进')!.trigger('click'); await flushPromises()
|
|
const original = vi.mocked(handleImpact).mock.calls[0]![1]
|
|
expect(original).toMatchObject({ action: 'CLAIM', expectedVersion: 1, forecastToken: 'current' })
|
|
await wrapper.get('[data-close]').trigger('click'); expect(wrapper.emitted('close')).toBeUndefined()
|
|
await wrapper.findAll('button').find(b => b.text() === '重试本次处理')!.trigger('click'); await flushPromises()
|
|
expect(handleImpact).toHaveBeenNthCalledWith(2, '1', original)
|
|
expect(wrapper.emitted('saved')).toHaveLength(1)
|
|
expect(getImpact).toHaveBeenCalledTimes(2); wrapper.unmount()
|
|
})
|
|
it('does not resolve without an actual handling result', async () => {
|
|
const wrapper = render(); await flushPromises()
|
|
await wrapper.findAll('button').find(b => b.text() === '完成本次核对')!.trigger('click'); await flushPromises()
|
|
expect(handleImpact).not.toHaveBeenCalled(); wrapper.unmount()
|
|
})
|
|
})
|