Barazo default frontend barazo.forum
at main 153 lines 5.1 kB view raw
1/** 2 * Tests for AgeGateDialog component. 3 */ 4 5import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' 6import { render, screen, waitFor } from '@testing-library/react' 7import userEvent from '@testing-library/user-event' 8import { AgeGateDialog } from './age-gate-dialog' 9 10const mockGetAccessToken = vi.fn<() => string | null>(() => 'mock-access-token') 11 12vi.mock('@/hooks/use-auth', () => ({ 13 useAuth: () => ({ 14 user: { 15 did: 'did:plc:user-jay-001', 16 handle: 'jay.bsky.team', 17 displayName: 'Jay', 18 avatarUrl: null, 19 }, 20 isAuthenticated: true, 21 isLoading: false, 22 getAccessToken: mockGetAccessToken, 23 login: vi.fn(), 24 logout: vi.fn(), 25 setSessionFromCallback: vi.fn(), 26 authFetch: vi.fn(), 27 }), 28})) 29 30// Mock localStorage 31const mockStorage: Record<string, string> = {} 32 33beforeEach(() => { 34 vi.stubGlobal('localStorage', { 35 getItem: vi.fn((key: string) => mockStorage[key] ?? null), 36 setItem: vi.fn((key: string, value: string) => { 37 mockStorage[key] = value 38 }), 39 removeItem: vi.fn((key: string) => { 40 delete mockStorage[key] 41 }), 42 clear: vi.fn(), 43 length: 0, 44 key: vi.fn(), 45 }) 46 mockStorage['accessToken'] = 'test-token' 47}) 48 49afterEach(() => { 50 vi.restoreAllMocks() 51 Object.keys(mockStorage).forEach((key) => delete mockStorage[key]) 52}) 53 54describe('AgeGateDialog', () => { 55 it('renders nothing when not open', () => { 56 const { container } = render( 57 <AgeGateDialog open={false} onConfirm={vi.fn()} onCancel={vi.fn()} /> 58 ) 59 expect(container.innerHTML).toBe('') 60 }) 61 62 it('renders dialog with dropdown when open', () => { 63 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 64 expect(screen.getByText('Age Declaration')).toBeInTheDocument() 65 expect(screen.getByLabelText('Your age bracket')).toBeInTheDocument() 66 expect(screen.getByText('Cancel')).toBeInTheDocument() 67 expect(screen.getByText('Confirm')).toBeInTheDocument() 68 }) 69 70 it('has correct ARIA attributes', () => { 71 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 72 const dialog = screen.getByRole('dialog') 73 expect(dialog).toHaveAttribute('aria-modal', 'true') 74 expect(dialog).toHaveAttribute('aria-labelledby', 'age-gate-title') 75 }) 76 77 it('calls onCancel when Cancel is clicked', async () => { 78 const onCancel = vi.fn() 79 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={onCancel} />) 80 81 const user = userEvent.setup() 82 await user.click(screen.getByText('Cancel')) 83 84 expect(onCancel).toHaveBeenCalledOnce() 85 }) 86 87 it('shows all age bracket options in dropdown', () => { 88 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 89 const select = screen.getByLabelText('Your age bracket') as HTMLSelectElement 90 const options = Array.from(select.options).map((o) => o.text) 91 92 expect(options).toContain('Select age bracket...') 93 expect(options).toContain('Rather not say') 94 expect(options).toContain('13+') 95 expect(options).toContain('14+') 96 expect(options).toContain('15+') 97 expect(options).toContain('16+') 98 expect(options).toContain('18+') 99 }) 100 101 it('disables Confirm button when no age selected', () => { 102 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 103 const confirmBtn = screen.getByText('Confirm') 104 expect(confirmBtn).toBeDisabled() 105 }) 106 107 it('enables Confirm button when age is selected', async () => { 108 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 109 110 const user = userEvent.setup() 111 await user.selectOptions(screen.getByLabelText('Your age bracket'), '16') 112 113 expect(screen.getByText('Confirm')).not.toBeDisabled() 114 }) 115 116 it('calls onConfirm with declaredAge when confirmed', async () => { 117 const onConfirm = vi.fn() 118 render(<AgeGateDialog open={true} onConfirm={onConfirm} onCancel={vi.fn()} />) 119 120 const user = userEvent.setup() 121 await user.selectOptions(screen.getByLabelText('Your age bracket'), '16') 122 await user.click(screen.getByText('Confirm')) 123 124 await waitFor(() => { 125 expect(onConfirm).toHaveBeenCalledOnce() 126 }) 127 expect(onConfirm).toHaveBeenCalledWith(16) 128 }) 129 130 it('shows "Rather not say" explanation when 0 is selected', async () => { 131 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 132 133 const user = userEvent.setup() 134 await user.selectOptions(screen.getByLabelText('Your age bracket'), '0') 135 136 expect( 137 screen.getByText(/Rather not say.*means you will only see Safe content/) 138 ).toBeInTheDocument() 139 }) 140 141 it('shows error when not authenticated', async () => { 142 mockGetAccessToken.mockReturnValue(null) 143 render(<AgeGateDialog open={true} onConfirm={vi.fn()} onCancel={vi.fn()} />) 144 145 const user = userEvent.setup() 146 await user.selectOptions(screen.getByLabelText('Your age bracket'), '16') 147 await user.click(screen.getByText('Confirm')) 148 149 await waitFor(() => { 150 expect(screen.getByRole('alert')).toHaveTextContent('Not authenticated') 151 }) 152 }) 153})