feat: roles SPA foundations
This commit is contained in:
1 parent
80f7037d3a
commit
907949aa12
180 files changed
+4308
-835
No files matched your search
@@ -7,6 +7,7 @@ import { ApiError, NetworkError, useApi } from '@/composables/useApi'
|
||||
import type { CreatedInvite, UserListItem } from '@/composables/useUsers'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import UsersView from '../UsersView.vue'
|
||||
import { accessFor, roleRefFor } from '@/test/access'
|
||||
|
||||
vi.mock('@/composables/useApi', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/composables/useApi')>()
|
||||
@@ -38,6 +39,7 @@ function defaultFixture(): UserListItem[] {
|
||||
kind: 'invite',
|
||||
id: 2,
|
||||
email: 'sam.oliveira@renew.example',
|
||||
role: roleRefFor('admin'),
|
||||
status: 'pending',
|
||||
created_at: '2026-09-27T09:00:00Z',
|
||||
},
|
||||
@@ -46,6 +48,7 @@ function defaultFixture(): UserListItem[] {
|
||||
id: 1,
|
||||
email: 'p.huber@gmail.com',
|
||||
display_name: 'Paul Huber',
|
||||
role: roleRefFor('admin'),
|
||||
status: 'expired',
|
||||
created_at: '2026-08-03T09:00:00Z',
|
||||
},
|
||||
@@ -54,7 +57,7 @@ function defaultFixture(): UserListItem[] {
|
||||
id: 3,
|
||||
username: 'ines',
|
||||
email: 'ines.duarte@renew.example',
|
||||
role: 'admin',
|
||||
role: roleRefFor('admin'),
|
||||
display_name: 'Inès Duarte',
|
||||
status: 'accepted',
|
||||
created_at: '2026-06-18T09:00:00Z',
|
||||
@@ -64,7 +67,7 @@ function defaultFixture(): UserListItem[] {
|
||||
id: 2,
|
||||
username: 'jonas',
|
||||
email: 'jonas@renew.example',
|
||||
role: 'admin',
|
||||
role: roleRefFor('admin'),
|
||||
display_name: '',
|
||||
status: 'accepted',
|
||||
created_at: '2026-04-02T09:00:00Z',
|
||||
@@ -74,7 +77,7 @@ function defaultFixture(): UserListItem[] {
|
||||
id: 1,
|
||||
username: 'marta',
|
||||
email: 'marta@renew.example',
|
||||
role: 'owner',
|
||||
role: roleRefFor('owner'),
|
||||
display_name: '',
|
||||
status: 'accepted',
|
||||
created_at: '2026-03-12T09:00:00Z',
|
||||
@@ -91,6 +94,7 @@ function inviteFixture(email = VALID_EMAIL): CreatedInvite {
|
||||
id: 3,
|
||||
kind: 'invite',
|
||||
email,
|
||||
role: roleRefFor('admin'),
|
||||
status: 'pending',
|
||||
created_at: new Date().toISOString(),
|
||||
expires_at: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
@@ -139,7 +143,7 @@ async function mountPage(
|
||||
username: 'marta',
|
||||
id: 1,
|
||||
displayName: '',
|
||||
role: role ?? undefined,
|
||||
access: accessFor(role ?? undefined),
|
||||
}
|
||||
session.status = 'ready'
|
||||
requestMock = vi.fn((path: string) => {
|
||||
@@ -156,7 +160,7 @@ async function mountPage(
|
||||
verified: true,
|
||||
username: 'marta',
|
||||
id: 1,
|
||||
role: role ?? undefined,
|
||||
access: accessFor(role ?? undefined),
|
||||
}),
|
||||
request: requestMock,
|
||||
})
|
||||
@@ -368,7 +372,7 @@ describe('Display names in the Users list', () => {
|
||||
username: 'marta',
|
||||
id: 1,
|
||||
displayName: 'Marta Alves',
|
||||
role: 'owner',
|
||||
access: accessFor('owner'),
|
||||
}
|
||||
await flushPromises()
|
||||
|
||||
@@ -436,10 +440,10 @@ describe('Only you — nobody has been invited yet', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Non-owners cannot invite', () => {
|
||||
describe('Roles without users.invite cannot invite', () => {
|
||||
it.each([
|
||||
{ name: 'admin on desktop', role: 'admin', desktop: true },
|
||||
{ name: 'admin below md', role: 'admin', desktop: false },
|
||||
{ name: 'a writer on desktop', role: 'member', desktop: true },
|
||||
{ name: 'a writer below md', role: 'member', desktop: false },
|
||||
{ name: 'a missing role on desktop', role: null, desktop: true },
|
||||
{ name: 'an unknown role below md', role: 'guest', desktop: false },
|
||||
])('renders the list but no Invite user for $name', async ({ role, desktop }) => {
|
||||
@@ -449,8 +453,8 @@ describe('Non-owners cannot invite', () => {
|
||||
expect(rows()).toHaveLength(5)
|
||||
})
|
||||
|
||||
it('hides the Only-you hint for an admin', async () => {
|
||||
await mountPage({ list: () => Promise.resolve(onlyYouFixture()) }, true, 'admin')
|
||||
it('hides the Only-you hint for a writer', async () => {
|
||||
await mountPage({ list: () => Promise.resolve(onlyYouFixture()) }, true, 'member')
|
||||
|
||||
expect(rows()).toHaveLength(1)
|
||||
expect(document.body.textContent).not.toContain('Invite someone so they can sign in')
|
||||
@@ -458,12 +462,12 @@ describe('Non-owners cannot invite', () => {
|
||||
})
|
||||
|
||||
it.each([true, false])(
|
||||
'shows no hint for an admin on marta and jonas alone (desktop %s)',
|
||||
'shows no hint for a writer on marta and jonas alone (desktop %s)',
|
||||
async (desktop) => {
|
||||
await mountPage(
|
||||
{ list: () => Promise.resolve(defaultFixture().slice(3)) },
|
||||
desktop,
|
||||
'admin',
|
||||
'member',
|
||||
)
|
||||
|
||||
expect(rows()).toHaveLength(2)
|
||||
|
||||
Reference in new issue
Block a user