Files
resolutionflow/frontend/src/hooks/usePermissions.test.ts
chihlasm 791fd133c4 test: set up Vitest with 61 frontend tests
Add Vitest + testing-library/react + jsdom for frontend testing.
Tests cover: cn() utility (6), usePermissions hook (27), useTreeValidation
hook (22), and userPreferencesStore (6). CI updated to run frontend tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:18:38 -05:00

193 lines
6.2 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest'
import { renderHook } from '@testing-library/react'
import { usePermissions } from './usePermissions'
import { useAuthStore } from '@/store/authStore'
import type { User } from '@/types'
function makeUser(overrides: Partial<User> = {}): User {
return {
id: 'user-1',
email: 'test@example.com',
name: 'Test User',
role: 'engineer',
is_super_admin: false,
account_id: 'acct-1',
account_role: 'engineer',
created_at: '2026-01-01T00:00:00Z',
last_login: null,
...overrides,
}
}
describe('usePermissions', () => {
beforeEach(() => {
useAuthStore.setState({ user: null, token: null, isAuthenticated: false })
})
describe('when not authenticated', () => {
it('defaults to viewer role', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.effectiveRole).toBe('viewer')
expect(result.current.isViewer).toBe(true)
})
it('cannot create trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canCreateTrees).toBe(false)
})
it('cannot create steps', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canCreateSteps).toBe(false)
})
it('cannot delete trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canDeleteTree()).toBe(false)
})
})
describe('viewer role', () => {
beforeEach(() => {
useAuthStore.setState({ user: makeUser({ role: 'viewer', account_role: 'viewer' }) })
})
it('has viewer effective role', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.effectiveRole).toBe('viewer')
expect(result.current.isViewer).toBe(true)
})
it('cannot create trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canCreateTrees).toBe(false)
})
it('cannot edit any tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'user-1' })).toBe(false)
})
it('cannot manage categories', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageCategories).toBe(false)
})
})
describe('engineer role', () => {
beforeEach(() => {
useAuthStore.setState({ user: makeUser() })
})
it('has engineer effective role', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.effectiveRole).toBe('engineer')
expect(result.current.isEngineer).toBe(true)
})
it('can create trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canCreateTrees).toBe(true)
})
it('can edit own tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'user-1' })).toBe(true)
})
it('cannot edit other user tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'other-user' })).toBe(false)
})
it('cannot delete trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canDeleteTree()).toBe(false)
})
it('can edit own step', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditStep({ created_by: 'user-1' })).toBe(true)
})
it('cannot edit other step', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditStep({ created_by: 'other' })).toBe(false)
})
it('cannot manage categories', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageCategories).toBe(false)
})
})
describe('owner role', () => {
beforeEach(() => {
useAuthStore.setState({ user: makeUser({ account_role: 'owner' }) })
})
it('has owner effective role', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.effectiveRole).toBe('owner')
expect(result.current.isAccountOwner).toBe(true)
})
it('can edit account tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'other', account_id: 'acct-1' })).toBe(true)
})
it('cannot edit other account tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'other', account_id: 'other-acct' })).toBe(false)
})
it('can manage categories', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageCategories).toBe(true)
})
it('cannot manage global categories', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageGlobalCategories).toBe(false)
})
})
describe('super admin', () => {
beforeEach(() => {
useAuthStore.setState({ user: makeUser({ is_super_admin: true }) })
})
it('has super_admin effective role', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.effectiveRole).toBe('super_admin')
expect(result.current.isSuperAdmin).toBe(true)
})
it('can edit any tree', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditTree({ author_id: 'anyone', account_id: 'any' })).toBe(true)
})
it('can delete trees', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canDeleteTree()).toBe(true)
})
it('can edit any step', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canEditStep({ created_by: 'anyone' })).toBe(true)
})
it('can manage global categories', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageGlobalCategories).toBe(true)
})
it('can manage account', () => {
const { result } = renderHook(() => usePermissions())
expect(result.current.canManageAccount).toBe(true)
})
})
})