mobile-cert-card-fields.test.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import assert from 'node:assert/strict'
  2. import test from 'node:test'
  3. import { readFileSync } from 'node:fs'
  4. import { parse } from '@vue/compiler-sfc'
  5. import ts from 'typescript'
  6. import { computed, effectScope, ref, watch, nextTick } from 'vue'
  7. import { useMobilePlanListDetails } from '../src/views/m/pl/plan/mobileListDetails'
  8. import { formatBrandConstraintLabel } from '../src/utils/crm-product-brand'
  9. import { getPlanProductActualQuantity } from '../src/utils/pl-display'
  10. function card(plan: any) {
  11. const ast = ts.createSourceFile(
  12. 'card.ts',
  13. parse(
  14. readFileSync(new URL('../src/views/m/pl/plan-cert/PlanCard.vue', import.meta.url), 'utf8')
  15. ).descriptor.scriptSetup!.content,
  16. ts.ScriptTarget.Latest,
  17. true
  18. )
  19. const code = ast.statements
  20. .filter(
  21. (n) =>
  22. ts.isVariableStatement(n) &&
  23. n.declarationList.declarations.some((d) => d.name.getText(ast) === 'productNameLabel')
  24. )
  25. .map((n) => n.getText(ast))
  26. .join('\n')
  27. const shared = ts.createSourceFile(
  28. 'shared.ts',
  29. readFileSync(new URL('../src/utils/mp-pl-plan-card.ts', import.meta.url), 'utf8'),
  30. ts.ScriptTarget.Latest,
  31. true
  32. )
  33. const productCode = shared.statements
  34. .filter(
  35. (node) =>
  36. ts.isFunctionDeclaration(node) &&
  37. [
  38. 'formatStopProductLine',
  39. 'resolveLinkedPlanProduct',
  40. 'formatQty',
  41. 'resolveUnitLabel'
  42. ].includes(node.name!.text)
  43. )
  44. .map((node) => node.getText(shared))
  45. .join('\n')
  46. const env = {
  47. props: { plan },
  48. computed,
  49. formatBrandConstraintLabel,
  50. getPlanProductActualQuantity,
  51. resolveProductHazardFlags: () => ({ isToxic: false, isExplosive: false }),
  52. resolveMpPackageSpecLabel: (value: string) => value || '—',
  53. resolveMpProductUnitLabel: (value: string) => value || '—'
  54. }
  55. return new Function(
  56. ...Object.keys(env),
  57. `${ts.transpile(code + '\n' + productCode, { target: ts.ScriptTarget.ESNext })}; return {productNameLabel,productLine: (product, kind) => formatStopProductLine(props.plan, { kind }, product)}`
  58. )(...Object.values(env))
  59. }
  60. test('certificate expanded card joins list standard names, preserves segment empty brands and actual quantities', async () => {
  61. const scope = effectScope()
  62. const detail = {
  63. id: 'A',
  64. products: [
  65. {
  66. id: 'p',
  67. productName: '根别名',
  68. brandMode: 'required',
  69. brandName: '根品牌',
  70. aliasName: '根别名'
  71. }
  72. ]
  73. }
  74. const api = scope.run(() =>
  75. useMobilePlanListDetails({ scope: () => 'query/account', load: async () => detail })
  76. )!
  77. await api.ensure('A')
  78. const displayed = api.cardPlan({
  79. id: 'A',
  80. products: [{ id: 'p', productName: '标准产品' }]
  81. } as any)
  82. const h = card(displayed)
  83. const row = {
  84. planProductId: 'p',
  85. productName: '根别名',
  86. model: '99%',
  87. unit: '吨',
  88. planQty: 3,
  89. inboundQty: 8,
  90. outboundQty: 0,
  91. mobileProductMeta: { brandMode: 'any', brandName: '', aliasName: '送货别名' }
  92. }
  93. assert.equal(h.productNameLabel.value, '标准产品')
  94. const renderProduct = h.productLine
  95. const delivery = renderProduct(row, 'delivery')
  96. assert.equal(delivery.name, '标准产品')
  97. assert.equal(delivery.brand, '—')
  98. assert.equal(delivery.alias, '送货别名')
  99. assert.equal(delivery.actualQty, '0')
  100. assert.equal(renderProduct({ ...row, outboundQty: undefined }, 'delivery').actualQty, '—')
  101. assert.equal(renderProduct(row, 'load').actualQty, '8')
  102. scope.stop()
  103. })
  104. test('actual certificate list expands only the requested card and fetches mobile segment metadata', async () => {
  105. const source = parse(
  106. readFileSync(new URL('../src/views/m/pl/plan-cert/index.vue', import.meta.url), 'utf8')
  107. ).descriptor.scriptSetup!.content
  108. const ast = ts.createSourceFile('list.ts', source, ts.ScriptTarget.Latest, true)
  109. const code = ast.statements
  110. .filter(
  111. (n) =>
  112. (ts.isVariableStatement(n) &&
  113. n.declarationList.declarations.some((d) => d.name.getText(ast) === 'planDetails')) ||
  114. (ts.isFunctionDeclaration(n) && n.name?.text === 'toggleExpanded') ||
  115. (ts.isExpressionStatement(n) &&
  116. ts.isCallExpression(n.expression) &&
  117. n.expression.expression.getText(ast) === 'watch' &&
  118. n.expression.arguments[0]?.getText(ast) === 'expandedIds')
  119. )
  120. .map((n) => n.getText(ast))
  121. .join('\n')
  122. const calls: any[] = []
  123. const env = {
  124. useMobilePlanListDetails,
  125. watch,
  126. expandedIds: ref(new Set()),
  127. requestKey: () => 'scope',
  128. planApi: {
  129. get: (id: string, mobile: boolean) =>
  130. new Promise((resolve) => calls.push({ id, mobile, resolve }))
  131. }
  132. }
  133. const scope = effectScope()
  134. const api = scope.run(() =>
  135. new Function(
  136. ...Object.keys(env),
  137. `${ts.transpile(code, { target: ts.ScriptTarget.ESNext })};return {planDetails,toggleExpanded}`
  138. )(...Object.values(env))
  139. )
  140. assert.equal(calls.length, 0)
  141. api.toggleExpanded('A', true)
  142. await nextTick()
  143. assert.deepEqual(
  144. calls.map(({ id, mobile }) => [id, mobile]),
  145. [['A', true]]
  146. )
  147. assert.equal(api.planDetails.records.A.plan, null)
  148. calls[0].resolve({
  149. id: 'A',
  150. products: [{ id: 'p', productName: '根别名' }],
  151. deliveries: [
  152. {
  153. products: [
  154. {
  155. planProductId: 'p',
  156. mobileProductMeta: { aliasName: '段次别名', brandMode: 'any', brandName: '' }
  157. }
  158. ]
  159. }
  160. ]
  161. })
  162. await api.planDetails.ensure('A')
  163. const plan = api.planDetails.cardPlan({
  164. id: 'A',
  165. products: [{ id: 'p', productName: '标准产品' }]
  166. })
  167. const rendered = card(plan)
  168. assert.equal(rendered.productNameLabel.value, '标准产品')
  169. assert.equal(rendered.productLine(plan.deliveries[0].products[0], 'delivery').alias, '段次别名')
  170. api.toggleExpanded('A', false)
  171. api.toggleExpanded('A', true)
  172. await nextTick()
  173. assert.equal(calls.length, 1)
  174. scope.stop()
  175. })