mobile-product-category-loading.test.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  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 ts from 'typescript'
  6. const source = parse(
  7. readFileSync(new URL('../src/views/m/crm/product/categories.vue', import.meta.url), 'utf8')
  8. ).descriptor.scriptSetup!.content
  9. const ast = ts.createSourceFile('category.ts', source, ts.ScriptTarget.Latest, true)
  10. function harness(fetchCategoryTree: (...args: any[]) => Promise<any>) {
  11. const names = ['loadTree', 'getCategoryTreeSelect', 'openAdd', 'resetForm']
  12. const code = ast.statements
  13. .filter((n) => ts.isFunctionDeclaration(n) && names.includes(n.name!.text))
  14. .map((n) => n.getText(ast))
  15. .join('\n')
  16. const state: Record<string, any> = {
  17. fetchCategoryTree,
  18. keyword: { value: ' 酸 ' },
  19. loading: { value: false },
  20. loadError: { value: '' },
  21. tree: { value: [] },
  22. expandedIds: { value: {} },
  23. collectExpandIds: () => ({}),
  24. categoryTreeOptions: { value: [] },
  25. actionOpen: { value: false },
  26. formOpen: { value: false },
  27. formTitle: { value: '' },
  28. formData: {},
  29. nameError: { value: '' },
  30. showFailToast: (message: string) => state.toasts.push(message),
  31. toasts: []
  32. }
  33. const handlers = new Function(
  34. ...Object.keys(state),
  35. 'let loadSeq = 0;\n' + ts.transpile(code) + ';return {loadTree,openAdd}'
  36. )(...Object.values(state))
  37. return { ...state, ...handlers }
  38. }
  39. test('category search trims query, reports errors, and retries successfully', async () => {
  40. let fail = true
  41. const calls: any[] = []
  42. const h = harness(async (params) => {
  43. calls.push(params)
  44. if (fail) throw new Error('网络中断')
  45. return { data: [{ id: 'a', categoryName: '酸' }] }
  46. })
  47. await h.loadTree()
  48. assert.equal(h.loadError.value, '网络中断')
  49. assert.equal(h.loading.value, false)
  50. fail = false
  51. await h.loadTree()
  52. assert.deepEqual(calls, [{ categoryName: '酸' }, { categoryName: '酸' }])
  53. assert.equal(h.loadError.value, '')
  54. assert.equal(h.tree.value[0].id, 'a')
  55. })
  56. test('an earlier category response cannot replace the latest search', async () => {
  57. const resolve: ((value: any) => void)[] = []
  58. const h = harness(() => new Promise((r) => resolve.push(r)))
  59. const first = h.loadTree()
  60. h.keyword.value = '碱'
  61. const second = h.loadTree()
  62. resolve[1]({ data: [{ id: 'new' }] })
  63. await second
  64. resolve[0]({ data: [{ id: 'old' }] })
  65. await first
  66. assert.equal(h.tree.value[0].id, 'new')
  67. })
  68. test('failed parent-option loading keeps the category form closed and explains retry', async () => {
  69. const h = harness(async () => {
  70. throw new Error('分类加载失败')
  71. })
  72. await h.openAdd()
  73. assert.equal(h.formOpen.value, false)
  74. assert.deepEqual(h.toasts, ['分类加载失败'])
  75. })