Barazo default frontend
barazo.forum
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})