import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import UserView from '@/views/user/index.vue'
import { wrapResponse, wrapPageResponse, wrapErrorResponse } from '../../../fixtures'
// Mock vue-router
const mockPush = vi.fn()
vi.mock('vue-router', () => ({
useRouter: () => ({ push: mockPush }),
useRoute: () => ({ query: {} })
}))
// Mock element-plus
vi.mock('element-plus', () => ({
ElMessage: {
success: vi.fn(),
error: vi.fn(),
info: vi.fn(),
warning: vi.fn()
},
ElMessageBox: {
confirm: vi.fn().mockResolvedValue(true)
}
}))
// Mock user API
const mockListUsers = vi.fn()
const mockCreateUser = vi.fn()
const mockUpdateUser = vi.fn()
const mockDeleteUser = vi.fn()
const mockResetPassword = vi.fn()
vi.mock('@/api/user', () => ({
listUsers: (...args: any[]) => mockListUsers(...args),
createUser: (...args: any[]) => mockCreateUser(...args),
updateUser: (...args: any[]) => mockUpdateUser(...args),
deleteUser: (...args: any[]) => mockDeleteUser(...args),
resetPassword: (...args: any[]) => mockResetPassword(...args)
}))
// Mock user data
const mockUsers = [
{ id: '1', username: 'admin', role: 'admin', createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' },
{
id: '2',
username: 'operator1',
role: 'operator',
createdAt: '2024-01-02T00:00:00Z',
updatedAt: '2024-01-02T00:00:00Z'
},
{ id: '3', username: 'viewer1', role: 'viewer', createdAt: '2024-01-03T00:00:00Z', updatedAt: '2024-01-03T00:00:00Z' }
]
describe('User View', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
mockListUsers.mockResolvedValue(wrapPageResponse(mockUsers, 3))
})
const mountUser = () => {
return mount(UserView, {
global: {
plugins: [createPinia()],
stubs: {
'el-form': { template: '
' },
'el-form-item': { template: '
' },
'el-input': {
template:
'',
props: ['modelValue', 'placeholder', 'disabled', 'type']
},
'el-select': {
template:
'',
props: ['modelValue', 'placeholder', 'clearable']
},
'el-option': {
template: '',
props: ['label', 'value']
},
'el-button': {
template: '',
props: ['type', 'icon', 'loading', 'link'],
computed: {
htmlType() {
return this.type === 'submit' ? 'submit' : 'button'
}
}
},
'el-table': {
template: '',
props: ['data', 'loading', 'border']
},
'el-table-column': {
template: ' | ',
props: ['prop', 'label', 'width', 'align', 'type', 'fixed', 'minWidth']
},
'el-tag': {
template: '',
props: ['type']
},
'el-pagination': {
template: '',
props: ['currentPage', 'pageSize', 'pageSizes', 'total', 'layout']
},
'el-dialog': {
template: '
',
props: ['modelValue', 'title', 'width', 'destroyOnClose']
}
}
}
})
}
describe('页面渲染', () => {
it('应该正确渲染用户管理页面', async () => {
const wrapper = mountUser()
await flushPromises()
expect(wrapper.find('.page-container').exists()).toBe(true)
expect(wrapper.find('.search-form').exists()).toBe(true)
expect(wrapper.find('.table-actions').exists()).toBe(true)
})
it('应该显示新增用户按钮', async () => {
const wrapper = mountUser()
await flushPromises()
const addButton = wrapper.find('.table-actions button')
expect(addButton.exists()).toBe(true)
expect(addButton.text()).toContain('新增用户')
})
it('页面加载时应该调用获取用户列表 API', async () => {
mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
})
describe('用户列表', () => {
it('应该正确显示用户数据', async () => {
const wrapper = mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
it('搜索应该触发列表刷新', async () => {
const wrapper = mountUser()
await flushPromises()
mockListUsers.mockClear()
const searchBtn = wrapper.findAll('button').find((btn) => btn.text().includes('搜索'))
if (searchBtn) {
await searchBtn.trigger('click')
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
}
})
it('重置应该清空搜索条件', async () => {
const wrapper = mountUser()
await flushPromises()
mockListUsers.mockClear()
const resetBtn = wrapper.findAll('button').find((btn) => btn.text().includes('重置'))
if (resetBtn) {
await resetBtn.trigger('click')
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
}
})
})
describe('新增用户', () => {
it('点击新增按钮应该打开弹窗', async () => {
const wrapper = mountUser()
await flushPromises()
const addBtn = wrapper.findAll('button').find((btn) => btn.text().includes('新增用户'))
expect(addBtn).toBeDefined()
if (addBtn) {
await addBtn.trigger('click')
await flushPromises()
expect(wrapper.find('.el-dialog').exists()).toBe(true)
}
})
it('新增用户成功应该刷新列表', async () => {
mockCreateUser.mockResolvedValue(wrapResponse({ id: '4', username: 'newuser', role: 'viewer' }))
const wrapper = mountUser()
await flushPromises()
// 打开新增弹窗
const addBtn = wrapper.findAll('button').find((btn) => btn.text().includes('新增用户'))
if (addBtn) {
await addBtn.trigger('click')
await flushPromises()
// 验证弹窗打开
expect(wrapper.find('.el-dialog').exists()).toBe(true)
}
})
})
describe('编辑用户', () => {
it('编辑用户成功应该刷新列表', async () => {
mockUpdateUser.mockResolvedValue(wrapResponse({ id: '1', username: 'admin', role: 'admin' }))
const wrapper = mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
})
describe('删除用户', () => {
it('删除用户成功应该刷新列表', async () => {
const { ElMessageBox, ElMessage } = await import('element-plus')
mockDeleteUser.mockResolvedValue(wrapResponse(null))
const wrapper = mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
})
describe('重置密码', () => {
it('重置密码成功应该显示成功消息', async () => {
mockResetPassword.mockResolvedValue(wrapResponse(null))
const wrapper = mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
})
describe('角色显示', () => {
it('应该正确转换角色标签', async () => {
const wrapper = mountUser()
await flushPromises()
// 角色标签转换测试
expect(wrapper.html()).toBeDefined()
})
})
describe('API 错误处理', () => {
it('API 返回错误码应该正确处理', async () => {
mockListUsers.mockResolvedValue(wrapErrorResponse('获取失败', 500))
const wrapper = mountUser()
await flushPromises()
expect(mockListUsers).toHaveBeenCalled()
})
it('创建用户返回错误应该处理', async () => {
mockCreateUser.mockResolvedValue(wrapErrorResponse('用户名已存在', 400))
const wrapper = mountUser()
await flushPromises()
expect(wrapper.html()).toBeDefined()
})
})
})