Files
admin-web/src/app/components/WorkspaceTabs.test.ts
T
2026-10-20 23:34:11 +08:00

96 lines
4.2 KiB
TypeScript

import ElementPlus, { ElMessageBox } from 'element-plus'
import { createPinia, setActivePinia } from 'pinia'
import { flushPromises, mount } from '@vue/test-utils'
import { createMemoryHistory, createRouter } from 'vue-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { useAuthStore } from '@/core/auth/auth'
import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork'
import { useWorkspaceTabsStore } from '@/core/workspace-tabs/workspaceTabs'
import WorkspaceTabs from './WorkspaceTabs.vue'
vi.mock('element-plus', async (importOriginal) => {
const actual = await importOriginal<typeof import('element-plus')>()
return { ...actual, ElMessage: { error: vi.fn() }, ElMessageBox: { confirm: vi.fn() } }
})
async function mountTabs() {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
auth.token = 'token'
auth.user = { id: '1', name: '平台管理员', account: 'admin', roles: ['PLATFORM_ADMIN'] }
auth.permissions = ['platform:customer:list', 'platform:warehouse:list']
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/customers', name: 'customers', component: { template: '<p />' }, meta: { requiresAuth: true, title: '客户管理' } },
{ path: '/warehouses', name: 'warehouses', component: { template: '<p />' }, meta: { requiresAuth: true, title: '仓库管理' } },
],
})
await router.push('/customers')
await router.isReady()
const workspace = useWorkspaceTabsStore()
workspace.initialize('1')
workspace.open({ id: 'warehouses', title: '仓库管理', path: '/warehouses' })
workspace.open({ id: 'customers', title: '客户管理', path: '/customers' })
const wrapper = mount(WorkspaceTabs, { attachTo: document.body, global: { plugins: [pinia, router, ElementPlus] } })
return { wrapper, router, workspace, unsaved: useUnsavedWorkStore() }
}
describe('WorkspaceTabs', () => {
beforeEach(() => {
sessionStorage.clear()
vi.mocked(ElMessageBox.confirm).mockReset()
})
afterEach(() => { document.body.innerHTML = '' })
it('retains its own theme on the teleported management menu', async () => {
const { wrapper } = await mountTabs()
await wrapper.get('[aria-label="页签管理"]').trigger('click')
await flushPromises()
const popper = document.body.querySelector('.shell-header-popper')
expect(popper?.classList.contains('admin-workbench')).toBe(true)
expect(popper?.closest('.workspace-tabs')).toBeNull()
wrapper.unmount()
})
it('keeps a dirty tab open when discard confirmation is cancelled', async () => {
const { wrapper, workspace, unsaved } = await mountTabs()
unsaved.upsert({ key: 'customer-form', label: '客户资料', dirty: true, requiredPermission: null, workspaceTabId: 'customers' })
vi.mocked(ElMessageBox.confirm).mockRejectedValueOnce('cancel')
await wrapper.get('[aria-label="关闭页签:客户管理"]').trigger('click')
await flushPromises()
expect(workspace.tabs.map((tab) => tab.id)).toContain('customers')
expect(wrapper.find('.workspace-tab.is-dirty').exists()).toBe(true)
})
it('closes confirmed dirty work and activates the most recent remaining tab', async () => {
const { wrapper, router, workspace, unsaved } = await mountTabs()
unsaved.upsert({ key: 'customer-form', label: '客户资料', dirty: true, requiredPermission: null, workspaceTabId: 'customers' })
vi.mocked(ElMessageBox.confirm).mockResolvedValueOnce({} as never)
await wrapper.get('[aria-label="关闭页签:客户管理"]').trigger('click')
await flushPromises()
expect(workspace.tabs.map((tab) => tab.id)).toEqual(['warehouses'])
expect(router.currentRoute.value.path).toBe('/warehouses')
expect(unsaved.findForWorkspace('customers')).toEqual([])
})
it('uses the mouse wheel to scroll an overflowing tab list horizontally', async () => {
const { wrapper } = await mountTabs()
const tabList = wrapper.get<HTMLElement>('.workspace-tab-list')
Object.defineProperties(tabList.element, {
clientWidth: { configurable: true, value: 320 },
scrollWidth: { configurable: true, value: 720 },
})
await tabList.trigger('wheel', { deltaY: 120 })
expect(tabList.element.scrollLeft).toBe(120)
})
})