mobile-main-record-actions.test.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import assert from 'node:assert/strict'
  2. import { readFileSync } from 'node:fs'
  3. import test from 'node:test'
  4. import { parse } from '@vue/compiler-sfc'
  5. import { computed, ref } from 'vue'
  6. import ts from 'typescript'
  7. function component(path: string) {
  8. return parse(readFileSync(new URL(`../src/views/m/${path}.vue`, import.meta.url), 'utf8'))
  9. .descriptor
  10. }
  11. function evaluate(path: string, name: string, bindings: Record<string, any>) {
  12. const ast = ts.createSourceFile(
  13. 'page.ts',
  14. component(path).scriptSetup!.content,
  15. ts.ScriptTarget.Latest,
  16. true
  17. )
  18. const statement = ast.statements.find((node) =>
  19. ts.isVariableStatement(node)
  20. ? node.declarationList.declarations.some((d) => d.name.getText(ast) === name)
  21. : ts.isFunctionDeclaration(node) && node.name?.text === name
  22. )!
  23. assert.ok(statement)
  24. return new Function(
  25. ...Object.keys(bindings),
  26. ts.transpile(statement.getText(ast)) + `; return ${name}`
  27. )(...Object.values(bindings))
  28. }
  29. test('main record list/detail templates have no delete buttons or delete event bindings', () => {
  30. for (const path of [
  31. 'crm/customer/index',
  32. 'crm/customer/detail',
  33. 'crm/product/index',
  34. 'crm/product/detail',
  35. 'crm/quote/MpQuoteList',
  36. 'crm/quote/detail',
  37. 'crm/quote/index',
  38. 'crm/quote/approval',
  39. 'crm/contract/MpContractList',
  40. 'crm/contract/detail',
  41. 'crm/contract/index',
  42. 'crm/contract/approval',
  43. 'pl/plan/index',
  44. 'pl/plan/detail',
  45. 'pl/plan-cert/index',
  46. 'pl/plan-cert/PlanCard',
  47. 'pl/plan-cert/detail',
  48. 'pl/plan-cert/CertDetail'
  49. ]) {
  50. const template = component(path).template!.content
  51. assert.doesNotMatch(template, /删除|@click="(?:onDelete|deleteCard|remove)\b/, path)
  52. }
  53. })
  54. test('quote and contract management/approval actions never offer main record deletion across statuses', () => {
  55. for (const domain of ['quote', 'contract']) {
  56. for (const approve of [false, true]) {
  57. for (const status of [
  58. 'draft',
  59. 'submitted',
  60. 'approving',
  61. 'accepted',
  62. 'rejected',
  63. 'approved',
  64. 'executing',
  65. 'completed',
  66. 'invalid'
  67. ]) {
  68. const actions = evaluate(`crm/${domain}/detail`, 'fabActions', {
  69. computed,
  70. quote: ref({ status }),
  71. contract: ref({ status }),
  72. props: { embedded: false },
  73. isApproveEntry: ref(approve),
  74. pendingApproval: ref({ id: 'approval' }),
  75. canApproveNow: ref(true),
  76. canInvalid: ref(true),
  77. canDelete: ref(true),
  78. CREATOR_DELETE_TIP: '',
  79. isQuoteEditableStatus: () => true,
  80. isContractEditableStatus: () => true
  81. }).value
  82. assert.ok(actions.length > 0)
  83. assert.ok(
  84. actions.every((action: any) => action.key !== 'delete' && action.label !== '删除')
  85. )
  86. const cardActions = evaluate(
  87. `crm/${domain}/Mp${domain === 'quote' ? 'Quote' : 'Contract'}List`,
  88. 'cardActions',
  89. {
  90. props: { mode: approve ? 'approve' : 'manage' },
  91. view: ref('submitted')
  92. }
  93. )({ status, canApprove: true })
  94. assert.ok(
  95. cardActions.every((action: any) => action.key !== 'delete' && action.label !== '删除')
  96. )
  97. }
  98. }
  99. }
  100. })