281 lines
8.2 KiB
JavaScript
281 lines
8.2 KiB
JavaScript
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)
|
||
})
|
||
}) |