/** * M03-01 客户主数据 - CustomerStore 单元测试骨架 * * 覆盖场景: * - US-01: 列表分页查询 (正常/筛选/角色默认/空结果) * - US-03: 新增客户 * - US-04: 客户详情查询 * - US-05: 编辑客户 (含乐观锁 versionNum) * - US-06: 地址管理 (列表/新增/删除) */ import { setActivePinia, createPinia } from 'pinia' import { describe, it, expect, beforeEach, vi } from 'vitest' // Mock API 模块 vi.mock('@/api/mdmCustomer/index', () => ({ findCrmCustomerPage: vi.fn(), saveCrmCustomer: vi.fn(), updateCrmCustomer: vi.fn(), getCrmCustomerById: vi.fn(), findAddressListByCustomerId: vi.fn(), saveCrmCustomerAddress: vi.fn(), deleteCrmCustomerAddress: vi.fn(), })) import { findCrmCustomerPage, saveCrmCustomer, updateCrmCustomer, getCrmCustomerById, findAddressListByCustomerId, } from '@/api/mdmCustomer/index' import { useCustomerStore } from '@/store/modules/mdmCustomer/customerStore' // 5 条以上 Mock 客户数据 const mockCustomers = [ { customerId: 1, customerCode: 'CUS-20260626-0001', customerName: '上海XX汽车零部件有限公司', shortName: 'XX汽配', tradeType: 'DOMESTIC', creditCode: '91310115XXXXXXXXXX', parentId: null, parentCustomerName: null, responsibleUserId: 1001, customerStatus: '正常', creationDate: '2026-06-20 14:00:00', versionNum: 1 }, { customerId: 2, customerCode: 'CUS-20260626-0002', customerName: '浙江YY铝业有限公司', shortName: 'YY铝业', tradeType: 'EXPORT', creditCode: '91330100XXXXXXXXXX', parentId: null, parentCustomerName: null, responsibleUserId: 1002, customerStatus: '正常', creationDate: '2026-06-19 10:30:00', versionNum: 1 }, { customerId: 3, customerCode: 'CUS-20260626-0003', customerName: '北京ZZ贸易有限公司', shortName: null, tradeType: 'DOMESTIC', creditCode: null, parentId: 100, parentCustomerCode: 'CUS-20260601-0001', parentCustomerName: 'XX集团', responsibleUserId: 1003, customerStatus: '正常', creationDate: '2026-06-18 16:45:00', versionNum: 2 }, { customerId: 4, customerCode: 'CUS-20260626-0004', customerName: '广州AA金属制品有限公司', shortName: 'AA金属', tradeType: 'EXPORT', creditCode: '91440100XXXXXXXXXX', parentId: null, parentCustomerName: null, responsibleUserId: 1004, customerStatus: '停用', creationDate: '2026-05-15 11:20:00', versionNum: 3 }, { customerId: 5, customerCode: 'CUS-20260626-0005', customerName: '深圳BB电子有限公司', shortName: 'BB电子', tradeType: 'DOMESTIC', creditCode: '91440300XXXXXXXXXX', parentId: null, parentCustomerName: null, responsibleUserId: 1005, customerStatus: '正常', creationDate: '2026-06-21 09:30:00', versionNum: 1 }, { customerId: 6, customerCode: 'CUS-20260626-0006', customerName: '成都CC装备有限公司', shortName: null, tradeType: 'DOMESTIC', creditCode: null, parentId: 100, parentCustomerCode: 'CUS-20260601-0001', parentCustomerName: 'XX集团', responsibleUserId: 1006, customerStatus: '停用', creationDate: '2026-06-22 08:00:00', versionNum: 1 }, ] describe('CustomerStore', () => { let store: ReturnType beforeEach(() => { setActivePinia(createPinia()) vi.clearAllMocks() store = useCustomerStore() }) // ==================== US-01: 列表分页查询 ==================== describe('fetchList - 列表分页查询', () => { it('无条件分页查询返回客户数据', async () => { ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: mockCustomers.slice(0, 3), total: 6 }, }) await store.fetchList() expect(store.tableData.length).toBe(3) expect(store.total).toBe(6) expect(findCrmCustomerPage).toHaveBeenCalledTimes(1) }) it('按客户名称模糊搜索', async () => { ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [mockCustomers[0]], total: 1 }, }) await store.fetchList({ customerName: 'XX' }) expect(store.tableData.length).toBe(1) expect(store.tableData[0].customerName).toContain('XX') }) it('空结果返回空数组', async () => { ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [], total: 0 }, }) await store.fetchList({ customerName: '不存在' }) expect(store.tableData).toEqual([]) expect(store.total).toBe(0) }) it('销售员角色默认仅查询正常状态', async () => { store.currentRole = 'sales' ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [], total: 0 }, }) await store.fetchList() const callArgs = (findCrmCustomerPage as ReturnType).mock.calls[0][0] expect(callArgs.params.customerStatus).toBe('正常') }) it('管理员角色默认查询全部状态', async () => { store.currentRole = 'admin' ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [], total: 0 }, }) await store.fetchList() const callArgs = (findCrmCustomerPage as ReturnType).mock.calls[0][0] expect(callArgs.params.customerStatus).toBe('') }) }) // ==================== US-04: 客户详情 ==================== describe('fetchDetail - 客户详情查询', () => { it('按 customerId 查询客户详情', async () => { ;(getCrmCustomerById as ReturnType).mockResolvedValue({ code: 800, data: { ...mockCustomers[0], addressList: [] }, }) await store.fetchDetail(1) expect(store.detailData?.customerName).toBe('上海XX汽车零部件有限公司') expect(store.detailVisible).toBe(false) // fetchDetail 不自动打开 }) }) // ==================== US-03 / US-05: 新增/编辑 ==================== describe('openAddForm / openEditForm', () => { it('openAddForm 设置为新增模式', () => { store.openAddForm() expect(store.formMode).toBe('add') expect(store.formVisible).toBe(true) expect(store.formData.customerCode).toBe('') }) it('openEditForm 回填行数据并保存 versionNum', () => { const row = mockCustomers[0] store.openEditForm(row) expect(store.formMode).toBe('edit') expect(store.formVisible).toBe(true) expect(store.formData.customerCode).toBe('CUS-20260626-0001') expect(store.formVersionNum).toBe(1) }) }) describe('saveOrUpdateCustomer', () => { it('新增客户成功关闭弹窗', async () => { store.openAddForm() store.formData.customerName = '新客户' store.formData.tradeType = 'DOMESTIC' ;(saveCrmCustomer as ReturnType).mockResolvedValue({ code: 800 }) ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [], total: 0 }, }) const result = await store.saveOrUpdateCustomer() expect(result).toBe(true) expect(store.formVisible).toBe(false) }) it('编辑客户携带 versionNum', async () => { const row = mockCustomers[0] store.openEditForm(row) store.formData.shortName = '新简称' ;(updateCrmCustomer as ReturnType).mockResolvedValue({ code: 800 }) ;(findCrmCustomerPage as ReturnType).mockResolvedValue({ code: 800, data: { rows: [], total: 0 }, }) await store.saveOrUpdateCustomer() const callArgs = (updateCrmCustomer as ReturnType).mock.calls[0][0] expect(callArgs.params.versionNum).toBe(1) }) }) // ==================== US-06: 地址管理 ==================== describe('地址管理', () => { it('fetchAddressList 加载地址列表', async () => { ;(findAddressListByCustomerId as ReturnType).mockResolvedValue({ code: 800, data: [{ addressId: 1, customerId: 1, addressDetail: '上海市金山区XX路100号', isDefault: 1 }], }) await store.fetchAddressList(1) expect(store.addressList.length).toBe(1) expect(store.addressList[0].isDefault).toBe(1) }) }) })