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-15 10:03:03 +08:00
|
|
|
const routesSource = readFileSync(resolve(process.cwd(), 'src/modules/ocean-bookings/routes.ts'), 'utf8')
|
2026-08-08 15:15:04 +08:00
|
|
|
|
2026-08-18 16:09:43 +08:00
|
|
|
describe('OceanBookingDetailView draft and release contract', () => {
|
|
|
|
|
it('recognizes create and DRAFT detail routes as editable', () => {
|
2026-08-15 09:47:23 +08:00
|
|
|
expect(source).toContain("route.name === 'ocean-booking-create' || id.value === 'new'")
|
2026-08-18 16:09:43 +08:00
|
|
|
expect(source).toContain("const editable = computed(() => isNew.value || detail.value?.status === 'DRAFT')")
|
|
|
|
|
expect(source).toContain("platform:firstleg:ocean-booking:edit")
|
2026-08-15 10:03:03 +08:00
|
|
|
expect(routesSource.indexOf("path: 'ocean-bookings/new'")).toBeLessThan(routesSource.indexOf("path: 'ocean-bookings/:bookingId'"))
|
2026-08-15 09:47:23 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-18 16:09:43 +08:00
|
|
|
it('uses only DRAFT and RELEASED actions and removes voyage-change/cancel paths', () => {
|
|
|
|
|
expect(source).toContain("detail.value?.status === 'DRAFT'")
|
|
|
|
|
expect(source).toContain('删除草稿')
|
|
|
|
|
expect(source).toContain('放舱')
|
|
|
|
|
expect(source).not.toContain('申请改船')
|
|
|
|
|
expect(source).not.toContain('取消订舱')
|
|
|
|
|
expect(source).not.toContain('change-request')
|
|
|
|
|
expect(source).not.toContain('取消订舱')
|
|
|
|
|
expect(source).not.toContain('cancelOceanBooking')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('saves incomplete DRAFT data and sends null for blank nullable fields', () => {
|
|
|
|
|
expect(source).toContain('保存草稿')
|
|
|
|
|
expect(source).toContain('channelCode: form.data.channelCode?.trim() || null')
|
|
|
|
|
expect(source).toContain('warehouseId: form.data.warehouseId?.trim() || null')
|
|
|
|
|
expect(source).toContain('parties: form.data.parties.filter((party) => party.partyName.trim())')
|
|
|
|
|
expect(source).toContain('containerRequirements: form.data.containerRequirements.map')
|
|
|
|
|
expect(source).not.toContain('登记后直接确认')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('renders editable container requirements with fixed specifications', () => {
|
|
|
|
|
expect(source).toContain('aria-label="柜型需求"')
|
|
|
|
|
expect(source).toContain('containerRequirements')
|
|
|
|
|
expect(source).toContain('<th scope="col">数量</th>')
|
|
|
|
|
expect(source).toContain('v-model="requirement.quantity"')
|
|
|
|
|
expect(source).toContain('selectContainerRequirementType(requirement')
|
|
|
|
|
expect(source).toContain('{{ requirement.capacityCbm }} m³')
|
|
|
|
|
expect(source).toContain('{{ requirement.maxWeightKg }} kg')
|
|
|
|
|
expect(source).not.toContain('v-model="container.containerNo"')
|
|
|
|
|
expect(source).not.toContain('新增真实柜')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('performs the complete release gate and presents the irreversible warning', () => {
|
|
|
|
|
expect(source).toContain('function releaseValidationError()')
|
|
|
|
|
expect(source).toContain('预计到港时间不能早于预计开船时间')
|
|
|
|
|
expect(source).toContain("[form.data.customerId, '客户']")
|
|
|
|
|
expect(source).toContain('放舱前请填写${missing[1]}')
|
|
|
|
|
expect(source).toContain('放舱后资料将同步到关联货柜,不能修改、不能删除;填写错误只能由管理员处理数据库,请确认资料无误。')
|
|
|
|
|
expect(source).toContain('releaseOceanBooking(id.value, { expectedVersion: detail.value.version })')
|
|
|
|
|
expect(source).not.toContain('releaseReason')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps warehouse-scoped channel and customer/supplier selectors accessible', () => {
|
|
|
|
|
expect(source).toContain("warehouseType: 'DOMESTIC', status: 'ACTIVE'")
|
|
|
|
|
expect(source).toContain('listWarehouseLogisticsChannels')
|
|
|
|
|
expect(source).toContain('placeholder="草稿可暂不选择"')
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(source).toContain('暂无启用的订舱代理')
|
|
|
|
|
expect(source).toContain(':label="supplier.name"')
|
2026-08-18 16:09:43 +08:00
|
|
|
expect(source).toContain('filterable clearable')
|
2026-08-08 15:15:04 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-18 16:09:43 +08:00
|
|
|
it('keeps dense layout, operation feedback and narrow-screen behavior', () => {
|
2026-08-18 09:09:34 +08:00
|
|
|
expect(source).toContain('<OperationErrorToast :details="actionError" />')
|
2026-08-18 16:09:43 +08:00
|
|
|
expect(source).toContain('LoadErrorState')
|
|
|
|
|
expect(source).toContain('@media(max-width:720px)')
|
|
|
|
|
expect(source).toContain('@media(max-width:420px)')
|
2026-08-15 09:47:23 +08:00
|
|
|
})
|
2026-08-08 15:15:04 +08:00
|
|
|
})
|