feat: roles SPA foundations
This commit is contained in:
1 parent
80f7037d3a
commit
907949aa12
180 files changed
+4308
-835
No files matched your search
@@ -9,6 +9,8 @@ import DashboardShell from '../DashboardShell.vue'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { DESKTOP_WIDTH, MOBILE_WIDTH, setViewportWidth } from './viewport'
|
||||
import type { Access } from '@/lib/permissions'
|
||||
import { accessFor, customAccess, writerAccess } from '@/test/access'
|
||||
|
||||
vi.mock('@/composables/useApi', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/composables/useApi')>()
|
||||
@@ -23,6 +25,7 @@ interface ShellOptions {
|
||||
displayName?: string
|
||||
email?: string
|
||||
role?: string
|
||||
access?: Access
|
||||
width?: number
|
||||
}
|
||||
|
||||
@@ -33,7 +36,8 @@ async function mountShell(path: string, options: ShellOptions = {}) {
|
||||
username = 'marta',
|
||||
displayName = '',
|
||||
email = 'marta@renew.example',
|
||||
role = undefined,
|
||||
role = 'noschema',
|
||||
access,
|
||||
width = DESKTOP_WIDTH,
|
||||
} = options
|
||||
localStorage.clear()
|
||||
@@ -45,7 +49,7 @@ async function mountShell(path: string, options: ShellOptions = {}) {
|
||||
username,
|
||||
email,
|
||||
displayName,
|
||||
role,
|
||||
access: access ?? accessFor(role),
|
||||
}),
|
||||
request: vi.fn().mockResolvedValue(undefined),
|
||||
})
|
||||
@@ -106,6 +110,10 @@ describe('DashboardShell renders the eight navigation destinations', () => {
|
||||
|
||||
const links = Array.from(
|
||||
document.body.querySelectorAll('[role="dialog"] nav[aria-label="Main"] a'),
|
||||
).filter(
|
||||
(link) =>
|
||||
link.getAttribute('href') === '/settings' ||
|
||||
!link.getAttribute('href')?.startsWith('/settings/'),
|
||||
)
|
||||
expect(links.map((link) => link.getAttribute('href'))).toEqual([
|
||||
'/home',
|
||||
@@ -784,3 +792,87 @@ describe('DashboardShell mobile drawer rows', () => {
|
||||
expectUnfilled(navLink(drawer, '/settings/media/cache'))
|
||||
})
|
||||
})
|
||||
|
||||
function navHrefs(wrapper: VueWrapper) {
|
||||
return wrapper.findAll('nav[aria-label="Main"] a').map((link) => link.attributes('href'))
|
||||
}
|
||||
|
||||
describe('DashboardShell lists items per permission', () => {
|
||||
it('shows Home, Articles, Media, Authors, Activity and Integration for a role with those grants only', async () => {
|
||||
const wrapper = await mountShell('/home', {
|
||||
access: customAccess(
|
||||
'content.read, media.view, authors.view, activity.view=own',
|
||||
'Contributor',
|
||||
),
|
||||
})
|
||||
|
||||
expect(navHrefs(wrapper)).toEqual([
|
||||
'/home',
|
||||
'/article',
|
||||
'/media',
|
||||
'/authors',
|
||||
'/activity',
|
||||
'/integration',
|
||||
])
|
||||
expect(wrapper.find('nav[aria-label="Main"]').text()).not.toContain('Roles')
|
||||
})
|
||||
|
||||
it('shows the same items in the mobile drawer', async () => {
|
||||
await mountShell('/home', {
|
||||
width: MOBILE_WIDTH,
|
||||
access: customAccess('content.read, media.view, authors.view, activity.view=own'),
|
||||
})
|
||||
|
||||
const toggle = document.body.querySelector('[data-slot="toggle"]') as HTMLButtonElement
|
||||
toggle.click()
|
||||
await flushPromises()
|
||||
|
||||
const links = Array.from(
|
||||
document.body.querySelectorAll('[role="dialog"] nav[aria-label="Main"] a'),
|
||||
)
|
||||
expect(links.map((link) => link.getAttribute('href'))).toEqual([
|
||||
'/home',
|
||||
'/article',
|
||||
'/media',
|
||||
'/authors',
|
||||
'/activity',
|
||||
'/integration',
|
||||
])
|
||||
})
|
||||
|
||||
it('shows the Writer built-in menu with Schema and no Users or Settings', async () => {
|
||||
const wrapper = await mountShell('/home', { access: writerAccess() })
|
||||
|
||||
expect(navHrefs(wrapper)).toEqual([
|
||||
'/home',
|
||||
'/article',
|
||||
'/media',
|
||||
'/authors',
|
||||
'/schema',
|
||||
'/activity',
|
||||
'/integration',
|
||||
])
|
||||
})
|
||||
|
||||
it('shows the Sites child under Settings with settings.manage_sites', async () => {
|
||||
const wrapper = await mountShell('/settings', {
|
||||
access: customAccess('settings.view, settings.manage_sites'),
|
||||
})
|
||||
|
||||
expect(wrapper.find('a[href="/settings/sites"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('hides the Sites child for the built-in Admin without settings.manage_sites', async () => {
|
||||
const wrapper = await mountShell('/settings', {
|
||||
access: customAccess('settings.view'),
|
||||
})
|
||||
|
||||
expect(wrapper.find('a[href="/settings/sites"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('lists Sites for the built-in Admin', async () => {
|
||||
const wrapper = await mountShell('/settings', { role: 'admin' })
|
||||
|
||||
expect(wrapper.find('a[href="/settings/sites"]').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user