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: '' }, })) // ===== 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: '', props: ['type', 'size', 'link'] }, 'el-card': { template: '
' }, 'el-input': { template: '', props: ['modelValue'], emits: ['update:modelValue'] }, 'el-icon': { template: '' }, 'el-table': { template: '
', props: ['data', 'loading'] }, 'el-table-column': true, 'el-tag': { template: '', props: ['type', 'effect', 'size', 'color'] }, 'el-pagination': true, 'el-dialog': { template: '
', props: ['modelValue', 'title', 'width'] }, 'el-form': { template: '
', props: ['model', 'rules', 'label-width', 'ref'] }, 'el-form-item': { template: '
', props: ['label', 'prop'] }, 'el-color-picker': true, } beforeEach(() => { const pinia = createPinia() setActivePinia(pinia) router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/', component: { template: '
' } }], }) 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) }) })