59 lines
4.6 KiB
TypeScript
59 lines
4.6 KiB
TypeScript
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
|
import { shallowMount, flushPromises, type VueWrapper } from '@vue/test-utils'
|
|
import FleetQuoteImport from './FleetQuoteImport.vue'
|
|
import { ApiResponseError } from '@/shared/api/http'
|
|
import { fleetQuoteCsvHeader } from '../fleetQuoteCsv'
|
|
import * as api from '../api/fleetQuotes'
|
|
vi.mock('../api/fleetQuotes', async original => ({ ...(await original<typeof import('../api/fleetQuotes')>()), importFleetQuotes: vi.fn() }))
|
|
const wrappers: VueWrapper[] = []
|
|
async function mount() {
|
|
const wrapper = shallowMount(FleetQuoteImport, { props: { modelValue: true, kind: 'SALE' }, global: { stubs: {
|
|
AppDialog: { template: '<div><slot/><slot name="footer"/></div>' },
|
|
FleetQuotePartySelect: { name: 'FleetQuotePartySelect', props: ['modelValue', 'kind', 'disabled'], emits: ['update:modelValue'], template: '<div/>' },
|
|
ElButton: { props: ['disabled', 'loading'], template: '<button :disabled="disabled || loading"><slot/></button>' },
|
|
ElAlert: { props: ['title'], template: '<p>{{ title }}<slot/></p>' }, ApiErrorMeta: true,
|
|
} } }); wrappers.push(wrapper)
|
|
wrapper.findComponent({ name: 'FleetQuotePartySelect' }).vm.$emit('update:modelValue', '101'); await flushPromises(); return wrapper
|
|
}
|
|
async function pick(wrapper: VueWrapper, city = 'QA-FIRST', size = 100, text?: () => Promise<string>) {
|
|
Object.defineProperty(wrapper.find('input[type="file"]').element, 'files', { configurable: true, value: [{ size, text: text || (async () => `${fleetQuoteCsvHeader.join(',')}\n${city},QA-DEST,10,USD,,0,12,2026-01-01,2100-01-01,`) }] })
|
|
await wrapper.find('input[type="file"]').trigger('change'); await flushPromises()
|
|
}
|
|
async function save(wrapper: VueWrapper) { await wrapper.findAll('button').find(b => ['确认导入', '按原请求重试'].includes(b.text()))!.trigger('click'); await flushPromises() }
|
|
beforeEach(() => vi.clearAllMocks()); afterEach(() => wrappers.splice(0).forEach(w => w.unmount()))
|
|
it('definite import rejection unlocks file selection and corrected rows use a new key', async () => {
|
|
const wrapper = await mount(); await pick(wrapper)
|
|
vi.mocked(api.importFleetQuotes).mockRejectedValueOnce(new ApiResponseError('组合重复', 'A0409', 200)); await save(wrapper)
|
|
const first = vi.mocked(api.importFleetQuotes).mock.calls[0]!
|
|
expect(wrapper.find('input[type="file"]').attributes('disabled')).toBeUndefined()
|
|
await pick(wrapper, 'QA-CORRECTED'); vi.mocked(api.importFleetQuotes).mockResolvedValue({ importedRows: 1 }); await save(wrapper)
|
|
const second = vi.mocked(api.importFleetQuotes).mock.calls[1]!
|
|
expect(second[0]).not.toBe(first[0]); expect(second[1][0]?.originCity).toBe('QA-CORRECTED')
|
|
})
|
|
it('unknown result preserves original purpose, key and rows across closing and category changes', async () => {
|
|
const wrapper = await mount(); await pick(wrapper)
|
|
vi.mocked(api.importFleetQuotes).mockRejectedValueOnce(new Error('network')); await save(wrapper)
|
|
const call = vi.mocked(api.importFleetQuotes).mock.calls[0]!
|
|
const first = [call[0], call[1].map(row => ({ ...row }))]
|
|
await wrapper.setProps({ modelValue: false, kind: 'PURCHASE' }); await wrapper.setProps({ modelValue: true }); await flushPromises()
|
|
expect(wrapper.findComponent({ name: 'FleetQuotePartySelect' }).props('kind')).toBe('SALE')
|
|
expect(wrapper.find('input[type="file"]').attributes('disabled')).toBeDefined()
|
|
vi.mocked(api.importFleetQuotes).mockResolvedValue({ importedRows: 1 }); await save(wrapper)
|
|
expect(vi.mocked(api.importFleetQuotes).mock.calls[1]).toEqual(first)
|
|
})
|
|
it('selecting an oversized replacement file cannot accidentally submit the previous valid file', async () => {
|
|
const wrapper = await mount(); await pick(wrapper); await pick(wrapper, 'TOO-LARGE', 500001)
|
|
expect(wrapper.findAll('button').find(b => b.text() === '确认导入')!.attributes('disabled')).toBeDefined()
|
|
expect(wrapper.text()).not.toContain('QA-FIRST'); expect(api.importFleetQuotes).not.toHaveBeenCalled()
|
|
})
|
|
it('a late old file read cannot replace the most recently selected CSV', async () => {
|
|
const wrapper = await mount(); let release!: (value: string) => void
|
|
const slow = new Promise<string>(resolve => { release = resolve })
|
|
await pick(wrapper, 'SLOW', 100, () => slow)
|
|
await pick(wrapper, 'QA-LATEST')
|
|
release(`${fleetQuoteCsvHeader.join(',')}\nQA-OLD,QA-DEST,10,USD,,0,12,2026-01-01,2100-01-01,`); await flushPromises()
|
|
expect(wrapper.text()).toContain('QA-LATEST'); expect(wrapper.text()).not.toContain('QA-OLD')
|
|
vi.mocked(api.importFleetQuotes).mockResolvedValue({ importedRows: 1 }); await save(wrapper)
|
|
expect(vi.mocked(api.importFleetQuotes).mock.calls[0]?.[1][0]?.originCity).toBe('QA-LATEST')
|
|
})
|