Barazo default frontend barazo.forum
at main 160 lines 5.4 kB view raw
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})