Files
client-web/src/modules/first-leg/components/InboundBillingPanel.test.mjs
T

131 lines
6.4 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { compile, computed, createSSRApp, h, inject, provide } from 'vue'
import { renderToString } from 'vue/server-renderer'
import ts from 'typescript'
const source = readFileSync(new URL('./InboundBillingPanel.vue', import.meta.url), 'utf8')
const confirmationBody = source.slice(source.indexOf('<dl class="fee-dialog-summary"'), source.indexOf('<div class="fee-confirm-notice"'))
const formatterSource = readFileSync(new URL('../utils/inboundBilling.ts', import.meta.url), 'utf8')
const formatters = await import(`data:text/javascript;base64,${Buffer.from(ts.transpileModule(formatterSource, {
compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
}).outputText).toString('base64')}`)
async function renderConfirmation(data) {
const app = createSSRApp({
render: compile(`<div>${confirmationBody}</div>`),
setup: () => ({ props: { data }, ...formatters }),
})
app.component('ElTable', {
props: ['data'],
setup(tableProps, { slots }) {
provide('billingRows', computed(() => tableProps.data))
return () => h('table', slots.default?.())
},
})
app.component('ElTableColumn', {
props: ['label', 'prop'],
setup(columnProps, { slots }) {
const rows = inject('billingRows')
return () => h('tr', [h('th', columnProps.label), ...rows.value.map(row =>
h('td', slots.default?.({ row }) ?? row[columnProps.prop] ?? ''))])
},
})
return renderToString(app)
}
test('renders loading, pending, isolated error, and published states', () => {
assert.match(source, /v-if="props\.loading"/)
assert.match(source, /v-else-if="props\.error"/)
assert.match(source, /role="alert"/)
assert.match(source, /v-else-if="props\.unavailable \|\| !props\.data"/)
assert.match(source, /role="status"/)
assert.match(source, /费用已发布 · 待确认/)
assert.match(source, /\$emit\('retry'\)/)
})
test('supports a standalone dialog and keeps the unpushed billing-weight state visible', () => {
assert.match(source, /dialogOnly\?: boolean/)
assert.match(source, /v-if="!props\.dialogOnly"/)
assert.match(source, /watch\(\(\) => props\.autoOpen/)
assert.match(source, /if \(autoOpen\) feeDialogOpen\.value = true/)
assert.match(source, /v-else-if="props\.unavailable \|\| !props\.data"/)
assert.match(source, /平台推送费用后,可在这里核对并确认/)
assert.match(source, /@closed="emit\('closed'\)"/)
})
test('shows the published merchant inbound, measurement details and customer confirmation entry', () => {
for (const label of ['入仓码', '平台收货仓', '实际毛重', '实测体积', '托盘与库位', '换算与精度', '最终计费量', '单价', '基础费用', '附加费用', '费用合计', '平台备注']) {
assert.ok(source.includes(label), `missing label: ${label}`)
}
assert.match(source, /formatInboundMoney\(props\.data\.latestFee\.totalAmount/)
assert.match(source, /formatConversionPrecision\(/)
assert.match(source, /title="确认计费重"/)
assert.match(source, /确认后当前费用版本将锁定/)
assert.match(source, /customerConfirmationStatus === 'CONFIRMED'/)
assert.match(source, /confirmMerchantInboundFee/)
assert.match(source, /measurementItems/)
assert.doesNotMatch(source, /调整费用|发送费用|下载照片/)
assert.doesNotMatch(source, /internalNotes|statusHistory|files/)
assert.doesNotMatch(source, /latestFee\.(feeVersion|ruleCode|rateCode|chargeRuleVersion|carrierRateVersion|sentAt)/)
assert.match(source, /箱规规则/)
assert.match(source, /getCalculationRuleLabel\(row\.calculationRuleVersion\)/)
})
test('keeps the billing summary, confirmation table, notice and footer', () => {
assert.match(source, /width="min\(1180px, calc\(100vw - 40px\)\)"/)
assert.match(source, /class="fee-dialog-summary" aria-label="计费汇总"/)
for (const label of ['计算重', '单价', '总金额', '计费毛重', '计费体积', '计费方式', '最终计费量']) assert.ok(confirmationBody.includes(label), `missing billing label: ${label}`)
assert.match(source, /class="fee-confirm-notice"/)
assert.match(source, /请核对计费毛重、计费体积、计费量、单价和总金额。确认后当前费用版本将锁定。/)
assert.match(source, /确认计费量和费用/)
assert.match(source, /\.fee-dialog-summary \{ display:grid; grid-template-columns:repeat\(3,minmax\(0,1fr\)\)/)
assert.match(source, /\.fee-dialog-table :deep\(\.el-table__body td\.el-table__cell\)/)
})
test('confirms the selected billing weight and volume even when outer carton totals differ', async () => {
const html = await renderConfirmation({
latestFee: {
actualWeightKg: 570.5, actualVolumeCbm: 3.6894,
chargeableQuantity: 616.1298, chargeMode: 'KG', chargeUnit: 'KG',
unitPrice: 13.6, totalAmount: 8379.37, currency: 'CNY',
},
billingBasis: { chargeableWeightKg: 650.298 },
measurementItems: [{ lengthCm: 63, widthCm: 53, heightCm: 53, packageCount: 22,
unitVolumeCbm: 0.177, totalVolumeCbm: 3.894, volumeWeightKg: 650.298 }],
})
assert.match(html, /570\.5 kg/)
assert.match(html, /3\.6894 m³/)
assert.match(html, /616\.13 kg/)
assert.match(html, /8,379\.37/)
assert.doesNotMatch(html, /3\.894|650\.298|0\.177/)
assert.doesNotMatch(confirmationBody, /measurementItems|billingBasis|箱规长|每箱体积/)
})
test('uses the published volume charge quantity and retains six decimal places', async () => {
const html = await renderConfirmation({
latestFee: {
actualWeightKg: 600, actualVolumeCbm: 1.234567,
chargeableQuantity: 1.652893, chargeMode: 'CBM', chargeUnit: 'CBM',
unitPrice: 100, totalAmount: 165.29, currency: 'CNY',
},
billingBasis: { chargeableWeightKg: 600 }, measurementItems: [],
})
assert.match(html, /计费量/)
assert.match(html, /按体积计费/)
assert.match(html, /600 kg/)
assert.match(html, /1\.234567 m³/)
assert.match(html, /1\.652893 m³/)
assert.doesNotMatch(html, /1\.652893 kg/)
})
test('stacks core information for 720 and 360 pixel layouts', () => {
assert.match(source, /@media \(max-width: 720px\)/)
assert.match(source, /\.inbound-overview, \.inbound-measurements, \.fee-calculation-grid \{ grid-template-columns: 1fr;/)
assert.match(source, /\.inbound-billing-state \.el-button \{[^}]*min-height: 44px;/)
assert.match(source, /@media \(max-width: 360px\)/)
})