Files
it/frontend/tests/unit/components/Header.spec.js
XCool f25b0307db
Some checks failed
CI/CD Pipeline / 测试 (18.x) (push) Has been cancelled
CI/CD Pipeline / 测试 (20.x) (push) Has been cancelled
CI/CD Pipeline / 安全检查 (push) Has been cancelled
CI/CD Pipeline / 部署 (push) Has been cancelled
CI/CD Pipeline / 通知 (push) Has been cancelled
初始化
2025-11-03 19:47:36 +08:00

281 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import Header from '@/components/Header.vue'
import { useCategoryStore } from '@/stores/categoryStore'
// 模拟路由
const mockRouter = {
push: vi.fn(),
replace: vi.fn(),
go: vi.fn(),
back: vi.fn(),
forward: vi.fn(),
currentRoute: {
value: {
path: '/',
name: 'Home',
params: {},
query: {},
hash: '',
fullPath: '/',
matched: [],
meta: {},
}
}
}
// 模拟Element Plus组件
vi.mock('element-plus', () => ({
ElMenu: {
name: 'ElMenu',
template: '<div class="el-menu"><slot></slot></div>',
props: ['mode', 'default-active', 'router']
},
ElMenuItem: {
name: 'ElMenuItem',
template: '<div class="el-menu-item" @click="$emit(\'click\')"><slot></slot></div>',
props: ['index']
},
ElSubMenu: {
name: 'ElSubMenu',
template: '<div class="el-sub-menu"><slot name="title"></slot><slot></slot></div>',
props: ['index']
},
ElButton: {
name: 'ElButton',
template: '<button class="el-button" @click="$emit(\'click\')"><slot></slot></button>',
props: ['type', 'size', 'icon']
},
ElIcon: {
name: 'ElIcon',
template: '<i class="el-icon"><slot></slot></i>'
},
ElDropdown: {
name: 'ElDropdown',
template: '<div class="el-dropdown"><slot></slot></div>',
props: ['trigger']
},
ElDropdownMenu: {
name: 'ElDropdownMenu',
template: '<div class="el-dropdown-menu"><slot></slot></div>'
},
ElDropdownItem: {
name: 'ElDropdownItem',
template: '<div class="el-dropdown-item" @click="$emit(\'click\')"><slot></slot></div>'
},
ElDrawer: {
name: 'ElDrawer',
template: '<div class="el-drawer" v-if="modelValue"><slot></slot></div>',
props: ['modelValue', 'title', 'direction', 'size'],
emits: ['update:modelValue']
}
}))
// 模拟Element Plus图标
vi.mock('@element-plus/icons-vue', () => ({
Menu: { name: 'Menu', template: '<i class="icon-menu"></i>' },
Close: { name: 'Close', template: '<i class="icon-close"></i>' },
User: { name: 'User', template: '<i class="icon-user"></i>' },
Setting: { name: 'Setting', template: '<i class="icon-setting"></i>' },
Monitor: { name: 'Monitor', template: '<i class="icon-monitor"></i>' },
DataAnalysis: { name: 'DataAnalysis', template: '<i class="icon-data-analysis"></i>' }
}))
describe('Header.vue', () => {
let wrapper
let pinia
let categoryStore
beforeEach(() => {
// 创建Pinia实例
pinia = createPinia()
setActivePinia(pinia)
// 获取store实例
categoryStore = useCategoryStore()
// 模拟store数据
categoryStore.categories = [
{ id: 1, name: '手机CPU', productCount: 50 },
{ id: 2, name: '手机GPU', productCount: 40 },
{ id: 3, name: '电脑CPU', productCount: 60 },
{ id: 4, name: '电脑GPU', productCount: 45 }
]
// 挂载组件
wrapper = mount(Header, {
global: {
plugins: [pinia],
mocks: {
$router: mockRouter
},
stubs: {
'router-link': true,
'router-view': true,
'cache-status-indicator': true
}
}
})
})
it('应该正确渲染Header组件', () => {
expect(wrapper.exists()).toBe(true)
expect(wrapper.find('.header').exists()).toBe(true)
expect(wrapper.find('.header__logo').exists()).toBe(true)
expect(wrapper.find('.header__nav').exists()).toBe(true)
})
it('应该显示应用标题', () => {
const title = wrapper.find('.header__title')
expect(title.exists()).toBe(true)
expect(title.text()).toBe('硬件性能排行榜')
})
it('应该显示产品类别菜单', async () => {
// 等待组件加载完成
await wrapper.vm.$nextTick()
// 检查类别菜单是否存在
const categoryMenu = wrapper.find('.category-menu')
expect(categoryMenu.exists()).toBe(true)
// 检查类别数量
const menuItems = wrapper.findAll('.el-menu-item')
expect(menuItems.length).toBeGreaterThan(0)
})
it('应该显示产品对比按钮', () => {
const compareButton = wrapper.find('[data-testid="compare-button"]')
expect(compareButton.exists()).toBe(true)
expect(compareButton.text()).toContain('产品对比')
})
it('应该显示性能监控按钮', () => {
const monitorButton = wrapper.find('[data-testid="monitor-button"]')
expect(monitorButton.exists()).toBe(true)
expect(monitorButton.text()).toContain('性能监控')
})
it('应该响应式地在移动端显示菜单按钮', async () => {
// 模拟移动端窗口大小
global.innerWidth = 500
window.dispatchEvent(new Event('resize'))
await wrapper.vm.$nextTick()
// 检查移动端菜单按钮是否存在
const menuButton = wrapper.find('.header__mobile-menu')
expect(menuButton.exists()).toBe(true)
})
it('应该正确处理菜单点击事件', async () => {
// 获取第一个菜单项
const firstMenuItem = wrapper.find('.el-menu-item')
expect(firstMenuItem.exists()).toBe(true)
// 点击菜单项
await firstMenuItem.trigger('click')
// 检查是否触发了路由导航
// 注意由于使用了router-link stub这里需要检查组件内部逻辑
// 在实际测试中你可能需要模拟router-link的行为或检查组件内部状态
})
it('应该正确处理产品对比按钮点击', async () => {
const compareButton = wrapper.find('[data-testid="compare-button"]')
expect(compareButton.exists()).toBe(true)
// 点击产品对比按钮
await compareButton.trigger('click')
// 检查是否触发了路由导航
expect(mockRouter.push).toHaveBeenCalledWith('/compare')
})
it('应该正确处理性能监控按钮点击', async () => {
const monitorButton = wrapper.find('[data-testid="monitor-button"]')
expect(monitorButton.exists()).toBe(true)
// 点击性能监控按钮
await monitorButton.trigger('click')
// 检查是否触发了路由导航
expect(mockRouter.push).toHaveBeenCalledWith('/monitor')
})
it('应该在移动端正确处理菜单展开/收起', async () => {
// 模拟移动端窗口大小
global.innerWidth = 500
window.dispatchEvent(new Event('resize'))
await wrapper.vm.$nextTick()
// 获取菜单按钮
const menuButton = wrapper.find('.header__mobile-menu')
expect(menuButton.exists()).toBe(true)
// 点击菜单按钮
await menuButton.trigger('click')
// 检查菜单是否展开
expect(wrapper.vm.mobileMenuVisible).toBe(true)
// 再次点击菜单按钮
await menuButton.trigger('click')
// 检查菜单是否收起
expect(wrapper.vm.mobileMenuVisible).toBe(false)
})
it('应该正确计算当前激活的菜单项', async () => {
// 模拟当前路由为类别页面
mockRouter.currentRoute.value.path = '/category/1'
await wrapper.vm.$nextTick()
// 检查当前激活的菜单项
expect(wrapper.vm.activeMenuItem).toBe('category')
})
it('应该在组件挂载时加载类别数据', async () => {
// 模拟fetchCategories方法
const fetchCategoriesSpy = vi.spyOn(categoryStore, 'fetchCategories')
// 重新挂载组件
wrapper = mount(Header, {
global: {
plugins: [pinia],
mocks: {
$router: mockRouter
},
stubs: {
'router-link': true,
'router-view': true,
'cache-status-indicator': true
}
}
})
// 等待组件加载完成
await wrapper.vm.$nextTick()
// 检查是否调用了fetchCategories
expect(fetchCategoriesSpy).toHaveBeenCalled()
})
it('应该正确处理窗口大小变化', async () => {
// 模拟桌面端窗口大小
global.innerWidth = 1200
window.dispatchEvent(new Event('resize'))
await wrapper.vm.$nextTick()
// 检查isMobile计算属性
expect(wrapper.vm.isMobile).toBe(false)
// 模拟移动端窗口大小
global.innerWidth = 500
window.dispatchEvent(new Event('resize'))
await wrapper.vm.$nextTick()
// 检查isMobile计算属性
expect(wrapper.vm.isMobile).toBe(true)
})
})