178 lines
5.8 KiB
TypeScript
178 lines
5.8 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { readFileSync } from 'node:fs'
|
|
import { parse } from '@vue/compiler-sfc'
|
|
import ts from 'typescript'
|
|
import { computed, effectScope, ref, watch, nextTick } from 'vue'
|
|
import { useMobilePlanListDetails } from '../src/views/m/pl/plan/mobileListDetails'
|
|
import { formatBrandConstraintLabel } from '../src/utils/crm-product-brand'
|
|
import { getPlanProductActualQuantity } from '../src/utils/pl-display'
|
|
|
|
function card(plan: any) {
|
|
const ast = ts.createSourceFile(
|
|
'card.ts',
|
|
parse(
|
|
readFileSync(new URL('../src/views/m/pl/plan-cert/PlanCard.vue', import.meta.url), 'utf8')
|
|
).descriptor.scriptSetup!.content,
|
|
ts.ScriptTarget.Latest,
|
|
true
|
|
)
|
|
const code = ast.statements
|
|
.filter(
|
|
(n) =>
|
|
ts.isVariableStatement(n) &&
|
|
n.declarationList.declarations.some((d) => d.name.getText(ast) === 'productNameLabel')
|
|
)
|
|
.map((n) => n.getText(ast))
|
|
.join('\n')
|
|
const shared = ts.createSourceFile(
|
|
'shared.ts',
|
|
readFileSync(new URL('../src/utils/mp-pl-plan-card.ts', import.meta.url), 'utf8'),
|
|
ts.ScriptTarget.Latest,
|
|
true
|
|
)
|
|
const productCode = shared.statements
|
|
.filter(
|
|
(node) =>
|
|
ts.isFunctionDeclaration(node) &&
|
|
[
|
|
'formatStopProductLine',
|
|
'resolveLinkedPlanProduct',
|
|
'formatQty',
|
|
'resolveUnitLabel'
|
|
].includes(node.name!.text)
|
|
)
|
|
.map((node) => node.getText(shared))
|
|
.join('\n')
|
|
const env = {
|
|
props: { plan },
|
|
computed,
|
|
formatBrandConstraintLabel,
|
|
getPlanProductActualQuantity,
|
|
resolveProductHazardFlags: () => ({ isToxic: false, isExplosive: false }),
|
|
resolveMpPackageSpecLabel: (value: string) => value || '—',
|
|
resolveMpProductUnitLabel: (value: string) => value || '—'
|
|
}
|
|
return new Function(
|
|
...Object.keys(env),
|
|
`${ts.transpile(code + '\n' + productCode, { target: ts.ScriptTarget.ESNext })}; return {productNameLabel,productLine: (product, kind) => formatStopProductLine(props.plan, { kind }, product)}`
|
|
)(...Object.values(env))
|
|
}
|
|
test('certificate expanded card joins list standard names, preserves segment empty brands and actual quantities', async () => {
|
|
const scope = effectScope()
|
|
const detail = {
|
|
id: 'A',
|
|
products: [
|
|
{
|
|
id: 'p',
|
|
productName: '根别名',
|
|
brandMode: 'required',
|
|
brandName: '根品牌',
|
|
aliasName: '根别名'
|
|
}
|
|
]
|
|
}
|
|
const api = scope.run(() =>
|
|
useMobilePlanListDetails({ scope: () => 'query/account', load: async () => detail })
|
|
)!
|
|
await api.ensure('A')
|
|
const displayed = api.cardPlan({
|
|
id: 'A',
|
|
products: [{ id: 'p', productName: '标准产品' }]
|
|
} as any)
|
|
const h = card(displayed)
|
|
const row = {
|
|
planProductId: 'p',
|
|
productName: '根别名',
|
|
model: '99%',
|
|
unit: '吨',
|
|
planQty: 3,
|
|
inboundQty: 8,
|
|
outboundQty: 0,
|
|
mobileProductMeta: { brandMode: 'any', brandName: '', aliasName: '送货别名' }
|
|
}
|
|
assert.equal(h.productNameLabel.value, '标准产品')
|
|
const renderProduct = h.productLine
|
|
const delivery = renderProduct(row, 'delivery')
|
|
assert.equal(delivery.name, '标准产品')
|
|
assert.equal(delivery.brand, '—')
|
|
assert.equal(delivery.alias, '送货别名')
|
|
assert.equal(delivery.actualQty, '0')
|
|
assert.equal(renderProduct({ ...row, outboundQty: undefined }, 'delivery').actualQty, '—')
|
|
assert.equal(renderProduct(row, 'load').actualQty, '8')
|
|
scope.stop()
|
|
})
|
|
|
|
test('actual certificate list expands only the requested card and fetches mobile segment metadata', async () => {
|
|
const source = parse(
|
|
readFileSync(new URL('../src/views/m/pl/plan-cert/index.vue', import.meta.url), 'utf8')
|
|
).descriptor.scriptSetup!.content
|
|
const ast = ts.createSourceFile('list.ts', source, ts.ScriptTarget.Latest, true)
|
|
const code = ast.statements
|
|
.filter(
|
|
(n) =>
|
|
(ts.isVariableStatement(n) &&
|
|
n.declarationList.declarations.some((d) => d.name.getText(ast) === 'planDetails')) ||
|
|
(ts.isFunctionDeclaration(n) && n.name?.text === 'toggleExpanded') ||
|
|
(ts.isExpressionStatement(n) &&
|
|
ts.isCallExpression(n.expression) &&
|
|
n.expression.expression.getText(ast) === 'watch' &&
|
|
n.expression.arguments[0]?.getText(ast) === 'expandedIds')
|
|
)
|
|
.map((n) => n.getText(ast))
|
|
.join('\n')
|
|
const calls: any[] = []
|
|
const env = {
|
|
useMobilePlanListDetails,
|
|
watch,
|
|
expandedIds: ref(new Set()),
|
|
requestKey: () => 'scope',
|
|
planApi: {
|
|
get: (id: string, mobile: boolean) =>
|
|
new Promise((resolve) => calls.push({ id, mobile, resolve }))
|
|
}
|
|
}
|
|
const scope = effectScope()
|
|
const api = scope.run(() =>
|
|
new Function(
|
|
...Object.keys(env),
|
|
`${ts.transpile(code, { target: ts.ScriptTarget.ESNext })};return {planDetails,toggleExpanded}`
|
|
)(...Object.values(env))
|
|
)
|
|
assert.equal(calls.length, 0)
|
|
api.toggleExpanded('A', true)
|
|
await nextTick()
|
|
assert.deepEqual(
|
|
calls.map(({ id, mobile }) => [id, mobile]),
|
|
[['A', true]]
|
|
)
|
|
assert.equal(api.planDetails.records.A.plan, null)
|
|
calls[0].resolve({
|
|
id: 'A',
|
|
products: [{ id: 'p', productName: '根别名' }],
|
|
deliveries: [
|
|
{
|
|
products: [
|
|
{
|
|
planProductId: 'p',
|
|
mobileProductMeta: { aliasName: '段次别名', brandMode: 'any', brandName: '' }
|
|
}
|
|
]
|
|
}
|
|
]
|
|
})
|
|
await api.planDetails.ensure('A')
|
|
const plan = api.planDetails.cardPlan({
|
|
id: 'A',
|
|
products: [{ id: 'p', productName: '标准产品' }]
|
|
})
|
|
const rendered = card(plan)
|
|
assert.equal(rendered.productNameLabel.value, '标准产品')
|
|
assert.equal(rendered.productLine(plan.deliveries[0].products[0], 'delivery').alias, '段次别名')
|
|
api.toggleExpanded('A', false)
|
|
api.toggleExpanded('A', true)
|
|
await nextTick()
|
|
assert.equal(calls.length, 1)
|
|
scope.stop()
|
|
})
|