Barazo default frontend barazo.forum
at main 203 lines 7.0 kB view raw
1/** 2 * Tests for admin moderation page. 3 */ 4 5import { describe, it, expect, vi } from 'vitest' 6import { render, screen, waitFor } from '@testing-library/react' 7import userEvent from '@testing-library/user-event' 8import { axe } from 'vitest-axe' 9import AdminModerationPage from './page' 10 11vi.mock('next/navigation', () => ({ 12 useRouter: () => ({ push: vi.fn() }), 13 usePathname: () => '/admin/moderation', 14})) 15 16vi.mock('next/link', () => ({ 17 default: ({ 18 children, 19 href, 20 ...props 21 }: { children: React.ReactNode; href: string } & Record<string, unknown>) => ( 22 <a href={href} {...props}> 23 {children} 24 </a> 25 ), 26})) 27 28vi.mock('next/image', () => ({ 29 default: (props: Record<string, unknown>) => { 30 // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text 31 return <img {...props} /> 32 }, 33})) 34 35vi.mock('@/hooks/use-auth', () => { 36 const mockAuth = { 37 user: { 38 did: 'did:plc:user-jay-001', 39 handle: 'jay.bsky.team', 40 displayName: 'Jay', 41 avatarUrl: null, 42 }, 43 isAuthenticated: true, 44 isLoading: false, 45 getAccessToken: () => 'mock-access-token', 46 login: vi.fn(), 47 logout: vi.fn(), 48 setSessionFromCallback: vi.fn(), 49 authFetch: vi.fn(), 50 } 51 return { useAuth: () => mockAuth } 52}) 53 54describe('AdminModerationPage', () => { 55 it('renders moderation heading', () => { 56 render(<AdminModerationPage />) 57 expect(screen.getByRole('heading', { name: /moderation/i })).toBeInTheDocument() 58 }) 59 60 it('renders tab navigation', () => { 61 render(<AdminModerationPage />) 62 expect(screen.getByRole('tablist')).toBeInTheDocument() 63 expect(screen.getByRole('tab', { name: /reports/i })).toBeInTheDocument() 64 expect(screen.getByRole('tab', { name: /first post/i })).toBeInTheDocument() 65 expect(screen.getByRole('tab', { name: /action log/i })).toBeInTheDocument() 66 expect(screen.getByRole('tab', { name: /reported users/i })).toBeInTheDocument() 67 expect(screen.getByRole('tab', { name: /thresholds/i })).toBeInTheDocument() 68 }) 69 70 it('shows reports queue by default', async () => { 71 render(<AdminModerationPage />) 72 await waitFor(() => { 73 expect(screen.getAllByText(/misleading/i).length).toBeGreaterThan(0) 74 }) 75 }) 76 77 it('highlights potentially illegal reports', async () => { 78 render(<AdminModerationPage />) 79 await waitFor(() => { 80 expect(screen.getAllByText(/potentially illegal/i).length).toBeGreaterThan(0) 81 }) 82 }) 83 84 it('shows resolve actions on reports', async () => { 85 render(<AdminModerationPage />) 86 await waitFor(() => { 87 expect(screen.getAllByRole('button', { name: /dismiss/i }).length).toBeGreaterThan(0) 88 }) 89 }) 90 91 it('switches to first post queue tab', async () => { 92 const user = userEvent.setup() 93 render(<AdminModerationPage />) 94 const firstPostTab = screen.getByRole('tab', { name: /first post/i }) 95 await user.click(firstPostTab) 96 await waitFor(() => { 97 expect(screen.getByText(/newbie\.bsky\.social/i)).toBeInTheDocument() 98 }) 99 }) 100 101 it('shows account age for first post queue items', async () => { 102 const user = userEvent.setup() 103 render(<AdminModerationPage />) 104 await user.click(screen.getByRole('tab', { name: /first post/i })) 105 await waitFor(() => { 106 expect(screen.getByText(/2 days/i)).toBeInTheDocument() 107 }) 108 }) 109 110 it('shows cross-community count for first post items', async () => { 111 const user = userEvent.setup() 112 render(<AdminModerationPage />) 113 await user.click(screen.getByRole('tab', { name: /first post/i })) 114 await waitFor(() => { 115 expect(screen.getByText(/active in 3 other communities/i)).toBeInTheDocument() 116 }) 117 }) 118 119 it('switches to action log tab', async () => { 120 const user = userEvent.setup() 121 render(<AdminModerationPage />) 122 await user.click(screen.getByRole('tab', { name: /action log/i })) 123 await waitFor(() => { 124 expect(screen.getByText(/pinned/i)).toBeInTheDocument() 125 }) 126 }) 127 128 it('switches to reported users tab', async () => { 129 const user = userEvent.setup() 130 render(<AdminModerationPage />) 131 await user.click(screen.getByRole('tab', { name: /reported users/i })) 132 await waitFor(() => { 133 expect(screen.getByText(/robin\.bsky\.team/i)).toBeInTheDocument() 134 }) 135 }) 136 137 it('shows cross-community ban warning for reported users', async () => { 138 const user = userEvent.setup() 139 render(<AdminModerationPage />) 140 await user.click(screen.getByRole('tab', { name: /reported users/i })) 141 await waitFor(() => { 142 expect(screen.getByText(/banned from 2 other communities/i)).toBeInTheDocument() 143 }) 144 }) 145 146 it('switches to thresholds tab', async () => { 147 const user = userEvent.setup() 148 render(<AdminModerationPage />) 149 await user.click(screen.getByRole('tab', { name: /thresholds/i })) 150 await waitFor(() => { 151 expect(screen.getByLabelText(/auto-block/i)).toBeInTheDocument() 152 }) 153 }) 154 155 it('shows batch action controls in first post queue', async () => { 156 const user = userEvent.setup() 157 render(<AdminModerationPage />) 158 await user.click(screen.getByRole('tab', { name: /first post/i })) 159 await waitFor(() => { 160 expect(screen.getByText(/newbie\.bsky\.social/i)).toBeInTheDocument() 161 }) 162 // Select all checkbox 163 const selectAll = screen.getByRole('checkbox', { name: /select all/i }) 164 expect(selectAll).toBeInTheDocument() 165 // Individual checkboxes for each item 166 const itemCheckboxes = screen.getAllByRole('checkbox').filter((cb) => cb !== selectAll) 167 expect(itemCheckboxes.length).toBe(2) 168 }) 169 170 it('shows batch approve/reject buttons when items are selected', async () => { 171 const user = userEvent.setup() 172 render(<AdminModerationPage />) 173 await user.click(screen.getByRole('tab', { name: /first post/i })) 174 await waitFor(() => { 175 expect(screen.getByText(/newbie\.bsky\.social/i)).toBeInTheDocument() 176 }) 177 // Batch buttons should not be visible when nothing is selected 178 expect(screen.queryByRole('button', { name: /approve selected/i })).not.toBeInTheDocument() 179 // Select all items 180 await user.click(screen.getByRole('checkbox', { name: /select all/i })) 181 // Batch buttons should now be visible 182 expect(screen.getByRole('button', { name: /approve selected/i })).toBeInTheDocument() 183 expect(screen.getByRole('button', { name: /reject selected/i })).toBeInTheDocument() 184 }) 185 186 it('shows cross-community ban warning in first post queue', async () => { 187 const user = userEvent.setup() 188 render(<AdminModerationPage />) 189 await user.click(screen.getByRole('tab', { name: /first post/i })) 190 await waitFor(() => { 191 expect(screen.getByText(/banned from 1 other community/i)).toBeInTheDocument() 192 }) 193 }) 194 195 it('passes axe accessibility check', async () => { 196 const { container } = render(<AdminModerationPage />) 197 await waitFor(() => { 198 expect(screen.getAllByText(/misleading/i).length).toBeGreaterThan(0) 199 }) 200 const results = await axe(container) 201 expect(results).toHaveNoViolations() 202 }) 203})