68 lines
4.5 KiB
TypeScript
68 lines
4.5 KiB
TypeScript
import { flushPromises, mount } from '@vue/test-utils'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import AppointmentCargoPanel from './AppointmentCargoPanel.vue'
|
|
import { listPlanningCargo } from './planningApi'
|
|
|
|
vi.mock('./planningApi', () => ({ listPlanningCargo: vi.fn(), resolvePlanningSource: vi.fn() }))
|
|
vi.mock('vue-router', () => ({ onBeforeRouteLeave: vi.fn(), onBeforeRouteUpdate: vi.fn(), useRouter: () => ({ push: vi.fn() }) }))
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
|
|
const empty = { records: [], page: 1, size: 20, total: 0, pages: 0 }
|
|
function render(attach = false) {
|
|
return mount(AppointmentCargoPanel, { attachTo: attach ? document.body : undefined, global: { stubs: {
|
|
DataTable: { name: 'DataTable', props: ['data', 'total', 'resultsVisible'], emits: ['update:page'], template: '<div><slot name="filters"/><slot name="notices"/><button data-next @click="$emit(\'update:page\',2)">下一页</button></div>' },
|
|
QueryToolbar: { name: 'QueryToolbar', emits: ['search'], template: '<button data-search @click="$emit(\'search\')">查询</button>' },
|
|
LoadErrorState: true, ApiErrorMeta: true, RowActions: true, AppointmentExternalCandidates: { name: 'AppointmentExternalCandidates', template: '<section class="integrated-query-group"><slot name="query-tabs"/></section>' },
|
|
} } })
|
|
}
|
|
describe('appointment planning cargo query', () => {
|
|
beforeEach(() => { vi.resetAllMocks(); vi.mocked(listPlanningCargo).mockResolvedValue(empty) })
|
|
it('switches cargo tabs by click and keyboard without reloading first-leg cargo', async () => {
|
|
const wrapper = render(true); await flushPromises()
|
|
const tabs = () => wrapper.findAll('[role="tab"]')
|
|
expect(tabs().map(tab => tab.text())).toEqual(['头程货物', '外接货物'])
|
|
expect(wrapper.find('.integrated-query-group [role="tablist"]').exists()).toBe(true)
|
|
expect(tabs()[0]!.attributes('aria-selected')).toBe('true')
|
|
await tabs()[1]!.trigger('click')
|
|
expect(tabs()[1]!.attributes('aria-selected')).toBe('true')
|
|
expect(wrapper.findComponent({ name: 'AppointmentExternalCandidates' }).exists()).toBe(true)
|
|
await tabs()[1]!.trigger('keydown', { key: 'ArrowRight' })
|
|
await flushPromises()
|
|
expect(tabs()[0]!.attributes('aria-selected')).toBe('true')
|
|
expect(document.activeElement).toBe(tabs()[0]!.element)
|
|
expect(tabs()[1]!.attributes('tabindex')).toBe('-1')
|
|
await tabs()[0]!.trigger('keydown', { key: 'End' })
|
|
await flushPromises()
|
|
expect(tabs()[1]!.attributes('aria-selected')).toBe('true')
|
|
expect(document.activeElement).toBe(tabs()[1]!.element)
|
|
await tabs()[1]!.trigger('keydown', { key: 'Home' })
|
|
expect(tabs()[0]!.attributes('aria-selected')).toBe('true')
|
|
expect(listPlanningCargo).toHaveBeenCalledTimes(1)
|
|
wrapper.unmount()
|
|
})
|
|
it('uses one server-page request per entry, page change and reset', async () => {
|
|
const wrapper = render(); await flushPromises()
|
|
expect(listPlanningCargo).toHaveBeenCalledTimes(1)
|
|
await wrapper.get('[data-next]').trigger('click'); await flushPromises()
|
|
expect(listPlanningCargo).toHaveBeenLastCalledWith({ page: 2, size: 20, keyword: undefined }, expect.any(AbortSignal))
|
|
await wrapper.get('[data-search]').trigger('click'); await flushPromises()
|
|
expect(listPlanningCargo).toHaveBeenCalledTimes(3)
|
|
expect(listPlanningCargo).toHaveBeenLastCalledWith({ page: 1, size: 20, keyword: undefined }, expect.any(AbortSignal))
|
|
})
|
|
it('shows a source failure instead of an authoritative empty list', async () => {
|
|
vi.mocked(listPlanningCargo).mockRejectedValueOnce(new Error('目录不可用'))
|
|
const wrapper = render(); await flushPromises()
|
|
expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(true)
|
|
expect(wrapper.findComponent({ name: 'DataTable' }).props('resultsVisible')).toBe(false)
|
|
})
|
|
it('discards a late previous page and uses the new server total', async () => {
|
|
let resolveOld!: (value: typeof empty) => void
|
|
vi.mocked(listPlanningCargo).mockImplementationOnce(() => new Promise(resolve => { resolveOld = resolve }))
|
|
const wrapper = render()
|
|
vi.mocked(listPlanningCargo).mockResolvedValueOnce({ ...empty, page: 2, total: 21, pages: 2 })
|
|
await wrapper.get('[data-next]').trigger('click'); await flushPromises()
|
|
resolveOld({ ...empty, total: 100, pages: 5 }); await flushPromises()
|
|
expect(wrapper.findComponent({ name: 'DataTable' }).props('total')).toBe(21)
|
|
expect(vi.mocked(listPlanningCargo).mock.calls[0]![1]!.aborted).toBe(true)
|
|
})
|
|
})
|