20260921231313
This commit is contained in:
1 parent
ac710c1801
commit
583984c24f
81 files changed
+2523
-288
No files matched your search
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 诊断:侧栏菜单与页签的选中态(is-active)翻转序列。
|
||||
*
|
||||
* 背景:在"其他数据 / 联系人"之间来回切换时,旧项的选中背景会「再亮一次」再灭。
|
||||
* 本用例用 MutationObserver 记录完整的类变化序列,用于二分定位:
|
||||
* - 状态层问题(类被加回):序列出现多余的翻转 → 需要修导航/渲染时序
|
||||
* - 序列干净(每项只按导航翻转):闪烁来自 CSS 过渡的视觉 → 需要调过渡
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
|
||||
// 顶级菜单(不经目录):目录的展开状态只在 NavMenuItem 初始化时计算,
|
||||
// 初始路由 /dashboard 不在目录下 → 目录收起 → 子项不渲染,这里用顶级项保证可见
|
||||
const MENU_ROWS = [
|
||||
{
|
||||
b_id: 'm1',
|
||||
b_name: '其他数据',
|
||||
b_menu_type: 'page',
|
||||
b_canuse: 1,
|
||||
b_xh: 1,
|
||||
b_route: '/base/otherdata',
|
||||
b_parent_id: '',
|
||||
},
|
||||
{
|
||||
b_id: 'm2',
|
||||
b_name: '联系人',
|
||||
b_menu_type: 'page',
|
||||
b_canuse: 1,
|
||||
b_xh: 2,
|
||||
b_route: '/base/contact',
|
||||
b_parent_id: '',
|
||||
},
|
||||
]
|
||||
|
||||
vi.mock('@/views/dashboard/index.vue', () => ({
|
||||
default: { name: 'Dashboard', template: '<div data-page="Dashboard" />' },
|
||||
}))
|
||||
vi.mock('@/views/base/otherdata/index.vue', () => ({
|
||||
default: { name: 'BaseOtherdata', template: '<div data-page="Otherdata" />' },
|
||||
}))
|
||||
vi.mock('@/views/base/contact/index.vue', () => ({
|
||||
default: { name: 'BaseContact', template: '<div data-page="Contact" />' },
|
||||
}))
|
||||
vi.mock('@/views/module/module-management/index.vue', () => ({
|
||||
default: { name: 'ModuleManagement', template: '<div data-page="MM" />' },
|
||||
}))
|
||||
vi.mock('@/views/module/menu-management/index.vue', () => ({
|
||||
default: { name: 'MenuManagement', template: '<div data-page="MenuM" />' },
|
||||
}))
|
||||
vi.mock('@/views/detail/DetailPage.vue', () => ({
|
||||
default: { name: 'DetailPageStub', props: ['id'], template: '<div />' },
|
||||
}))
|
||||
vi.mock('@/services/api', () => ({
|
||||
loadDataApi: vi.fn((table) =>
|
||||
Promise.resolve({ data: table === 's_menu' ? MENU_ROWS : [], code: 0, success: true }),
|
||||
),
|
||||
describeApi: vi.fn(() => Promise.resolve({ data: [], code: 0, success: true })),
|
||||
saveObjectApi: vi.fn(() => Promise.resolve({ code: 0, success: true })),
|
||||
nextIdApi: vi.fn(() => Promise.resolve({ data: 1, code: 0, success: true })),
|
||||
}))
|
||||
|
||||
import App from '@/App.vue'
|
||||
import router from '@/router'
|
||||
import { createTestPinia } from '../helpers/app.js'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
sessionStorage.clear()
|
||||
createTestPinia()
|
||||
useAuthStore().setSession({ token: 't', user: { id: 1, account: 'g3soft', name: 'U' } }, 'ORG')
|
||||
await router.push('/dashboard')
|
||||
await router.isReady()
|
||||
})
|
||||
|
||||
async function mountApp() {
|
||||
const wrapper = mount(App, { global: { plugins: [router] }, attachTo: document.body })
|
||||
await flushPromises()
|
||||
return wrapper
|
||||
}
|
||||
|
||||
describe('选中态翻转序列(闪烁诊断)', () => {
|
||||
it('其他数据/联系人来回切换:is-active 序列无多余翻转', async () => {
|
||||
const wrapper = await mountApp()
|
||||
await router.push('/base/otherdata')
|
||||
await flushPromises()
|
||||
|
||||
const navItem = (name) =>
|
||||
Array.from(document.querySelectorAll('.fms-nav-link')).find((el) => el.textContent.includes(name))
|
||||
expect(navItem('其他数据')).toBeTruthy()
|
||||
|
||||
const read = () => ({
|
||||
other: navItem('其他数据')?.classList.contains('is-active') ? 1 : 0,
|
||||
contact: navItem('联系人')?.classList.contains('is-active') ? 1 : 0,
|
||||
})
|
||||
const flips = []
|
||||
let last = JSON.stringify(read())
|
||||
const mo = new MutationObserver(() => {
|
||||
const now = JSON.stringify(read())
|
||||
if (now !== last) {
|
||||
flips.push(now)
|
||||
last = now
|
||||
}
|
||||
})
|
||||
mo.observe(document.body, { subtree: true, attributes: true, attributeFilter: ['class'] })
|
||||
|
||||
// 场景 1:慢速来回(每次等导航完成)—— 期望恰好 2 次翻转
|
||||
await router.push('/base/contact')
|
||||
await flushPromises()
|
||||
await router.push('/base/otherdata')
|
||||
await flushPromises()
|
||||
|
||||
// 场景 2:快速来回(不等前一个导航完成)—— 模拟用户快速连点
|
||||
router.push('/base/contact')
|
||||
router.push('/base/otherdata')
|
||||
router.push('/base/contact')
|
||||
await flushPromises()
|
||||
|
||||
mo.disconnect()
|
||||
console.log('[诊断] is-active 翻转序列:', flips.join(' -> '))
|
||||
|
||||
// 最终态:最后 push 的是联系人
|
||||
const final = read()
|
||||
expect(final.contact).toBe(1)
|
||||
expect(final.other).toBe(0)
|
||||
// 场景 1 恰好 2 次翻转;场景 2 的快速连点可能合并为至多 3 次 → 上限 5。
|
||||
// 若状态层存在「旧项再亮一次」(类被加回),翻转次数会超过此上限并在这里变红
|
||||
expect(flips.length).toBeLessThanOrEqual(5)
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user