import { mount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' import AppointmentCalendarPanel from './AppointmentCalendarPanel.vue' import { scheduleTypes, type ScheduleEvent } from './schedule' 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[] = []) => mount(AppointmentCalendarPanel, { props: { initialDate: '2026-10-19', events }, global: { stubs: { ElButton: { template: '' } } } }) describe('four-type appointment calendar', () => { 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.text()).toContain('部分数据不可用') expect(wrapper.text()).not.toContain('当天暂无安排') }) })