81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import { createPinia, setActivePinia } from 'pinia'
|
|||
|
|
import { flushPromises, mount } from '@vue/test-utils'
|
||
|
|
import { createMemoryHistory, createRouter } from 'vue-router'
|
||
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||
|
|
import { useAccessPreviewStore } from '@/stores/accessPreview'
|
||
|
|
import { useAdminDataStore } from '@/stores/adminData'
|
||
|
|
import ForbiddenView from './ForbiddenView.vue'
|
||
|
|
|
||
|
|
let pinia: ReturnType<typeof createPinia>
|
||
|
|
|
||
|
|
function createMemoryStorage(): Storage {
|
||
|
|
const values = new Map<string, string>()
|
||
|
|
return {
|
||
|
|
get length() { return values.size },
|
||
|
|
clear: () => values.clear(),
|
||
|
|
getItem: (key) => values.get(key) ?? null,
|
||
|
|
key: (index) => [...values.keys()][index] ?? null,
|
||
|
|
removeItem: (key) => values.delete(key),
|
||
|
|
setItem: (key, value) => values.set(key, value),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function mountForbidden(from: string) {
|
||
|
|
const router = createRouter({
|
||
|
|
history: createMemoryHistory(),
|
||
|
|
routes: [
|
||
|
|
{ path: '/customers', component: { template: '<p>customers</p>' } },
|
||
|
|
{ path: '/403', component: ForbiddenView },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
await router.push({ path: '/403', query: { from } })
|
||
|
|
await router.isReady()
|
||
|
|
|
||
|
|
const wrapper = mount(ForbiddenView, {
|
||
|
|
global: {
|
||
|
|
plugins: [pinia, router],
|
||
|
|
stubs: {
|
||
|
|
'el-button': {
|
||
|
|
template: '<button type="button" @click="$emit(\'click\')"><slot /></button>',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
})
|
||
|
|
return { wrapper, router }
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('ForbiddenView', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.stubGlobal('localStorage', createMemoryStorage())
|
||
|
|
pinia = createPinia()
|
||
|
|
setActivePinia(pinia)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('shows the preview role and replaces the route with the first accessible page', async () => {
|
||
|
|
const access = useAccessPreviewStore()
|
||
|
|
access.initializeAfterLogin()
|
||
|
|
const { wrapper, router } = await mountForbidden('/customers?status=pending')
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('权限预览:超级管理员')
|
||
|
|
expect(wrapper.text()).toContain('来源页面:/customers?status=pending')
|
||
|
|
await wrapper.get('button').trigger('click')
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(router.currentRoute.value.fullPath).toBe('/customers')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('sanitizes an external-looking source and guides role switching when no role is available', async () => {
|
||
|
|
const data = useAdminDataStore()
|
||
|
|
data.roles.forEach((role) => { role.status = '停用' })
|
||
|
|
const access = useAccessPreviewStore()
|
||
|
|
access.initializeAfterLogin()
|
||
|
|
const { wrapper } = await mountForbidden('//example.com')
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('来源页面:/')
|
||
|
|
expect(wrapper.text()).not.toContain('//example.com')
|
||
|
|
expect(wrapper.text()).toContain('当前无可用角色')
|
||
|
|
expect(wrapper.text()).toContain('请使用顶栏切换角色')
|
||
|
|
expect(wrapper.find('button').exists()).toBe(false)
|
||
|
|
})
|
||
|
|
})
|