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() })