import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import { describe, expect, it } from 'vitest' const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/ContainerDetailView.vue'), 'utf8') const overviewSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/components/ContainerOverviewPanel.vue'), 'utf8') const routeSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/routes.ts'), 'utf8') const loadingRouteSource = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/routes.ts'), 'utf8') const navigationSource = readFileSync(resolve(process.cwd(), 'src/core/navigation/adminNavigation.ts'), 'utf8') const lastLegFeesSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/components/ContainerLastLegFeesPanel.vue'), 'utf8') describe('ContainerDetailView customs integration', () => { it('renders the requested detail sections in the confirmed business order', () => { const headings = [ 'container-information-heading">货柜信息', 'container-voyage-heading">船名航次信息', 'container-customs-heading">{{ isDomestic', 'container-receiving-heading">装柜作业与国内出仓', ] let previous = -1 for (const heading of headings) { const current = source.indexOf(heading) expect(current, heading).toBeGreaterThan(previous) previous = current } }) it('reuses the current schedule, customs, receipt and container-fee capabilities', () => { expect(source).toContain('(() => detail.value?.planStatus === 'COMPLETED' ? 'confirmed' : 'preload')") expect(source).toContain(' { expect(routeSource).toContain("name: 'domestic-container-detail'") expect(routeSource).toContain("name: 'overseas-container-detail'") expect(loadingRouteSource).toContain("name: 'loading-lists'") expect(navigationSource).toContain("title: '装柜清单'") expect(source).toContain(' { expect(source).toContain("v-show=\"activeTab === 'overview'\" class=\"detail-section surface-panel\" aria-labelledby=\"container-voyage-heading\"") expect(source).toContain("v-if=\"isOverseas\" v-show=\"activeTab === 'overview'\"") expect(source).not.toContain("v-if=\"activeTab === 'overview'\" class=\"detail-section surface-panel\"") }) it('exposes empty return directly in overseas detail only when permission and server action both allow it', () => { expect(source).toContain("access.can('platform:firstleg:terminal-drayage:empty-return:confirm')") expect(source).toContain("hasAction('RECORD_EMPTY_RETURN')") expect(source).toContain("terminalDrayage?.allowedActions.includes('RECORD_EMPTY_RETURN')") expect(source).toContain('v-if="canRecordEmptyReturn" type="primary"') expect(source).toContain('>确认还柜') expect(source).toContain('expectedVersion: current.version') expect(source).toContain('actualAt: emptyReturnAt.value') expect(source).toContain('remark: emptyReturnReason.value.trim() || undefined') expect(source).toContain('await load()') }) it('separates domestic first-leg fees and connects the overseas fee lifecycle lazily', () => { expect(source).toContain('label="国内段费用(头程)" name="first-leg-fees"') expect(source).toContain('label="目的港及海外段费用(尾程)" name="last-leg-fees"') expect(source).toContain('v-if="canViewFirstLegFees && firstLegFeesOpened"') expect(source).toContain('v-show="activeTab === \'first-leg-fees\'"') expect(source).toContain('title="国内段费用(头程)" :plan-id="overview.loadingPlanId" :container-booking-id="detail.id"') expect(source).not.toContain(':readonly="isOverseas"') expect(source).toContain('v-if="canViewLastLegFees && lastLegFeesOpened"') expect(source).toContain('title="目的港及海外段费用(尾程)"') expect(source).not.toContain(':readonly="isDomestic"') expect(source).toContain("if (isDomestic.value && activeTab.value === 'loading-list') await loadLoadingDetail(resource, aggregate)") expect(source).toContain("const needsLoadingDetail = tabName === 'first-leg-fees' || (isDomestic.value && tabName === 'loading-list')") expect(lastLegFeesSource.indexOf('label="操作"')).toBeLessThan(lastLegFeesSource.indexOf('label="客户"')) expect(lastLegFeesSource).toContain('show-overflow-tooltip') expect(lastLegFeesSource).toContain('尾程计费加载失败') expect(lastLegFeesSource).toContain('empty-text="当前货柜暂无尾程费用"') expect(lastLegFeesSource).toContain('v-loading="loading"') expect(lastLegFeesSource).toContain("canEdit.value && access.can('platform:supplier:list') ? listContainerFeeSupplierOptions(null) : Promise.resolve([])") expect(lastLegFeesSource).toContain("sourceType: 'OVERSEAS_CONTAINER_FEE', businessType: 'OVERSEAS_CONTAINER'") expect(lastLegFeesSource).toContain("form.settlementMode === 'DIRECT_PAID'") expect(lastLegFeesSource).toContain("access.can('platform:finance:fee-source:edit')") expect(lastLegFeesSource).toContain("access.can('platform:finance:fee-source:review')") expect(source).toContain("if (!overview.value?.loadingPlanId) return '当前货柜尚未关联装柜计划,暂无头程计费归属。'") expect(source).toContain(':description="firstLegFeesEmptyReason"') expect(source).toContain("const needsLoadingDetail = tabName === 'first-leg-fees' || (isDomestic.value && tabName === 'loading-list')") }) it('uses domestic and overseas route-specific primary status and summary labels', () => { expect(source).toContain("const primaryStatusText = computed(() => isDomestic.value ? statusText(detail.value?.planStatus) : overseasStageText.value)") expect(source).toContain("UNLOADED: '待还柜'") expect(source).toContain("EMPTY_RETURNED: '已还柜'") expect(source).toContain("{{ isDomestic ? '装柜清单号' : '当前海外阶段' }}") expect(source).toContain("{{ isDomestic ? '装柜仓库' : '目的仓 / 接柜仓' }}") expect(source).toContain('emptyReturnWarningText') }) it('mounts the new container customs panel only after loading completes', () => { expect(source).toContain("detail.planStatus === 'COMPLETED' && overview?.loadingPlanId && canListCustoms") expect(source).toContain(' { expect(overviewSource).toContain("sections?: Array<'exception' | 'documents' | 'pda'>") expect(overviewSource).toContain("visibleSections.has('exception')") expect(overviewSource).toContain("visibleSections.has('documents')") expect(overviewSource).toContain("visibleSections.has('pda')") }) it('removes the retired loading-list customs UI and calls', () => { for (const retired of ['getLoadingDeclaration', 'uploadLoadingDeclaration', 'completeLoadingDeclaration', 'downloadLoadingDeclaration', 'CUSTOMS_DECLARATION']) { expect(source).not.toContain(retired) expect(overviewSource).not.toContain(retired) } }) it('offers the released-booking link workflow only for an eligible unlinked simulation container', () => { expect(source).toContain("detail.value?.sourceType === 'SIMULATION'") expect(source).toContain("!detail.value.oceanBookingId") expect(source).toContain("['WAIT_LOADING', 'EDITING'].includes(detail.value.planStatus)") expect(source).toContain('关联已放舱订舱') expect(source).toContain('oceanBookingLinkOpen.value = true') expect(source).toContain('') expect(source).not.toContain('linkContainerId') }) })