mobile-customer-related-navigation.test.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import assert from 'node:assert/strict'
  2. import test from 'node:test'
  3. import * as navigation from '../src/utils/mp-webview'
  4. const api = navigation as any
  5. test('客户进入合同后继续查看报价,返回合同仍保留最外层客户来源', () => {
  6. assert.equal(typeof api.mpContractRelatedLocation, 'function')
  7. const location = api.mpContractRelatedLocation('quote', { id: 'K-1', quoteId: 'Q1' }, { from: 'customer', customerId: 'C1' })
  8. assert.equal(location.path, '/m/crm/quote/Q1')
  9. assert.deepEqual(api.mpRelatedContractReturnLocation(location.query), {
  10. path: '/m/crm/contract/K-1', query: { from: 'customer', customerId: 'C1' }
  11. })
  12. })
  13. test('关联客户和商机复用现有客户详情,不需要商机列表命中', () => {
  14. assert.equal(typeof api.mpContractRelatedLocation, 'function')
  15. const row = { id: 'K1', customerId: 'C1', opportunityId: 'O999' }
  16. assert.equal(api.mpContractRelatedLocation('customer', row, {}).path, '/m/crm/customer/C1')
  17. const location = api.mpContractRelatedLocation('opportunity', row, {})
  18. assert.equal(location.path, '/m/crm/customer/C1')
  19. assert.equal(location.query.tab, 'opportunity')
  20. assert.equal(location.query.opportunityId, 'O999')
  21. })
  22. test('非法目标与返回ID不能形成任意站外或相对路径', () => {
  23. assert.equal(typeof api.mpContractRelatedLocation, 'function')
  24. for (const value of ['../x', '//evil.test', 'x?foo=1', ['K1'], '']) {
  25. assert.equal(api.mpContractRelatedLocation('quote', { id: 'K1', quoteId: value }, {}), null)
  26. assert.equal(api.mpRelatedContractReturnLocation({ from: 'contract', returnContractId: value }), null)
  27. }
  28. })
  29. test('商机跟进展示使用Web crm_follow_type,未知值保留原值', async () => {
  30. const { readFileSync } = await import('node:fs')
  31. const { compile } = await import('vue')
  32. const { resolveOpportunityDictLabel } = await import('../src/utils/crm-opportunity-labels')
  33. const source = readFileSync(new URL('../src/views/m/crm/customer/RelatedPanel.vue', import.meta.url), 'utf8')
  34. const expression = source.match(/{{\s*([^{}]*row\.followType[^{}]*)\s*}}/)?.[1]
  35. assert.ok(expression)
  36. const dict = { dictMap: { crm_follow_type: [{ value: 'phone', label: '客户电话' }], crm_opportunity_follow_type: [{ value: 'phone', label: '商机电话' }] }, getLabel: (type: string) => type === 'crm_follow_type' ? '客户电话' : '商机电话' }
  37. const render = compile(`<span>{{ ${expression} }}</span>`)
  38. assert.equal(render({ row: { followType: 'phone' }, dict, resolveOpportunityDictLabel }, []).children, '客户电话')
  39. assert.equal(render({ row: { followType: 'legacy' }, dict, resolveOpportunityDictLabel }, []).children, 'legacy')
  40. })
  41. test('关闭旧商机不能清除新目标query,关闭当前只移除目标并保留返回链', async () => {
  42. const { readFileSync } = await import('node:fs')
  43. const { parse } = await import('@vue/compiler-sfc')
  44. const ts = (await import('typescript')).default
  45. const source = parse(readFileSync(new URL('../src/views/m/crm/customer/detail.vue', import.meta.url), 'utf8')).descriptor.scriptSetup!.content
  46. const ast = ts.createSourceFile('detail.ts', source, ts.ScriptTarget.Latest, true)
  47. const method = ast.statements.find((node) => ts.isFunctionDeclaration(node) && node.name?.text === 'clearTargetOpportunity')!
  48. const calls: any[] = []
  49. const route = { path: '/m/crm/customer/C1', query: { opportunityId: 'O2', from: 'contract', returnContractId: 'K1', originCustomerId: 'C0', tab: 'opportunity' } }
  50. const clear = new Function('route', 'router', 'targetOpportunityId', 'isCurrentCustomerRoute', `${ts.transpile(method.getText(ast))};return clearTargetOpportunity`)(route, { replace: (v: any) => calls.push(v) }, { value: 'O2' }, () => true)
  51. clear('O1')
  52. assert.equal(calls.length, 0)
  53. clear('O2')
  54. assert.deepEqual(calls, [{ path: route.path, query: { from: 'contract', returnContractId: 'K1', originCustomerId: 'C0', tab: 'opportunity' } }])
  55. })
  56. test('历史数值ID仅接受安全整数,合同和关联对象一致规范化', () => {
  57. for (const id of [1, 123, Number.MAX_SAFE_INTEGER]) {
  58. assert.equal(api.mpCrmRecordId(id), String(id))
  59. const location = api.mpContractRelatedLocation('customer', { id, customerId: id }, {})
  60. assert.equal(location.path, `/m/crm/customer/${id}`)
  61. assert.equal(api.mpRelatedContractReturnLocation(location.query).path, `/m/crm/contract/${id}`)
  62. }
  63. for (const id of [NaN, Infinity, -Infinity, 1.5, Number.MAX_SAFE_INTEGER + 1]) {
  64. assert.equal(api.mpCrmRecordId(id), '')
  65. assert.equal(api.mpContractRelatedLocation('customer', { id, customerId: 'C1' }, {}), null)
  66. assert.equal(api.mpContractRelatedLocation('customer', { id: 'K1', customerId: id }, {}), null)
  67. }
  68. })