File size: 3,686 Bytes
8059bf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useForm } from '@/composables/useForm'
import { useAppStore } from '@/stores/app'

// Mock API 依赖(app store 内部引用了这些)
vi.mock('@/api/admin/system', () => ({
  checkUpdates: vi.fn(),
}))
vi.mock('@/api/auth', () => ({
  getPublicSettings: vi.fn(),
}))

describe('useForm', () => {
  let appStore: ReturnType<typeof useAppStore>

  beforeEach(() => {
    setActivePinia(createPinia())
    appStore = useAppStore()
    vi.clearAllMocks()
  })

  it('submit 期间 loading 为 true,完成后为 false', async () => {
    let resolveSubmit: () => void
    const submitFn = vi.fn(
      () => new Promise<void>((resolve) => { resolveSubmit = resolve })
    )

    const { loading, submit } = useForm({
      form: { name: 'test' },
      submitFn,
    })

    expect(loading.value).toBe(false)

    const submitPromise = submit()
    // 提交中
    expect(loading.value).toBe(true)

    resolveSubmit!()
    await submitPromise

    expect(loading.value).toBe(false)
  })

  it('submit 成功时显示成功消息', async () => {
    const submitFn = vi.fn().mockResolvedValue(undefined)
    const showSuccessSpy = vi.spyOn(appStore, 'showSuccess')

    const { submit } = useForm({
      form: { name: 'test' },
      submitFn,
      successMsg: '保存成功',
    })

    await submit()

    expect(showSuccessSpy).toHaveBeenCalledWith('保存成功')
  })

  it('submit 成功但无 successMsg 时不调用 showSuccess', async () => {
    const submitFn = vi.fn().mockResolvedValue(undefined)
    const showSuccessSpy = vi.spyOn(appStore, 'showSuccess')

    const { submit } = useForm({
      form: { name: 'test' },
      submitFn,
    })

    await submit()

    expect(showSuccessSpy).not.toHaveBeenCalled()
  })

  it('submit 失败时显示错误消息并抛出错误', async () => {
    const error = Object.assign(new Error('提交失败'), {
      response: { data: { message: '服务器错误' } },
    })
    const submitFn = vi.fn().mockRejectedValue(error)
    const showErrorSpy = vi.spyOn(appStore, 'showError')

    const { submit, loading } = useForm({
      form: { name: 'test' },
      submitFn,
    })

    await expect(submit()).rejects.toThrow('提交失败')

    expect(showErrorSpy).toHaveBeenCalled()
    expect(loading.value).toBe(false)
  })

  it('submit 失败时使用自定义 errorMsg', async () => {
    const submitFn = vi.fn().mockRejectedValue(new Error('network'))
    const showErrorSpy = vi.spyOn(appStore, 'showError')

    const { submit } = useForm({
      form: { name: 'test' },
      submitFn,
      errorMsg: '自定义错误提示',
    })

    await expect(submit()).rejects.toThrow()

    expect(showErrorSpy).toHaveBeenCalledWith('自定义错误提示')
  })

  it('loading 中不会重复提交', async () => {
    let resolveSubmit: () => void
    const submitFn = vi.fn(
      () => new Promise<void>((resolve) => { resolveSubmit = resolve })
    )

    const { submit } = useForm({
      form: { name: 'test' },
      submitFn,
    })

    // 第一次提交
    const p1 = submit()
    // 第二次提交(应被忽略,因为 loading=true)
    submit()

    expect(submitFn).toHaveBeenCalledTimes(1)

    resolveSubmit!()
    await p1
  })

  it('传递 form 数据到 submitFn', async () => {
    const formData = { name: 'test', email: 'test@example.com' }
    const submitFn = vi.fn().mockResolvedValue(undefined)

    const { submit } = useForm({
      form: formData,
      submitFn,
    })

    await submit()

    expect(submitFn).toHaveBeenCalledWith(formData)
  })
})