201 lines
7.2 KiB
TypeScript
201 lines
7.2 KiB
TypeScript
import { beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest'
|
|||
|
|
import { createPinia, setActivePinia } from 'pinia'
|
||
|
|
import { nextTick } from 'vue'
|
||
|
|
import { getNavigationLeaves } from '@/navigation/adminNavigation'
|
||
|
|
import { useAdminDataStore } from '@/stores/adminData'
|
||
|
|
import { PREVIEW_ROLE_KEY, useAccessPreviewStore } from '@/stores/accessPreview'
|
||
|
|
|
||
|
|
function createMemoryStorage(): Storage {
|
||
|
|
const values = new Map<string, string>()
|
||
|
|
|
||
|
|
return {
|
||
|
|
get length() {
|
||
|
|
return values.size
|
||
|
|
},
|
||
|
|
clear() {
|
||
|
|
values.clear()
|
||
|
|
},
|
||
|
|
getItem(key) {
|
||
|
|
return values.get(key) ?? null
|
||
|
|
},
|
||
|
|
key(index) {
|
||
|
|
return [...values.keys()][index] ?? null
|
||
|
|
},
|
||
|
|
removeItem(key) {
|
||
|
|
values.delete(key)
|
||
|
|
},
|
||
|
|
setItem(key, value) {
|
||
|
|
values.set(key, value)
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('access preview store', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
setActivePinia(createPinia())
|
||
|
|
vi.stubGlobal('localStorage', createMemoryStorage())
|
||
|
|
})
|
||
|
|
|
||
|
|
it('defaults to the enabled super admin with all enabled permissions', () => {
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
|
||
|
|
expect(preview.enabledRoles).toHaveLength(5)
|
||
|
|
expect(preview.currentRole?.code).toBe('SUPER_ADMIN')
|
||
|
|
expect(preview.can('system:permission:write')).toBe(true)
|
||
|
|
expect(preview.firstAccessiblePath).toBe('/customers')
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBe('R001')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('ignores disabled permission resources even when the selected role still holds their ids', () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
const customerList = data.permissions.find((item) => item.code === 'platform:customer:list')
|
||
|
|
expect(customerList).toBeDefined()
|
||
|
|
|
||
|
|
if (customerList) customerList.status = '停用'
|
||
|
|
|
||
|
|
expect(preview.can('platform:customer:list')).toBe(false)
|
||
|
|
expect(getNavigationLeaves(preview.filteredNavigation).map((item) => item.path)).not.toContain('/customers')
|
||
|
|
expect(preview.firstAccessiblePath).toBe('/customers/reviews')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('does not cascade a disabled directory to enabled descendant leaves', () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
const businessDirectory = data.permissions.find((item) => item.code === 'business')
|
||
|
|
expect(businessDirectory).toBeDefined()
|
||
|
|
|
||
|
|
if (businessDirectory) businessDirectory.status = '停用'
|
||
|
|
|
||
|
|
expect(preview.can('business')).toBe(false)
|
||
|
|
expect(preview.can('platform:customer:list')).toBe(true)
|
||
|
|
expect(getNavigationLeaves(preview.filteredNavigation).map((item) => item.path)).toContain('/customers')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('reacts immediately to permission id changes and falls back when the current role is disabled', async () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
expect(preview.commitRole('R003')).toBe(true)
|
||
|
|
const operator = data.roles.find((item) => item.id === 'R003')
|
||
|
|
expect(operator).toBeDefined()
|
||
|
|
|
||
|
|
if (operator) operator.permissionIds = ['P100']
|
||
|
|
expect(preview.can('first-leg:list')).toBe(false)
|
||
|
|
expect(preview.can('platform:customer:list')).toBe(true)
|
||
|
|
expect(preview.permissionSignature).toBe('platform:customer:list')
|
||
|
|
|
||
|
|
if (operator) operator.status = '停用'
|
||
|
|
await nextTick()
|
||
|
|
|
||
|
|
expect(preview.currentRole?.code).toBe('SUPER_ADMIN')
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBe('R001')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('falls back from an invalid persisted role and reset clears selection and persistence', () => {
|
||
|
|
localStorage.setItem(PREVIEW_ROLE_KEY, 'missing-role')
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
expect(preview.currentRole?.code).toBe('SUPER_ADMIN')
|
||
|
|
|
||
|
|
preview.reset()
|
||
|
|
expect(preview.initialized).toBe(false)
|
||
|
|
expect(preview.currentRole).toBeNull()
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBeNull()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('restores an enabled persisted role', () => {
|
||
|
|
localStorage.setItem(PREVIEW_ROLE_KEY, 'R003')
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
|
||
|
|
expect(preview.currentRole?.id).toBe('R003')
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBe('R003')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('does not reload an initialized role during guarded navigation', () => {
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
expect(preview.commitRole('R003')).toBe(true)
|
||
|
|
|
||
|
|
localStorage.setItem(PREVIEW_ROLE_KEY, 'R001')
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
|
||
|
|
expect(preview.currentRole?.id).toBe('R003')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('has no role, permissions, or route when every role is disabled', () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
data.roles.forEach((role) => {
|
||
|
|
role.status = '停用'
|
||
|
|
})
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
|
||
|
|
expect(preview.currentRole).toBeNull()
|
||
|
|
expect([...preview.permissionCodes]).toEqual([])
|
||
|
|
expect(preview.filteredNavigation).toEqual([])
|
||
|
|
expect(preview.firstAccessiblePath).toBeNull()
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBeNull()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('does not recreate a default preview after reset when role data changes', async () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
preview.reset()
|
||
|
|
|
||
|
|
data.roles[0].status = '停用'
|
||
|
|
data.roles[1].status = '停用'
|
||
|
|
await nextTick()
|
||
|
|
|
||
|
|
expect(preview.initialized).toBe(false)
|
||
|
|
expect(preview.selectedRoleId).toBeNull()
|
||
|
|
expect(preview.currentRole).toBeNull()
|
||
|
|
expect(localStorage.getItem(PREVIEW_ROLE_KEY)).toBeNull()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('previews candidates without committing and rejects disabled roles', () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
const candidate = preview.previewRole('R003')
|
||
|
|
|
||
|
|
expect(candidate.codes.has('first-leg:create')).toBe(true)
|
||
|
|
expect(candidate.firstPath).toBe('/warehouses')
|
||
|
|
expect(getNavigationLeaves(candidate.navigation).map((item) => item.path)).toContain('/orders/first-leg')
|
||
|
|
expectTypeOf(candidate.codes).toEqualTypeOf<ReadonlySet<string>>()
|
||
|
|
expectTypeOf(preview.permissionCodes).toEqualTypeOf<ReadonlySet<string>>()
|
||
|
|
expect(preview.currentRole?.id).toBe('R001')
|
||
|
|
|
||
|
|
const finance = data.roles.find((item) => item.id === 'R004')
|
||
|
|
if (finance) finance.status = '停用'
|
||
|
|
expect(preview.commitRole('R004')).toBe(false)
|
||
|
|
expect(preview.previewRole('R004').firstPath).toBeNull()
|
||
|
|
expect(preview.currentRole?.id).toBe('R001')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('does not filter or mutate mock business data when switching data scopes', () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
const preview = useAccessPreviewStore()
|
||
|
|
preview.initializeAfterLogin()
|
||
|
|
const customerSnapshot = JSON.parse(JSON.stringify(data.customers)) as typeof data.customers
|
||
|
|
const orderSnapshot = JSON.parse(JSON.stringify(data.orders)) as typeof data.orders
|
||
|
|
|
||
|
|
expect(preview.commitRole('R003')).toBe(true)
|
||
|
|
expect(preview.currentRole?.dataScope).toBe('本部门数据')
|
||
|
|
expect(preview.commitRole('R005')).toBe(true)
|
||
|
|
expect(preview.currentRole?.dataScope).toBe('本人数据')
|
||
|
|
|
||
|
|
expect(data.customers).toEqual(customerSnapshot)
|
||
|
|
expect(data.orders).toEqual(orderSnapshot)
|
||
|
|
})
|
||
|
|
})
|