2026-08-08 15:15:04 +08:00
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { resolve } from 'node:path'
|
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
|
|
|
|
|
const source = readFileSync(resolve(process.cwd(), 'src/modules/ocean-bookings/pages/OceanBookingDetailView.vue'), 'utf8')
|
|
|
|
|
|
2026-08-11 18:06:35 +08:00
|
|
|
describe('OceanBookingDetailView interaction contract', () => {
|
|
|
|
|
it('selects business objects and automatically keeps their ids and names together', () => {
|
|
|
|
|
expect(source).toContain('placeholder="请选择客户"')
|
|
|
|
|
expect(source).toContain('placeholder="请选择船公司"')
|
|
|
|
|
expect(source).toContain('placeholder="请选择订舱代理"')
|
|
|
|
|
expect(source).toContain("selectPartner('shippingCompany'")
|
|
|
|
|
expect(source).toContain("selectPartner('bookingAgent'")
|
|
|
|
|
expect(source).not.toContain('label="客户 ID"')
|
|
|
|
|
expect(source).not.toContain('label="船公司 ID"')
|
|
|
|
|
expect(source).not.toContain('label="订舱代理 ID"')
|
2026-08-08 15:15:04 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-11 18:06:35 +08:00
|
|
|
it('hides unavailable customers instead of filling the selector with disabled options', () => {
|
|
|
|
|
expect(source).toContain("customer.status === 'ACTIVE' || String(customer.id) === String(form.data.customerId)")
|
|
|
|
|
expect(source).toContain('v-for="customer in selectableCustomers"')
|
|
|
|
|
expect(source).not.toContain(':disabled="customer.status !== \'ACTIVE\'"')
|
2026-08-10 09:12:06 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-12 09:25:55 +08:00
|
|
|
it('keeps historical partner names visible when an existing partner is no longer selectable', () => {
|
|
|
|
|
expect(source).toContain("form.data.shippingCompanyName || '当前船公司'")
|
|
|
|
|
expect(source).toContain("form.data.bookingAgentName || '当前订舱代理'")
|
|
|
|
|
expect(source).toContain("from '@/modules/master-data'")
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-11 18:43:56 +08:00
|
|
|
it('renders the translated status once in the detail heading', () => {
|
|
|
|
|
expect(source).toContain('<StatusBadge :status="statusLabel(detail.status)" />')
|
|
|
|
|
expect(source).not.toContain('<StatusBadge :status="detail.status" />')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-11 18:06:35 +08:00
|
|
|
it('provides guidance for every free-text booking field', () => {
|
|
|
|
|
for (const placeholder of [
|
|
|
|
|
'请输入提单号(可选)', '请输入船名', '请输入航次', '例如:CNNGB', '例如:宁波港',
|
|
|
|
|
'例如:USLAX', '例如:洛杉矶港', '请输入最终交货地点', '例如:CY-CY',
|
|
|
|
|
'例如:PREPAID', '请输入订舱备注(可选)',
|
|
|
|
|
]) expect(source).toContain(`placeholder="${placeholder}"`)
|
2026-08-08 15:15:04 +08:00
|
|
|
})
|
2026-08-12 16:40:09 +08:00
|
|
|
|
2026-08-12 18:43:54 +08:00
|
|
|
it('captures channel, warehouse, capacity snapshots and uses the release DTO', () => {
|
|
|
|
|
expect(source).toContain('v-model="form.data.channelCode"')
|
|
|
|
|
expect(source).toContain('v-model="form.data.channelName"')
|
|
|
|
|
expect(source).toContain('form.data.warehouseName = warehouses.find')
|
|
|
|
|
expect(source).toContain('v-model="container.capacityCbm"')
|
|
|
|
|
expect(source).toContain('v-model="container.maxWeightKg"')
|
|
|
|
|
expect(source).toContain('releaseOceanBooking(id.value, releaseRequest)')
|
2026-08-12 18:50:03 +08:00
|
|
|
expect(source).toContain('capacityCbm: null')
|
|
|
|
|
expect(source).toContain('maxWeightKg: null')
|
|
|
|
|
expect(source).toContain('function validateContainerRequirements()')
|
|
|
|
|
expect(source).toContain('容量、限重和数量必须大于 0')
|
|
|
|
|
expect(source).not.toContain('capacityCbm: 68')
|
|
|
|
|
expect(source).not.toContain('maxWeightKg: 28000')
|
2026-08-12 18:43:54 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-12 16:40:09 +08:00
|
|
|
it('keeps view, create and edit on the formal detail route with a return action', () => {
|
|
|
|
|
expect(source).toContain("router.push('/ocean-bookings')")
|
|
|
|
|
expect(source).toContain("router.replace(`/ocean-bookings/${created.id}`)")
|
|
|
|
|
expect(source).not.toContain('AppDrawer')
|
|
|
|
|
expect(source).not.toContain('打印')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('groups real detail fields into the frozen three-column prototype structure', () => {
|
|
|
|
|
expect(source).toContain('aria-label="基本信息"')
|
|
|
|
|
expect(source).toContain('aria-label="航线船期"')
|
|
|
|
|
expect(source).toContain('aria-label="条款与柜量"')
|
|
|
|
|
expect(source).toContain('grid-template-columns:minmax(0,1.4fr) repeat(2,minmax(0,1fr))')
|
|
|
|
|
expect(source).toContain('@media(max-width:720px){.booking-groups,.party-grid{grid-template-columns:1fr}')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps the three real booking parties in their own three-column section', () => {
|
|
|
|
|
expect(source).toContain("{ value: 'SHIPPER', label: '发货人' }")
|
|
|
|
|
expect(source).toContain("{ value: 'CONSIGNEE', label: '收货人' }")
|
|
|
|
|
expect(source).toContain("{ value: 'NOTIFY_PARTY', label: '通知人' }")
|
|
|
|
|
expect(source).toContain('.party-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))')
|
|
|
|
|
})
|
2026-08-08 15:15:04 +08:00
|
|
|
})
|