feat: roles SPA foundations
This commit is contained in:
1 parent
80f7037d3a
commit
907949aa12
180 files changed
+4308
-835
No files matched your search
@@ -3,6 +3,7 @@ import { flushPromises } from '@vue/test-utils'
|
||||
import router from '@/router'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { DESKTOP_WIDTH, MOBILE_WIDTH } from '@/components/__tests__/viewport'
|
||||
import { customAccess } from '@/test/access'
|
||||
import {
|
||||
callsTo,
|
||||
chooseMenuItem,
|
||||
@@ -239,33 +240,28 @@ describe('Sites: set as default', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('closes and shows the owner-only toast on a 403', async () => {
|
||||
it('closes and leaves the generic missing-permission toast to the API client on a 403', async () => {
|
||||
const backend = createBackend()
|
||||
backend.responders['PATCH /v1/site/2/default'] = failWith(
|
||||
403,
|
||||
'Only the owner can do this.',
|
||||
)
|
||||
backend.responders['PATCH /v1/site/2/default'] = failWith(403, 'missing_permission')
|
||||
await openDialog(backend)
|
||||
await click('Make default', dialog()!)
|
||||
|
||||
expect(dialog()).toBeNull()
|
||||
expect(document.body.textContent).toContain('Only the owner can manage sites')
|
||||
expect(document.body.textContent).toContain('Ask the owner of this site.')
|
||||
expect(document.body.textContent).not.toContain('Only the owner can manage sites')
|
||||
})
|
||||
})
|
||||
|
||||
describe.each([
|
||||
['admin', '/settings/sites'],
|
||||
['member', '/settings/sites'],
|
||||
['admin', '/settings/sites?edit=journal'],
|
||||
['member', '/settings/sites?edit=journal'],
|
||||
])('Sites: forbidden (403 page) for %s on %s', (role, path) => {
|
||||
it('explains instead of redirecting, opens no panel and Back to Settings goes to /settings', async () => {
|
||||
it('explains instead of redirecting, opens no panel and offers Go to Articles', async () => {
|
||||
const backend = await mountDirectory('sites', path, { role })
|
||||
|
||||
const text = document.body.textContent ?? ''
|
||||
expect(text).toContain("You don't have access to Sites")
|
||||
expect(text).toContain("You don't have access to this page")
|
||||
expect(text).toContain(
|
||||
'Only the owner can manage sites. Ask the owner of this site if you need a change.',
|
||||
"Your role, Writer, doesn't include it. Ask an owner or admin if you need it.",
|
||||
)
|
||||
expect(router.currentRoute.value.fullPath).toBe(path)
|
||||
expect(dialog()).toBeNull()
|
||||
@@ -275,9 +271,9 @@ describe.each([
|
||||
expect(callsTo(backend, 'GET', '/v1/site/')).toHaveLength(0)
|
||||
|
||||
const push = vi.spyOn(router, 'push').mockResolvedValue(undefined)
|
||||
await click('Back to Settings')
|
||||
await click('Go to Articles')
|
||||
|
||||
expect(push).toHaveBeenCalledWith('/settings')
|
||||
expect(push).toHaveBeenCalledWith('/article')
|
||||
push.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -288,7 +284,7 @@ describe('Sites: the API answers 403 where the SPA expected the owner', () => {
|
||||
backend.responders['GET /v1/site/'] = failWith(403, 'Only the owner can do this.')
|
||||
await mountDirectory('sites', '/settings/sites', { backend })
|
||||
|
||||
expect(document.body.textContent).toContain("You don't have access to Sites")
|
||||
expect(document.body.textContent).toContain("You don't have access to this page")
|
||||
expect(rows()).toHaveLength(0)
|
||||
})
|
||||
|
||||
@@ -300,8 +296,7 @@ describe('Sites: the API answers 403 where the SPA expected the owner', () => {
|
||||
await chooseMenuItem('Delete…')
|
||||
await click('Delete site', dialog()!)
|
||||
|
||||
expect(document.body.textContent).toContain('Only the owner can manage sites')
|
||||
expect(document.body.textContent).toContain('Ask the owner of this site.')
|
||||
expect(document.body.textContent).not.toContain('Only the owner can manage sites')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -316,13 +311,45 @@ describe('Sites: the sidebar for the owner and for an admin', () => {
|
||||
expect(link.getAttribute('aria-current')).toBe('page')
|
||||
})
|
||||
|
||||
it('has no Publishing group and no Sites entry for an admin', async () => {
|
||||
it('shows the Publishing group and the Sites entry for an admin', async () => {
|
||||
await mountDirectory('sites', '/settings/sites', { role: 'admin' })
|
||||
|
||||
const nav = document.body.querySelector('nav[aria-label="Main"]')!
|
||||
expect(textOf(nav)).toContain('Publishing')
|
||||
expect(nav.querySelector('a[href="/settings/sites"]')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('has no Publishing group and no Sites entry for a writer', async () => {
|
||||
await mountDirectory('sites', '/settings/sites', { role: 'member' })
|
||||
|
||||
const nav = document.body.querySelector('nav[aria-label="Main"]')!
|
||||
expect(textOf(nav)).not.toContain('Publishing')
|
||||
expect(nav.querySelector('a[href="/settings/sites"]')).toBeNull()
|
||||
})
|
||||
|
||||
it('disables Delete site with the Owner only tag for an admin', async () => {
|
||||
await mountDirectory('sites', '/settings/sites', { role: 'admin' })
|
||||
await openRowMenu('Archive')
|
||||
|
||||
const item = Array.from(document.body.querySelectorAll('[role="menuitem"]')).find(
|
||||
(candidate) => candidate.textContent?.includes('Delete…'),
|
||||
)!
|
||||
expect(item.getAttribute('aria-disabled')).toBe('true')
|
||||
expect(item.textContent).toContain('Owner only')
|
||||
})
|
||||
|
||||
it('enables Delete site with settings.delete_site', async () => {
|
||||
await mountDirectory('sites', '/settings/sites', {
|
||||
access: customAccess('settings.view, settings.manage_sites, settings.delete_site'),
|
||||
})
|
||||
await openRowMenu('Archive')
|
||||
|
||||
const item = Array.from(document.body.querySelectorAll('[role="menuitem"]')).find(
|
||||
(candidate) => candidate.textContent?.includes('Delete…'),
|
||||
)!
|
||||
expect(item.getAttribute('aria-disabled')).not.toBe('true')
|
||||
expect(item.textContent).not.toContain('Owner only')
|
||||
})
|
||||
})
|
||||
|
||||
void useApi
|
||||
|
||||
Reference in new issue
Block a user