2026-08-25 19:56:37 +08:00
|
|
|
import { flushPromises, mount } from '@vue/test-utils'
|
|
|
|
|
import { defineComponent } from 'vue'
|
|
|
|
|
import { createMemoryHistory, createRouter } from 'vue-router'
|
2026-07-30 17:59:43 +08:00
|
|
|
import { describe, expect, it } from 'vitest'
|
2026-08-03 18:49:18 +08:00
|
|
|
import { adminNavigation, filterAdminNavigation, type AdminNavigationNode } from '@/core/navigation/adminNavigation'
|
2026-07-30 17:59:43 +08:00
|
|
|
import AdminSidebarMenu from './AdminSidebarMenu.vue'
|
|
|
|
|
|
2026-08-05 10:46:42 +08:00
|
|
|
const visibleNavigation = filterAdminNavigation(adminNavigation, new Set([
|
|
|
|
|
'platform:customer:list',
|
|
|
|
|
'platform:warehouse:list',
|
|
|
|
|
'platform:firstleg:inbound:list',
|
|
|
|
|
'platform:location:list',
|
|
|
|
|
'platform:pallet:list',
|
|
|
|
|
'firstleg:fba-pricing:rate:view',
|
|
|
|
|
'platform:express:list',
|
|
|
|
|
'platform:counterparty:list',
|
|
|
|
|
'platform:download:list',
|
|
|
|
|
'platform:user:list',
|
|
|
|
|
'platform:role:list',
|
|
|
|
|
'platform:permission:list',
|
|
|
|
|
]))
|
|
|
|
|
|
2026-07-30 17:59:43 +08:00
|
|
|
const stubs = {
|
2026-08-25 19:56:37 +08:00
|
|
|
'el-menu': defineComponent({
|
2026-08-18 16:09:43 +08:00
|
|
|
props: ['defaultActive', 'defaultOpeneds', 'collapse'],
|
2026-08-25 19:56:37 +08:00
|
|
|
emits: ['select'],
|
|
|
|
|
methods: { updateActiveIndex() {} },
|
|
|
|
|
template: '<nav :data-active="defaultActive" :data-openeds="JSON.stringify(defaultOpeneds)" :data-collapsed="String(collapse)" @click="$emit(\'select\', \'/warehouses\')"><slot /></nav>',
|
|
|
|
|
}),
|
2026-07-30 17:59:43 +08:00
|
|
|
'el-sub-menu': {
|
|
|
|
|
props: ['index'],
|
|
|
|
|
template: '<section data-kind="group" :data-index="index"><header><slot name="title" /></header><slot /></section>',
|
|
|
|
|
},
|
|
|
|
|
'el-menu-item': {
|
|
|
|
|
props: ['index'],
|
|
|
|
|
template: '<a data-kind="item" :data-index="index"><slot name="title" /><slot /></a>',
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('AdminSidebarMenu', () => {
|
2026-08-25 19:56:37 +08:00
|
|
|
function routerPlugin() {
|
|
|
|
|
return createRouter({
|
|
|
|
|
history: createMemoryHistory(),
|
|
|
|
|
routes: [
|
|
|
|
|
{ path: '/', component: { template: '<p />' } },
|
|
|
|
|
{ path: '/warehouses', component: { template: '<p />' } },
|
|
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
it('renders the visible phase-one modules and forwards the active route', () => {
|
2026-07-30 17:59:43 +08:00
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
2026-08-05 10:46:42 +08:00
|
|
|
props: { items: visibleNavigation, activePath: '/system/users', collapsed: false },
|
2026-08-25 19:56:37 +08:00
|
|
|
global: { stubs, plugins: [routerPlugin()] },
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(wrapper.get('nav').attributes('data-active')).toBe('/system/users')
|
2026-08-18 16:09:43 +08:00
|
|
|
expect(wrapper.get('nav').attributes('data-openeds')).toBe('[]')
|
2026-08-03 11:45:45 +08:00
|
|
|
const groupTitles = wrapper.findAll('[data-kind="group"] header').map((node) => node.text())
|
|
|
|
|
const paths = wrapper.findAll('[data-kind="item"]').map((node) => node.attributes('data-index'))
|
|
|
|
|
|
|
|
|
|
expect(groupTitles).toContain('客户管理')
|
2026-09-11 11:15:04 +08:00
|
|
|
expect(groupTitles).not.toContain('供应商中心')
|
|
|
|
|
expect(groupTitles).toContain('通用')
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(groupTitles).toContain('系统管理')
|
2026-10-03 04:35:55 +08:00
|
|
|
expect(groupTitles).not.toContain('预约管理')
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(groupTitles).not.toContain('财务管理')
|
2026-10-03 04:35:55 +08:00
|
|
|
expect(paths).not.toContain('/finance/customer-accounts')
|
2026-08-05 15:54:59 +08:00
|
|
|
expect(paths).toContain('/dashboard')
|
2026-08-06 13:56:04 +08:00
|
|
|
expect(paths).toContain('/orders')
|
2026-10-03 04:35:55 +08:00
|
|
|
expect(paths).not.toContain('/master-data/express-companies')
|
2026-08-05 10:46:42 +08:00
|
|
|
expect(paths).not.toContain('/orders/first-leg')
|
2026-08-05 15:54:59 +08:00
|
|
|
expect(paths).not.toContain('/first-leg-pricing/customer-rules')
|
2026-08-05 10:46:42 +08:00
|
|
|
expect(paths).toContain('/downloads')
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(paths).not.toContain('/prototype/reports/overview')
|
|
|
|
|
expect(paths).toContain('/system/permissions')
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('forwards desktop collapse state to Element Plus', () => {
|
|
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
2026-08-05 10:46:42 +08:00
|
|
|
props: { items: visibleNavigation, activePath: '/', collapsed: true },
|
2026-08-25 19:56:37 +08:00
|
|
|
global: { stubs, plugins: [routerPlugin()] },
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(wrapper.get('nav').attributes('data-collapsed')).toBe('true')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('renders only entries included in the filtered navigation input', () => {
|
|
|
|
|
const items = filterAdminNavigation(adminNavigation, new Set(['platform:permission:list']))
|
|
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
|
|
|
|
props: { items, activePath: '/', collapsed: false },
|
2026-08-25 19:56:37 +08:00
|
|
|
global: { stubs, plugins: [routerPlugin()] },
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(wrapper.text()).toContain('菜单权限')
|
2026-08-05 15:54:59 +08:00
|
|
|
expect(wrapper.text()).toContain('首页数据看板')
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(wrapper.text()).not.toContain('客户列表')
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('recursively renders three navigation levels', () => {
|
|
|
|
|
const items: AdminNavigationNode[] = [{
|
|
|
|
|
key: 'operations',
|
|
|
|
|
title: '运营管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
children: [{
|
|
|
|
|
key: 'shipment',
|
|
|
|
|
title: '运输管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
children: [{
|
|
|
|
|
key: 'tracking',
|
|
|
|
|
title: '轨迹管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
path: '/tracking',
|
|
|
|
|
permission: 'tracking:list',
|
|
|
|
|
}],
|
|
|
|
|
}],
|
|
|
|
|
}]
|
|
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
|
|
|
|
props: { items, activePath: '/tracking', collapsed: false },
|
2026-08-25 19:56:37 +08:00
|
|
|
global: { stubs, plugins: [routerPlugin()] },
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(wrapper.findAll('[data-kind="group"]')).toHaveLength(2)
|
|
|
|
|
expect(wrapper.get('[data-kind="item"]').attributes('data-index')).toBe('/tracking')
|
|
|
|
|
})
|
2026-08-11 18:06:35 +08:00
|
|
|
|
|
|
|
|
it('recreates the menu when asynchronously loaded groups change', async () => {
|
|
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
|
|
|
|
props: { items: [adminNavigation[0]], activePath: '/dashboard', collapsed: false },
|
2026-08-25 19:56:37 +08:00
|
|
|
global: { stubs, plugins: [routerPlugin()] },
|
2026-08-11 18:06:35 +08:00
|
|
|
})
|
|
|
|
|
const initialMenu = wrapper.get('nav').element
|
|
|
|
|
|
|
|
|
|
await wrapper.setProps({ items: visibleNavigation })
|
|
|
|
|
|
|
|
|
|
expect(wrapper.get('nav').element).not.toBe(initialMenu)
|
|
|
|
|
expect(wrapper.text()).toContain('系统管理')
|
|
|
|
|
})
|
2026-08-25 19:56:37 +08:00
|
|
|
|
|
|
|
|
it('navigates explicitly when a leaf menu item is selected', async () => {
|
|
|
|
|
const router = routerPlugin()
|
|
|
|
|
await router.push('/')
|
|
|
|
|
const wrapper = mount(AdminSidebarMenu, {
|
|
|
|
|
props: { items: visibleNavigation, activePath: '/', collapsed: false },
|
|
|
|
|
global: { stubs, plugins: [router] },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await wrapper.get('nav').trigger('click')
|
|
|
|
|
await flushPromises()
|
|
|
|
|
expect(router.currentRoute.value.path).toBe('/warehouses')
|
|
|
|
|
})
|
2026-07-30 17:59:43 +08:00
|
|
|
})
|