Barazo default frontend
barazo.forum
1/**
2 * Tests for TopicForm component.
3 */
4
5import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest'
6import { render, screen } from '@testing-library/react'
7import userEvent from '@testing-library/user-event'
8import { axe } from 'vitest-axe'
9import { setupServer } from 'msw/node'
10import { handlers } from '@/mocks/handlers'
11import { TopicForm } from './topic-form'
12
13vi.mock('@/hooks/use-auth', () => ({
14 useAuth: () => ({
15 crossPostScopesGranted: true,
16 requestCrossPostAuth: vi.fn(),
17 }),
18}))
19
20const server = setupServer(...handlers)
21
22beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
23afterEach(() => server.resetHandlers())
24afterAll(() => server.close())
25
26describe('TopicForm', () => {
27 it('renders title input', () => {
28 render(<TopicForm onSubmit={vi.fn()} />)
29 expect(screen.getByRole('textbox', { name: 'Title' })).toBeInTheDocument()
30 })
31
32 it('renders category select', () => {
33 render(<TopicForm onSubmit={vi.fn()} />)
34 expect(screen.getByRole('combobox', { name: 'Category' })).toBeInTheDocument()
35 })
36
37 it('renders tag input', () => {
38 render(<TopicForm onSubmit={vi.fn()} />)
39 expect(screen.getByRole('textbox', { name: /Tags/ })).toBeInTheDocument()
40 })
41
42 it('renders content editor', () => {
43 render(<TopicForm onSubmit={vi.fn()} />)
44 expect(screen.getByRole('textbox', { name: 'Content' })).toBeInTheDocument()
45 })
46
47 it('renders cross-post checkboxes', () => {
48 render(<TopicForm onSubmit={vi.fn()} />)
49 expect(screen.getByLabelText('Share on Bluesky')).toBeInTheDocument()
50 expect(screen.getByLabelText('Share on Frontpage')).toBeInTheDocument()
51 })
52
53 it('defaults Bluesky cross-post to checked', () => {
54 render(<TopicForm onSubmit={vi.fn()} />)
55 expect(screen.getByLabelText('Share on Bluesky')).toBeChecked()
56 })
57
58 it('defaults Frontpage cross-post to unchecked', () => {
59 render(<TopicForm onSubmit={vi.fn()} />)
60 expect(screen.getByLabelText('Share on Frontpage')).not.toBeChecked()
61 })
62
63 it('renders submit button', () => {
64 render(<TopicForm onSubmit={vi.fn()} />)
65 expect(screen.getByRole('button', { name: 'Create Topic' })).toBeInTheDocument()
66 })
67
68 it('shows edit mode submit button text', () => {
69 render(
70 <TopicForm
71 onSubmit={vi.fn()}
72 initialValues={{
73 title: 'Test',
74 content: 'Content',
75 category: 'general',
76 }}
77 mode="edit"
78 />
79 )
80 expect(screen.getByRole('button', { name: 'Save Changes' })).toBeInTheDocument()
81 })
82
83 it('validates required title', async () => {
84 const user = userEvent.setup()
85 const onSubmit = vi.fn()
86 render(<TopicForm onSubmit={onSubmit} />)
87
88 await user.click(screen.getByRole('button', { name: 'Create Topic' }))
89 expect(screen.getByText('Title is required')).toBeInTheDocument()
90 expect(onSubmit).not.toHaveBeenCalled()
91 })
92
93 it('validates required content', async () => {
94 const user = userEvent.setup()
95 const onSubmit = vi.fn()
96 render(<TopicForm onSubmit={onSubmit} />)
97
98 await user.type(screen.getByRole('textbox', { name: 'Title' }), 'Test Title')
99 await user.click(screen.getByRole('button', { name: 'Create Topic' }))
100 expect(screen.getByText('Content is required')).toBeInTheDocument()
101 expect(onSubmit).not.toHaveBeenCalled()
102 })
103
104 it('validates required category', async () => {
105 const user = userEvent.setup()
106 const onSubmit = vi.fn()
107 render(<TopicForm onSubmit={onSubmit} />)
108
109 await user.type(screen.getByRole('textbox', { name: 'Title' }), 'Test Title')
110 await user.type(screen.getByRole('textbox', { name: 'Content' }), 'Test content')
111 await user.click(screen.getByRole('button', { name: 'Create Topic' }))
112 expect(screen.getByText('Category is required')).toBeInTheDocument()
113 expect(onSubmit).not.toHaveBeenCalled()
114 })
115
116 it('validates title length', async () => {
117 const user = userEvent.setup()
118 const onSubmit = vi.fn()
119 render(<TopicForm onSubmit={onSubmit} />)
120
121 await user.type(screen.getByRole('textbox', { name: 'Title' }), 'AB')
122 await user.click(screen.getByRole('button', { name: 'Create Topic' }))
123 expect(screen.getByText('Title must be at least 3 characters')).toBeInTheDocument()
124 expect(onSubmit).not.toHaveBeenCalled()
125 })
126
127 it('renders preview tab', async () => {
128 const user = userEvent.setup()
129 render(<TopicForm onSubmit={vi.fn()} />)
130
131 const previewTab = screen.getByRole('tab', { name: 'Preview' })
132 expect(previewTab).toBeInTheDocument()
133
134 await user.click(previewTab)
135 expect(screen.getByText('Nothing to preview')).toBeInTheDocument()
136 })
137
138 it('pre-populates form in edit mode', () => {
139 render(
140 <TopicForm
141 onSubmit={vi.fn()}
142 initialValues={{
143 title: 'Existing Topic',
144 content: 'Existing content',
145 category: 'general',
146 tags: ['test', 'edit'],
147 }}
148 mode="edit"
149 />
150 )
151 expect(screen.getByRole('textbox', { name: 'Title' })).toHaveValue('Existing Topic')
152 expect(screen.getByRole('textbox', { name: 'Content' })).toHaveValue('Existing content')
153 })
154
155 it('passes axe accessibility check', async () => {
156 const { container } = render(<TopicForm onSubmit={vi.fn()} />)
157 const results = await axe(container)
158 expect(results).toHaveNoViolations()
159 })
160})