usePlanView.integration.test.js
8.13 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
* 计划书模块集成测试
*
* @description 测试计划书模块的核心流程,包括查看、字段依赖、字段转换等
* @module composables/__tests__/usePlanView.integration
* @author Claude Code
* @created 2026-02-14
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { ref, reactive } from 'vue'
import Taro from '@tarojs/taro'
import { viewProposal } from '../usePlanView'
import { useFieldValueTransform } from '../useFieldValueTransform'
import { useFieldDependencies } from '../useFieldDependencies'
import { PLAN_FIELD_DEFINITIONS, FIELD_GROUPS, getFieldsByGroup } from '@/config/plan-fields'
import { viewAPI } from '@/api/plan'
// Mock Taro API
vi.mock('@tarojs/taro', () => ({
default: {
showToast: vi.fn(),
showModal: vi.fn(),
showLoading: vi.fn(),
hideLoading: vi.fn(),
showActionSheet: vi.fn(),
navigateTo: vi.fn(),
redirectTo: vi.fn()
}
}))
// Mock viewAPI
vi.mock('@/api/plan', () => ({
viewAPI: vi.fn()
}))
describe('计划书模块集成测试', () => {
beforeEach(() => {
vi.clearAllMocks()
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('完整流程:查看计划书', () => {
it('应该成功预览单文件计划书', async () => {
viewAPI.mockResolvedValue({ code: 1 })
const proposal = {
id: 123,
order_status: '7', // COMPLETED
proposal_files: [{ file_name: '计划书.pdf', file_url: 'https://example.com/plan.pdf', id: 1 }]
}
await viewProposal(proposal)
// 验证:显示预览成功提示
expect(Taro.showToast).toHaveBeenCalledWith({
title: '已标记为查看',
icon: 'success'
})
// 验证:调用 viewAPI 标记查看
expect(viewAPI).toHaveBeenCalledWith({ i: 123 })
})
it('应该显示多文件选择弹框', async () => {
const proposal = {
id: 456,
order_status: '7',
proposal_files: [
{ file_name: '计划书A.pdf', file_url: 'https://example.com/planA.pdf', id: 1 },
{ file_name: '计划书B.pdf', file_url: 'https://example.com/planB.pdf', id: 2 }
]
}
await viewProposal(proposal)
// 验证:显示选择弹框(Taro.showActionSheet)
expect(Taro.showActionSheet).toHaveBeenCalled()
})
it('应该在计划书未生成时友好提示', async () => {
const proposal = {
id: 789,
order_status: '3', // PENDING
proposal_files: []
}
await viewProposal(proposal)
// 验证:显示友好提示
expect(Taro.showToast).toHaveBeenCalledWith({
title: '计划书尚未生成,请稍后',
icon: 'none'
})
})
})
describe('字段依赖关系测试', () => {
it('应该根据 withdrawal_enabled 控制字段可见性', () => {
const formData = reactive({
withdrawal_enabled: false
})
const { isFieldVisible } = useFieldDependencies(formData)
// 当 withdrawal_enabled 为 false 时,相关字段应该不可见
expect(isFieldVisible('withdrawal_mode')).toBe(false)
expect(isFieldVisible('withdrawal_start_age')).toBe(false)
expect(isFieldVisible('withdrawal_period')).toBe(false)
})
it('应该在启用 withdrawal_enabled 后显示相关字段', () => {
const formData = reactive({
withdrawal_enabled: true
})
const { isFieldVisible, isFieldEnabled } = useFieldDependencies(formData)
// 当 withdrawal_enabled 为 true 时,相关字段应该可见
expect(isFieldVisible('withdrawal_mode')).toBe(true)
expect(isFieldVisible('withdrawal_start_age')).toBe(true)
expect(isFieldEnabled('withdrawal_mode')).toBe(true)
})
})
describe('字段转换测试', () => {
it('应该正确转换分值为元值显示', () => {
const formData = ref({
coverage: 10000, // API 存的是分(整数)
annual_premium: 10000
})
const { toYuan } = useFieldValueTransform(formData)
// 分转元显示(÷100)
expect(toYuan('coverage', 10000)).toBe('100.00')
})
it('应该正确转换元值为分值提交', () => {
const formData = ref({
coverage: '100.00', // 表单显示的是元
annual_premium: '100.00'
})
const { toFen } = useFieldValueTransform(formData)
// 元转分提交(×100)
expect(toFen('coverage', '100.00')).toBe(10000)
})
it('应该批量转换表单数据为显示格式', () => {
const formData = ref({
coverage: 10000,
name: '张三',
gender: 'male'
})
const { displayData } = useFieldValueTransform(formData)
expect(displayData.value.coverage).toBe('100.00')
expect(displayData.value.name).toBe('张三')
expect(displayData.value.gender).toBe('male')
})
it('应该批量转换表单数据为提交格式', () => {
const formData = ref({
coverage: '100.00',
name: '张三',
gender: 'male'
})
const { submitData } = useFieldValueTransform(formData)
expect(submitData.value.coverage).toBe(10000)
expect(submitData.value.name).toBe('张三')
expect(submitData.value.gender).toBe('male')
})
})
describe('错误处理测试', () => {
it('应该在 proposal 参数无效时友好提示', async () => {
const consoleSpy = vi.spyOn(console, 'error')
await viewProposal(null)
// 验证:记录错误日志
expect(consoleSpy).toHaveBeenCalledWith(
'[usePlanView] proposal 参数无效:',
expect.any(Error)
)
consoleSpy.mockRestore()
})
it('应该在 proposal.id 缺失时友好提示', async () => {
await viewProposal({})
// 验证:显示友好提示
expect(Taro.showToast).toHaveBeenCalledWith({
title: '计划书 ID 缺失',
icon: 'none'
})
})
it('应该在 proposalFiles 为空时友好提示', async () => {
await viewProposal({
id: 123,
order_status: '7',
proposal_files: []
})
// 验证:显示友好提示
expect(Taro.showToast).toHaveBeenCalledWith({
title: '暂无可查看的计划书',
icon: 'none'
})
})
it('应该支持 onError 回调', async () => {
const onError = vi.fn()
await viewProposal({}, { onError })
expect(onError).toHaveBeenCalledWith(expect.any(Error))
})
})
describe('字段分组测试', () => {
it('应该能按分组获取字段', () => {
// 由于 getFieldsByGroup 不在 useFieldValueTransform 导出中,我们测试配置
const basicFields = Object.values(PLAN_FIELD_DEFINITIONS).filter(f => f.group === FIELD_GROUPS.BASIC)
const coverageFields = Object.values(PLAN_FIELD_DEFINITIONS).filter(f => f.group === FIELD_GROUPS.COVERAGE)
const withdrawalFields = Object.values(PLAN_FIELD_DEFINITIONS).filter(f => f.group === FIELD_GROUPS.WITHDRAWAL)
// 验证:分组正确
expect(basicFields.length).toBeGreaterThan(0)
expect(coverageFields.length).toBeGreaterThan(0)
expect(withdrawalFields.length).toBeGreaterThan(0)
// 验证:customer_name 在 BASIC 组
expect(PLAN_FIELD_DEFINITIONS.customer_name.group).toBe(FIELD_GROUPS.BASIC)
// 验证:coverage 在 COVERAGE 组
expect(PLAN_FIELD_DEFINITIONS.coverage.group).toBe(FIELD_GROUPS.COVERAGE)
// 验证:withdrawal_mode 在 WITHDRAWAL 组
expect(PLAN_FIELD_DEFINITIONS.withdrawal_mode.group).toBe(FIELD_GROUPS.WITHDRAWAL)
})
it('应该通过 getFieldsByGroup 获取分组字段', () => {
const basicFields = getFieldsByGroup(FIELD_GROUPS.BASIC)
const coverageFields = getFieldsByGroup(FIELD_GROUPS.COVERAGE)
const withdrawalFields = getFieldsByGroup(FIELD_GROUPS.WITHDRAWAL)
expect(Object.keys(basicFields).length).toBeGreaterThan(0)
expect(Object.keys(coverageFields).length).toBeGreaterThan(0)
expect(Object.keys(withdrawalFields).length).toBeGreaterThan(0)
expect(basicFields.customer_name).toBeDefined()
expect(coverageFields.coverage).toBeDefined()
expect(withdrawalFields.withdrawal_mode).toBeDefined()
})
})
})