Files
gym-manage/gym-manage-web/src/__tests__/components/CourseLabelManagement.test.ts
T

248 lines
7.5 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'
import { createPinia, setActivePinia } from 'pinia'
import CourseLabelManagement from '@/views/groupcourse/CourseLabelManagement.vue'
// ===== Mock Element Plus =====
vi.mock('element-plus', () => ({
ElMessage: { success: vi.fn(), error: vi.fn(), warning: vi.fn() },
ElMessageBox: { confirm: vi.fn() },
}))
// ===== Mock @element-plus/icons-vue =====
vi.mock('@element-plus/icons-vue', () => ({
Search: { template: '<svg/>' },
}))
// ===== Mock groupCourse.api =====
vi.mock('@/api/groupCourse.api', () => ({
courseLabelApi: {
getPage: vi.fn().mockResolvedValue({
content: [
{ id: 1, labelName: '热门', color: '#FF0000', description: '热门标签' },
{ id: 2, labelName: '新手', color: '#00FF00', description: '新手友好' },
],
totalElements: 2,
}),
create: vi.fn().mockResolvedValue({}),
update: vi.fn().mockResolvedValue({}),
delete: vi.fn().mockResolvedValue({}),
},
}))
describe('CourseLabelManagement Component', () => {
let router: any
let wrapper: any
const defaultStubs = {
'el-button': { template: '<button @click="$emit(\'click\')"><slot/></button>', props: ['type', 'size', 'link'] },
'el-card': { template: '<div class="el-card"><slot/><slot name="header"/></div>' },
'el-input': { template: '<input/>', props: ['modelValue'], emits: ['update:modelValue'] },
'el-icon': { template: '<svg/>' },
'el-table': { template: '<div class="el-table"><slot/></div>', props: ['data', 'loading'] },
'el-table-column': true,
'el-tag': { template: '<span class="el-tag"><slot/></span>', props: ['type', 'effect', 'size', 'color'] },
'el-pagination': true,
'el-dialog': { template: '<div v-if="modelValue" class="el-dialog"><slot/><slot name="footer"/></div>', props: ['modelValue', 'title', 'width'] },
'el-form': { template: '<form><slot/></form>', props: ['model', 'rules', 'label-width', 'ref'] },
'el-form-item': { template: '<div class="el-form-item"><slot/></div>', props: ['label', 'prop'] },
'el-color-picker': true,
}
beforeEach(() => {
const pinia = createPinia()
setActivePinia(pinia)
router = createRouter({
history: createMemoryHistory(),
routes: [{ path: '/', component: { template: '<div/>' } }],
})
vi.clearAllMocks()
})
afterEach(() => {
if (wrapper) wrapper.unmount()
})
// ==================== 组件初始化 ====================
it('应该渲染标签管理容器', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.find('.course-label-management').exists()).toBe(true)
})
it('应该渲染搜索区域', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.html()).toContain('搜索标签名称')
})
it('应该渲染新增标签按钮', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.html()).toContain('新增标签')
})
// ==================== 初始状态 ====================
it('初始 loading 状态为 false', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.loading).toBe(false)
})
it('初始搜索关键词为空', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.searchKeyword).toBe('')
})
it('初始 modalVisible 为 false', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.modalVisible).toBe(false)
})
it('初始 currentPage 为 1', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.currentPage).toBe(1)
})
it('初始 pageSize 为 10', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.pageSize).toBe(10)
})
// ==================== 方法存在性 ====================
it('应该暴露 handleSearch 方法', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(typeof wrapper.vm.handleSearch).toBe('function')
})
it('应该暴露 handleAdd 方法', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(typeof wrapper.vm.handleAdd).toBe('function')
})
it('应该暴露 handleEdit 方法', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(typeof wrapper.vm.handleEdit).toBe('function')
})
it('应该暴露 handleDelete 方法', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(typeof wrapper.vm.handleDelete).toBe('function')
})
// ==================== 新增弹窗 ====================
it('handleAdd 应打开新增弹窗并重置表单', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
wrapper.vm.handleAdd()
await wrapper.vm.$nextTick()
expect(wrapper.vm.modalVisible).toBe(true)
expect(wrapper.vm.modalTitle).toBe('新增标签')
expect(wrapper.vm.formState.labelName).toBe('')
})
// ==================== 表单规则 ====================
it('应定义 labelName 必填规则', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.formRules.labelName).toBeDefined()
expect(wrapper.vm.formRules.labelName[0].required).toBe(true)
})
it('应定义 color 必填规则', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
expect(wrapper.vm.formRules.color).toBeDefined()
})
// ==================== 分页操作 ====================
it('onSizeChange 应重置当前页为 1', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
wrapper.vm.currentPage = 3
wrapper.vm.onSizeChange()
await wrapper.vm.$nextTick()
expect(wrapper.vm.currentPage).toBe(1)
})
it('handleSearch 应重置当前页为 1', async () => {
wrapper = mount(CourseLabelManagement, {
global: { plugins: [router], stubs: defaultStubs },
})
await wrapper.vm.$nextTick()
wrapper.vm.currentPage = 3
wrapper.vm.handleSearch()
await wrapper.vm.$nextTick()
expect(wrapper.vm.currentPage).toBe(1)
})
})