Files
admin-web/src/modules/appointment-calendar/AppointmentCalendarPanel.test.ts
T

121 lines
7.0 KiB
TypeScript
Raw Normal View History

import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import AppointmentCalendarPanel from './AppointmentCalendarPanel.vue'
describe('appointment calendar interactions', () => {
it('keeps creation beside the title and record actions beside data notices', () => {
const wrapper = mount(AppointmentCalendarPanel, {
props: { canCreate: true, events: [], notices: ['数据范围不完整'] },
slots: { 'toolbar-actions': '<button>未定时安排</button><button>取消记录</button>' },
global: { stubs: { ElButton: { template: '<button><slot /></button>' } }, directives: { loading: {} } },
})
expect(wrapper.get('.calendar-label').text()).toContain('新增预约')
const actions = wrapper.get('[aria-label="预约记录与数据提示"]')
expect(actions.text()).toContain('未定时安排')
expect(actions.text()).toContain('取消记录')
expect(actions.get('summary').text()).toContain('数据提示')
wrapper.unmount()
})
const render = () => mount(AppointmentCalendarPanel, {
props: { initialDate: '2026-09-24', canCreate: true, events: [
{ key: 'a', type: 'delivery', date: '2026-09-24', time: '09:00-11:00', title: 'ONT8 预约送货', fields: [], canEdit: true, canDelete: true },
] },
global: { stubs: { ElButton: { template: '<button><slot /></button>' } }, directives: { loading: {} } },
})
it('opens a readable appointment without exposing write actions', async () => {
const wrapper = render()
const event = {key:'a',type:'delivery' as const,date:'2026-09-24',time:'09:00',title:'预约',fields:[],canView:true,canEdit:false,canDelete:false}
await wrapper.setProps({events:[event]})
const actions=wrapper.get('.detail-event footer')
expect(actions.text()).toBe('查看预约')
await actions.get('button').trigger('click')
expect(wrapper.emitted('view')?.[0]).toEqual([event])
expect(wrapper.emitted('edit')).toBeUndefined()
wrapper.unmount()
})
it('hides optional references by default and counts only appointments after enabling them', async () => {
const wrapper = render()
await wrapper.setProps({ events: [
{ key: 'a', type: 'delivery', date: '2026-09-24', time: '09:00', title: '收货预约', fields: [] },
{ key: 'b', type: 'arrival', date: '2026-09-24', time: '08:00', title: '到港参考', fields: [] },
{ key: 'c', type: 'trip', date: '2026-09-24', time: '07:00', title: '运输计划', fields: [] },
] })
expect(wrapper.findAll('.detail-event')).toHaveLength(1)
const switches = wrapper.get('[aria-label="参考图层"]').findAll('input')
await switches[0]!.setValue(true); await switches[1]!.setValue(true)
expect(wrapper.findAll('.detail-event')).toHaveLength(3)
expect(wrapper.get('[aria-label="当天安排"] header').text()).toContain('1 项预约')
expect(wrapper.get('[aria-label="当天安排"] header').text()).toContain('2 条参考计划')
expect(wrapper.emitted('range')).toBeUndefined()
})
it('selects an already loaded date without requesting data again', async () => {
const wrapper = render()
await wrapper.get('[aria-label="2026-09-25,0 项安排"]').trigger('click')
expect(wrapper.emitted('range')).toBeUndefined()
expect(wrapper.get('[aria-label="当天安排"]').text()).toContain('当天暂无预约')
})
it('shows an overnight slot on both days without fetching again and excludes midnight ends', async () => {
const wrapper = render()
await wrapper.setProps({ events: [
{ key: 'overnight', type: 'delivery', date: '2026-09-23', endAt: '2026-09-24T01:00:00', time: '23:00–01:00', title: '跨日送仓', fields: [] },
{ key: 'midnight', type: 'delivery', date: '2026-09-23', endAt: '2026-09-24T00:00:00', time: '23:00–00:00', title: '午夜结束', fields: [] },
] })
expect(wrapper.find('[aria-label="2026-09-23,2 项安排"]').exists()).toBe(true)
expect(wrapper.find('[aria-label="2026-09-24,1 项安排"]').exists()).toBe(true)
expect(wrapper.get('[aria-label="当天安排"]').text()).toContain('跨日送仓')
expect(wrapper.get('[aria-label="当天安排"]').text()).not.toContain('午夜结束')
expect(wrapper.emitted('range')).toBeUndefined()
})
it('requests exactly one range for a view switch and navigates seven days', async () => {
const wrapper = render()
await wrapper.get('[aria-label="预约视图"]').findAll('button')[1]!.trigger('click')
expect(wrapper.emitted('range')).toEqual([[{ from: '2026-09-21', to: '2026-09-28' }]])
await wrapper.get('[aria-label="下一周"]').trigger('click')
expect(wrapper.emitted('range')?.at(-1)).toEqual([{ from: '2026-09-28', to: '2026-10-05' }])
})
it('keeps busy days to three previews in both views and exposes the full count', async () => {
const wrapper = render()
await wrapper.setProps({ events: Array.from({ length: 233 }, (_, index) => ({
key: `arrival-${index}`, type: 'arrival' as const, date: '2026-09-24', time: '09:00',
title: `船舶 ${index}`, fields: [],
})) })
await wrapper.get('[aria-label="参考图层"] input').setValue(true)
for (const view of ['month', 'week']) {
if (view === 'week') await wrapper.get('[aria-label="预约视图"]').findAll('button')[1]!.trigger('click')
const day = wrapper.get('[aria-label="2026-09-24,233 项安排"]')
expect(day.findAll('.event-chip')).toHaveLength(3)
expect(day.attributes('title')).toContain('共 233 条安排')
expect(day.get('.event-more').text()).toBe('还有 230 条')
expect(wrapper.findAll('.detail-event')).toHaveLength(233)
}
})
it('uses the selected day for creation and emits edit/delete only from detail actions', async () => {
const wrapper = render()
await wrapper.findAll('button').find(button => button.text() === '+ 新增预约')!.trigger('click')
expect(wrapper.emitted('create')).toEqual([['2026-09-24']])
await wrapper.findAll('button').find(button => button.text() === '编辑')!.trigger('click')
await wrapper.findAll('button').find(button => button.text() === '取消预约')!.trigger('click')
expect(wrapper.emitted('edit')?.[0]?.[0]).toMatchObject({ key: 'a' })
expect(wrapper.emitted('delete')?.[0]?.[0]).toMatchObject({ key: 'a' })
})
it('renders many inbound codes outside the two-column fields with only three previews', async () => {
const wrapper = render()
await wrapper.setProps({ events: [{
key: 'many', type: 'delivery', date: '2026-09-24', time: '09:00', title: 'ONT8 预约送仓',
fields: [{ label: '货物信息', value: '20 票 / 12 个客户' }],
inboundCodes: Array.from({ length: 20 }, (_, index) => `WT-INBOUND-${index}`), canEdit: true,
}] })
const card = wrapper.get('.detail-event')
expect(card.get('dl').text()).not.toContain('WT-INBOUND')
expect(card.findAll('.inbound-codes li')).toHaveLength(3)
expect(card.get('.inbound-codes').text()).toContain('20 个')
expect(card.get('footer').text()).toContain('编辑')
})
})