接入前端P0页面真实接口

This commit is contained in:
andy
2026-07-08 16:20:06 +08:00
parent 5c5b8e33b0
commit 66a613d6cd
45 changed files with 8719 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createI18n } from 'vue-i18n'
import { createMemoryHistory, createRouter } from 'vue-router'
import zhCN from '@/i18n/locales/zh-CN'
import { EndpointPendingError } from '@/services/reservationService'
import ReservationOrderListView from '@/views/reservation/ReservationOrderListView.vue'
import ReservationSourceMessageConversationView from '@/views/reservation/ReservationSourceMessageConversationView.vue'
import ReservationTaskListView from '@/views/reservation/ReservationTaskListView.vue'
vi.mock('@/services/reservationService', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/services/reservationService')>()
return {
...actual,
fetchReservationOrders: vi.fn(),
fetchReservationTaskList: vi.fn(),
fetchSourceMessageConversation: vi.fn(),
}
})
const service = await import('@/services/reservationService')
function createTaskListResult(displayOrderKey: string, sourceSubject: string) {
return {
items: [
{
task_id: displayOrderKey === 'GRP-NEW' ? '10002' : '10001',
order_id: '20001',
hotel_id: 'HOTEL-TEST',
display_order_key: displayOrderKey,
temporary_order_no: null,
task_type: 'NEW_BOOKING',
task_subtype: 'NEW_BOOKING',
task_status: 'PENDING_CONFIRM',
card_name: 'New Booking',
queue_sequence: 1,
queue_participation: true,
can_process: true,
readonly_reason_code: null,
source_message_id: '30001',
source_subject: sourceSubject,
source_sender_summary: null,
source_received_at: null,
external_conversation_id: null,
conversation_message_count: null,
created_at: '2026-07-08T03:00:00Z',
updated_at: '2026-07-08T03:10:00Z',
},
],
page: {
page_num: 1,
page_size: 20,
total: 1,
},
}
}
function createDeferred<T>() {
let resolve!: (value: T) => void
let reject!: (reason?: unknown) => void
const promise = new Promise<T>((promiseResolve, promiseReject) => {
resolve = promiseResolve
reject = promiseReject
})
return { promise, resolve, reject }
}
async function mountWithPlugins(component: object, initialPath = '/') {
const i18n = createI18n({
legacy: false,
locale: 'zh-CN',
messages: {
'zh-CN': zhCN,
},
})
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/', component: { template: '<div />' } },
{ path: '/reservation/orders/:orderId', component: { template: '<div />' } },
{ path: '/reservation/tasks/:taskId', component: { template: '<div />' } },
{ path: '/reservation/source-messages/:sourceMessageId/conversation', component: { template: '<div />' } },
],
})
await router.push(initialPath)
await router.isReady()
return mount(component, {
global: {
plugins: [i18n, router],
stubs: {
RouterLink: true,
},
},
})
}
describe('reservation P0 views', () => {
beforeEach(() => {
vi.mocked(service.fetchReservationOrders).mockReset()
vi.mocked(service.fetchReservationTaskList).mockReset()
vi.mocked(service.fetchSourceMessageConversation).mockReset()
})
it('shows pending state for the order list endpoint', async () => {
vi.mocked(service.fetchReservationOrders).mockRejectedValue(
new EndpointPendingError('GET /api/reservation/orders is pending backend implementation.'),
)
const wrapper = await mountWithPlugins(ReservationOrderListView)
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('接口待接入')
expect(wrapper.text()).not.toContain('#ORD-083')
})
it('renders task list items returned by the backend', async () => {
vi.mocked(service.fetchReservationTaskList).mockResolvedValue(createTaskListResult('GRP-001', 'Booking Request'))
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('GRP-001')
expect(wrapper.text()).toContain('Booking Request')
expect(wrapper.text()).toContain('接口待补')
})
it('keeps the latest task list response when filters change quickly', async () => {
const firstRequest = createDeferred<Awaited<ReturnType<typeof service.fetchReservationTaskList>>>()
const secondRequest = createDeferred<Awaited<ReturnType<typeof service.fetchReservationTaskList>>>()
vi.mocked(service.fetchReservationTaskList)
.mockReturnValueOnce(firstRequest.promise)
.mockReturnValueOnce(secondRequest.promise)
const wrapper = await mountWithPlugins(ReservationTaskListView)
await wrapper.find('input[type="search"]').setValue('fresh')
expect(service.fetchReservationTaskList).toHaveBeenCalledTimes(2)
secondRequest.resolve(createTaskListResult('GRP-NEW', 'New Booking Request'))
await flushPromises()
expect(wrapper.text()).toContain('GRP-NEW')
firstRequest.resolve(createTaskListResult('GRP-OLD', 'Old Booking Request'))
await flushPromises()
expect(wrapper.text()).toContain('GRP-NEW')
expect(wrapper.text()).not.toContain('GRP-OLD')
})
it('shows pending state for the source message conversation endpoint', async () => {
vi.mocked(service.fetchSourceMessageConversation).mockRejectedValue(
new EndpointPendingError('GET /api/source-messages/30001/conversation is pending backend implementation.'),
)
const wrapper = await mountWithPlugins(
ReservationSourceMessageConversationView,
'/reservation/source-messages/30001/conversation',
)
await vi.dynamicImportSettled()
expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
expect(wrapper.text()).toContain('接口待接入')
expect(wrapper.text()).not.toContain('完整邮件正文')
})
})