116 lines
7.8 KiB
TypeScript
116 lines
7.8 KiB
TypeScript
import { mount } from '@vue/test-utils'
|
|
import { describe, expect, it } from 'vitest'
|
|
import AppointmentCalendarPanel from './AppointmentCalendarPanel.vue'
|
|
import { scheduleTypes, type ScheduleEvent } from './schedule'
|
|
import { projectTripCalendar, type TripCalendarEntry } from './tripCalendar'
|
|
|
|
const event = (type: ScheduleEvent['type'], index = 0): ScheduleEvent => ({ key: `${type}-${index}`, type, date: '2026-10-19', time: '', title: `TEST-CONT ${type}`, fields: [{ label: '仓库', value: 'ONT8' }] })
|
|
const render = (events: ScheduleEvent[] = [], initialDate = '2026-10-19') => mount(AppointmentCalendarPanel, { props: { initialDate, events },
|
|
global: { stubs: { ElButton: { template: '<button><slot /></button>' } } } })
|
|
function reportedDispatch() {
|
|
const row: TripCalendarEntry = { sourceType: 'DISPATCH', dispatchNo: 'PC-CALENDAR-001', containerNumbers: ['TSTU1234567', 'TSTU7654321'], status: 'PLANNED', originWarehouseId: '1', originWarehouseCode: 'LAX', originWarehouseName: '洛杉矶仓', originCountryCode: 'US', originTimezone: 'America/Los_Angeles', originWarehouseStatus: 'ACTIVE', plannedDepartureAt: '2026-10-22T16:00:00Z', expectedDriverEndAt: null, departedAt: null, completedAt: null, stops: [{ name: '洛杉矶仓' }, { name: 'ONT8' }], incompleteReasons: [] }
|
|
return projectTripCalendar({ records: [row, row], unresolvedRecords: [] }, { from: '2026-10-01', to: '2026-11-01' }).events
|
|
}
|
|
describe('four-type appointment calendar', () => {
|
|
it.each(['TSTU1234567', 'tstu1234567', 'TSTU7654321', ' tstu7654321 '])('finds the one shared dispatch by associated container %s on its origin-local date, not the Beijing date', async keyword => {
|
|
const wrapper = render(reportedDispatch(), '2026-10-23')
|
|
const search = wrapper.get('[aria-label="搜索当前日历"]')
|
|
expect(search.attributes('placeholder')).toBe('柜号、排车单号、仓库')
|
|
await search.setValue(keyword)
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(0)
|
|
expect(wrapper.get('[aria-label="当天安排"]').text()).toContain('当天暂无安排')
|
|
expect(wrapper.get('[data-date="2026-10-23"]').findAll('.event-chip')).toHaveLength(0)
|
|
expect(wrapper.get('[data-date="2026-10-22"]').findAll('.event-chip')).toHaveLength(1)
|
|
await wrapper.get('[data-date="2026-10-22"]').trigger('click')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(1)
|
|
expect(wrapper.get('.detail-event').text()).toContain('PC-CALENDAR-001')
|
|
expect(wrapper.get('.detail-event').text()).toContain('09:00')
|
|
expect(wrapper.get('.detail-event').text()).toContain('TSTU1234567、TSTU7654321')
|
|
expect(wrapper.get('.detail-event').text()).toContain('2026-10-22 09:00')
|
|
expect(wrapper.emitted('range')).toBeUndefined()
|
|
wrapper.unmount()
|
|
})
|
|
it('does not match an unrelated container and can find the same event by the dispatch number', async () => {
|
|
const wrapper = render(reportedDispatch(), '2026-10-22')
|
|
await wrapper.get('[aria-label="搜索当前日历"]').setValue('UNRELATED9999')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(0)
|
|
expect(wrapper.findAll('.event-chip')).toHaveLength(0)
|
|
await wrapper.get('[aria-label="搜索当前日历"]').setValue('PC-CALENDAR-001')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(1)
|
|
expect(wrapper.findAll('.event-chip')).toHaveLength(1)
|
|
expect(wrapper.emitted('range')).toBeUndefined()
|
|
wrapper.unmount()
|
|
})
|
|
it('shows all four types by default with a calendar and persistent daily detail', () => {
|
|
const wrapper = render(scheduleTypes.map(type => event(type)))
|
|
expect(wrapper.findAll('[aria-label="安排类型"] input')).toHaveLength(4)
|
|
expect(wrapper.get('[aria-label="安排类型"]').text()).toBe('到港排车还柜计划到仓')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(4)
|
|
expect(wrapper.get('[aria-label="当天安排"] header').text()).toContain('4 项安排')
|
|
expect(wrapper.findAll('.calendar-day')).toHaveLength(35)
|
|
expect(wrapper.find('[data-date="2026-11-01"]').exists()).toBe(true)
|
|
expect(wrapper.find('[data-date="2026-11-02"]').exists()).toBe(false)
|
|
expect(wrapper.text()).not.toMatch(/新增预约|全局待办|需复核|中转|直送/)
|
|
})
|
|
it('filters locally without requesting another range', async () => {
|
|
const wrapper = render([event('arrival'), event('dispatch')])
|
|
await wrapper.get('[aria-label="安排类型"] input').setValue(false)
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(1)
|
|
await wrapper.get('[aria-label="搜索当前日历"]').setValue('missing')
|
|
expect(wrapper.text()).toContain('当天暂无安排')
|
|
expect(wrapper.emitted('range')).toBeUndefined()
|
|
})
|
|
it('offers creation in the header only when authorized', async () => {
|
|
const wrapper = render()
|
|
expect(wrapper.findAll('button').some(button => button.text() === '新增预约')).toBe(false)
|
|
await wrapper.setProps({ canCreate: true })
|
|
await wrapper.findAll('.calendar-controls button').find(button => button.text() === '新增预约')!.trigger('click')
|
|
expect(wrapper.emitted('create')).toEqual([[]])
|
|
expect(wrapper.emitted('range')).toBeUndefined()
|
|
wrapper.unmount()
|
|
})
|
|
it('changes the selected date locally and requests one range per mode or period change', async () => {
|
|
const wrapper = render()
|
|
await wrapper.get('[data-date="2026-10-20"]').trigger('click')
|
|
expect(wrapper.emitted('range')).toBeUndefined()
|
|
expect(wrapper.get('[aria-label="当天安排"]').text()).toContain('2026-10-20')
|
|
await wrapper.get('[aria-label="预约视图"]').findAll('button')[1]!.trigger('click')
|
|
expect(wrapper.emitted('range')).toEqual([[{ from: '2026-10-19', to: '2026-10-26' }]])
|
|
await wrapper.get('[aria-label="下一周"]').trigger('click')
|
|
expect(wrapper.emitted('range')?.at(-1)).toEqual([{ from: '2026-10-26', to: '2026-11-02' }])
|
|
})
|
|
it('limits previews to three while retaining every event in the daily details', () => {
|
|
const wrapper = render(Array.from({ length: 23 }, (_, index) => event('arrival', index)))
|
|
expect(wrapper.get('[data-date="2026-10-19"]').findAll('.event-chip')).toHaveLength(3)
|
|
expect(wrapper.get('.event-more').text()).toBe('还有 20 条')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(23)
|
|
})
|
|
it('keeps undated records outside date cells and exposes them explicitly', async () => {
|
|
const wrapper = render([{ ...event('warehouse'), date: null }])
|
|
expect(wrapper.findAll('.event-chip')).toHaveLength(0)
|
|
await wrapper.get('.undated-button').trigger('click')
|
|
expect(wrapper.findAll('.detail-event')).toHaveLength(1)
|
|
expect(wrapper.get('[aria-label="当天安排"]').text()).toContain('日期未定')
|
|
})
|
|
it('retains business links and exposes editing only with authoritative eligibility', async () => {
|
|
const row = { ...event('warehouse'), sourcePath: '/container-loading/overseas/1', canEdit: true }
|
|
const wrapper = render([row])
|
|
await wrapper.get('.detail-event footer').findAll('button')[0]!.trigger('click')
|
|
await wrapper.get('.detail-event footer').findAll('button')[1]!.trigger('click')
|
|
expect(wrapper.emitted('open')).toEqual([[row]])
|
|
expect(wrapper.emitted('edit')).toEqual([[row]])
|
|
await wrapper.setProps({ events: [{ ...row, canEdit: false, sourcePath: undefined }] })
|
|
expect(wrapper.find('.detail-event footer').exists()).toBe(false)
|
|
})
|
|
it('distinguishes loading and partial failure from a complete empty day', async () => {
|
|
const wrapper = render()
|
|
await wrapper.setProps({ loading: true })
|
|
expect(wrapper.text()).toContain('正在读取安排')
|
|
expect(wrapper.text()).not.toContain('当天暂无安排')
|
|
await wrapper.setProps({ loading: false, incomplete: true })
|
|
expect(wrapper.get('[aria-label="当天安排"] header').text()).toContain('当前已加载 0 项安排')
|
|
expect(wrapper.text()).not.toContain('部分数据不可用')
|
|
expect(wrapper.text()).not.toContain('当天暂无安排')
|
|
})
|
|
})
|